commit 7c7ad7d3385d344a6f7e50233e88f45057d55864 from: Lukas Henkel date: Sun Aug 31 14:49:21 2025 UTC New beginning commit - 45dea4164a5971a03765af93e6c483677535f03e commit + 7c7ad7d3385d344a6f7e50233e88f45057d55864 blob - 76ef324781b9865b9b3a48bccfe4c84c29ff996c blob + a1a6c97e4ebbd334ccc158b81a82f148af41034e --- .gitignore +++ .gitignore @@ -17,6 +17,7 @@ feeds.el nov-places org-roam.db private.el +custom.el projects semanticdb tramp blob - b9a5b68820ba656e0a561e857aa1e775cc0aed52 (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/.github/FUNDING.yml +++ /dev/null @@ -1,2 +0,0 @@ -liberapay: abo-abo -patreon: abo_abo blob - f31aa8318fcf37cd0339ba03a34ae26ccd7023f9 (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -emacs ?= emacs - -update: - $(emacs) -batch -l test/make-update.el - -compile: clean - $(emacs) -batch -l test/elpa.el -l test/make-compile.el - -plain: - $(emacs) -Q -l test/elpa.el -l test/make-plain - -clean: - rm -f *.elc - -.PHONY: update compile clean blob - b60d21eca3ee61189c804ca128be136b1dc5b8a8 (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/README.md +++ /dev/null @@ -1,168 +0,0 @@ -# ace-window - -[![MELPA](https://melpa.org/packages/ace-window-badge.svg)](https://melpa.org/#/ace-window) -[![MELPA Stable](https://stable.melpa.org/packages/ace-window-badge.svg)](https://stable.melpa.org/#/ace-window) - -**GNU Emacs package for selecting a window to switch to** - -## What and why - -I'm sure you're aware of the `other-window` command. While it's great -for two windows, it quickly loses its value when there are more windows. -You need to call it many times, and since it's not easily predictable, -you have to check each time if you're in the window that you wanted. - -Another approach is to use `windmove-left`, `windmove-up`, etc. These -are fast and predictable. Their disadvantage is that they need 4 key -bindings. The default ones are shift+arrows, which are hard to reach. - -This package aims to take the speed and predictability of `windmove` -and pack it into a single key binding, similar to `other-window`. - -## Setup - -Just assign `ace-window` to a short key binding, as switching windows -is a common task. I suggest M-o, as it's short and not -bound to anything important in the default Emacs. - -## Usage - -When there are two windows, `ace-window` will call `other-window` -(unless `aw-dispatch-always` is set non-nil). If there are more, each -window will have the first character of its window label highlighted -at the upper left of the window. Pressing that character will either -switch to that window or filter to the next character needed to select -a specific window. Note that, unlike `ace-jump-mode`, the position of -point will not be changed, i.e. the same behavior as that of -`other-window`. - -A special character defined by `aw-make-frame-char` (default = `z`) -means create a new frame and use its window as the target. The new -frame's location is set relative to the prior selected frame's location -and given by `aw-frame-offset`. The new frame's size is given by -`aw-frame-size`. See their documentation strings for more information. - -The windows are ordered top-down, left-to-right. This means that if you -remember your window layouts, you can switch windows without even -looking at the leading char. For instance, the top left window will -always be `1` (or `a` if you use letters for window characters). - -`ace-window` works across multiple frames, as you can see from the -[in-action gif](http://oremacs.com/download/ace-window.gif). - - -## Swap and delete window - -- You can swap windows by calling `ace-window` with a prefix argument C-u. - -- You can delete the selected window by calling `ace-window` with a double prefix argument, i.e. C-u C-u. - -## Change the action midway - -You can also start by calling `ace-window` and then decide to switch the action to `delete` or `swap` etc. By default the bindings are: - -- x - delete window -- m - swap windows -- M - move window -- c - copy window -- j - select buffer -- n - select the previous window -- u - select buffer in the other window -- c - split window fairly, either vertically or horizontally -- v - split window vertically -- b - split window horizontally -- o - maximize current window -- ? - show these command bindings - -For proper operation, these keys *must not* be in `aw-keys`. Additionally, -if you want these keys to work with fewer than three windows, you need to -have `aw-dispatch-always` set to `t`. - -## Customization -Aside from binding `ace-window`: - - (global-set-key (kbd "M-o") 'ace-window) - -the following customizations are available: - -### `aw-keys` -`aw-keys` - the list of initial characters used in window labels: - - (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) - -`aw-keys` are 0-9 by default, which is reasonable, but in the setup -above, the keys are on the home row. - -### `aw-scope` -The default one is `global`, which means that `ace-window` will work -across frames. If you set this to `frame`, `ace-window` will offer you -only the windows of the current frame. - -### `aw-background` - -By default, `ace-window` temporarily sets a gray background and -removes color from available windows in order to make the -window-switching characters more visible. This is the behavior -inherited from `ace-jump-mode`. - -This behavior might not be necessary, as you already know the locations -where to look, i.e. the top-left corners of each window. -So you can turn off the gray background with: - - (setq aw-background nil) - -### `aw-dispatch-always` - -When non-nil, `ace-window` will issue a `read-char` even for one window. -This will make `ace-window` act differently from `other-window` for one -or two windows. This is useful to change the action midway and execute -an action other than the default *jump* action. -By default, this is set to `nil`. - -### `aw-dispatch-alist` - -This is the list of actions you can trigger from `ace-window` other than the -*jump* default. By default it is: - - (defvar aw-dispatch-alist - '((?x aw-delete-window "Delete Window") - (?m aw-swap-window "Swap Windows") - (?M aw-move-window "Move Window") - (?c aw-copy-window "Copy Window") - (?j aw-switch-buffer-in-window "Select Buffer") - (?n aw-flip-window) - (?u aw-switch-buffer-other-window "Switch Buffer Other Window") - (?c aw-split-window-fair "Split Fair Window") - (?v aw-split-window-vert "Split Vert Window") - (?b aw-split-window-horz "Split Horz Window") - (?o delete-other-windows "Delete Other Windows") - (?? aw-show-dispatch-help)) - "List of actions for `aw-dispatch-default'.") - -When using ace-window, if the action character is followed by a string, -then `ace-window` will be invoked again to select the target window for -the action. Otherwise, the current window is selected. - -### `aw-minibuffer-flag` - -When non-nil, also display `ace-window-mode` string in the minibuffer -when `ace-window` is active. This is useful when there are many -side-by-side windows and the `ace-window-mode` string is cutoff in the -minor mode area of the modeline. - -### `aw-ignored-buffers` - -List of buffers and major-modes to ignore when choosing a window from -the window list. Active only when `aw-ignore-on` is non-nil. Windows -displaying these buffers can still be chosen by typing their specific -labels. - -### `aw-ignore-on` - -When t, `ace-window` will ignore buffers and major-modes in -`aw-ignored-buffers`. Use M-0 `ace-window` to toggle this value. - :type 'boolean) - -### `aw-ignore-current` - -When t, `ace-window` will ignore `selected-window'. blob - 84eae1998f30cfd71effe2eb6fccd43ae7f98319 (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/ace-window-autoloads.el +++ /dev/null @@ -1,85 +0,0 @@ -;;; ace-window-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from ace-window.el - -(autoload 'ace-select-window "ace-window" "\ -Ace select window." t) -(autoload 'ace-delete-window "ace-window" "\ -Ace delete window." t) -(autoload 'ace-swap-window "ace-window" "\ -Ace swap window." t) -(autoload 'ace-delete-other-windows "ace-window" "\ -Ace delete other windows." t) -(autoload 'ace-display-buffer "ace-window" "\ -Make `display-buffer' and `pop-to-buffer' select using `ace-window'. -See sample config for `display-buffer-base-action' and `display-buffer-alist': -https://github.com/abo-abo/ace-window/wiki/display-buffer. - -(fn BUFFER ALIST)") -(autoload 'ace-window "ace-window" "\ -Select a window. -Perform an action based on ARG described below. - -By default, behaves like extended `other-window'. -See `aw-scope' which extends it to work with frames. - -Prefixed with one \\[universal-argument], does a swap between the -selected window and the current window, so that the selected -buffer moves to current window (and current buffer moves to -selected window). - -Prefixed with two \\[universal-argument]'s, deletes the selected -window. - -(fn ARG)" t) -(defvar ace-window-display-mode nil "\ -Non-nil if Ace-Window-Display mode is enabled. -See the `ace-window-display-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `ace-window-display-mode'.") -(custom-autoload 'ace-window-display-mode "ace-window" nil) -(autoload 'ace-window-display-mode "ace-window" "\ -Minor mode for showing the ace window key in the mode line. - -This is a global minor mode. If called interactively, toggle the -`Ace-Window-Display mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='ace-window-display-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "ace-window" '("ace-window-mode" "aw-")) - -;;; End of scraped data - -(provide 'ace-window-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; ace-window-autoloads.el ends here blob - ca4f2d49af787a5ff4ad7aae33c99a3eb4b6398b (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/ace-window-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from ace-window.el -*- no-byte-compile: t -*- -(define-package "ace-window" "0.10.0" "Quickly switch windows." '((avy "0.5.0")) :keywords '("window" "location") :authors '(("Oleh Krehel" . "ohwoeowho@gmail.com")) :maintainer '("Oleh Krehel" . "ohwoeowho@gmail.com") :url "https://github.com/abo-abo/ace-window") blob - 416aa7dfcf4fe327083eb117602d96c0d11fa968 (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/ace-window.el +++ /dev/null @@ -1,952 +0,0 @@ -;;; ace-window.el --- Quickly switch windows. -*- lexical-binding: t -*- - -;; Copyright (C) 2015-2020 Free Software Foundation, Inc. - -;; Author: Oleh Krehel -;; Maintainer: Oleh Krehel -;; URL: https://github.com/abo-abo/ace-window -;; Version: 0.10.0 -;; Package-Requires: ((avy "0.5.0")) -;; Keywords: window, location - -;; This file is part of GNU Emacs. - -;; 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 program 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. - -;; For a full copy of the GNU General Public License -;; see . - -;;; Commentary: -;; -;; The main function, `ace-window' is meant to replace `other-window' -;; by assigning each window a short, unique label. When there are only -;; two windows present, `other-window' is called (unless -;; aw-dispatch-always is set non-nil). If there are more, each -;; window will have its first label character highlighted. Once a -;; unique label is typed, ace-window will switch to that window. -;; -;; To setup this package, just add to your .emacs: -;; -;; (global-set-key (kbd "M-o") 'ace-window) -;; -;; replacing "M-o" with an appropriate shortcut. -;; -;; By default, ace-window uses numbers for window labels so the window -;; labeling is intuitively ordered. But if you prefer to type keys on -;; your home row for quicker access, use this setting: -;; -;; (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) -;; -;; Whenever ace-window prompts for a window selection, it grays out -;; all the window characters, highlighting window labels in red. To -;; disable this behavior, set this: -;; -;; (setq aw-background nil) -;; -;; If you want to know the selection characters ahead of time, turn on -;; `ace-window-display-mode'. -;; -;; When prefixed with one `universal-argument', instead of switching -;; to the selected window, the selected window is swapped with the -;; current one. -;; -;; When prefixed with two `universal-argument', the selected window is -;; deleted instead. - -;;; Code: -(require 'avy) -(require 'ring) -(require 'subr-x) - -;;* Customization -(defgroup ace-window nil - "Quickly switch current window." - :group 'convenience - :prefix "aw-") - -(defcustom aw-keys '(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9) - "Keys for selecting window." - :type '(repeat character)) - -(defcustom aw-scope 'global - "The scope used by `ace-window'." - :type '(choice - (const :tag "visible frames" visible) - (const :tag "global" global) - (const :tag "frame" frame))) - -(defcustom aw-translate-char-function #'identity - "Function to translate user input key into another key. -For example, to make SPC do the same as ?a, use -\(lambda (c) (if (= c 32) ?a c))." - :type '(choice - (const :tag "Off" #'identity) - (const :tag "Ignore Case" #'downcase) - (function :tag "Custom"))) - -(defcustom aw-minibuffer-flag nil - "When non-nil, also display `ace-window-mode' string in the minibuffer when ace-window is active." - :type 'boolean) - -(defcustom aw-ignored-buffers '("*Calc Trail*" " *LV*") - "List of buffers and major-modes to ignore when choosing a window from the window list. -Active only when `aw-ignore-on' is non-nil." - :type '(repeat string)) - -(defcustom aw-ignore-on t - "When t, `ace-window' will ignore buffers and major-modes in `aw-ignored-buffers'. -Use M-0 `ace-window' to toggle this value." - :type 'boolean) - -(defcustom aw-ignore-current nil - "When t, `ace-window' will ignore `selected-window'." - :type 'boolean) - -(defcustom aw-background t - "When t, `ace-window' will dim out all buffers temporarily when used." - :type 'boolean) - -(defcustom aw-leading-char-style 'char - "Style of the leading char overlay." - :type '(choice - (const :tag "single char" 'char) - (const :tag "full path" 'path))) - -(defcustom aw-dispatch-always nil - "When non-nil, `ace-window' will issue a `read-char' even for one window. -This will make `ace-window' act different from `other-window' for - one or two windows." - :type 'boolean) - -(defcustom aw-dispatch-when-more-than 2 - "If the number of windows is more than this, activate ace-window-ness." - :type 'integer) - -(defcustom aw-reverse-frame-list nil - "When non-nil `ace-window' will order frames for selection in -the reverse of `frame-list'" - :type 'boolean) - -(defcustom aw-frame-offset '(13 . 23) - "Increase in pixel offset for new ace-window frames relative to the selected frame. -Its value is an (x-offset . y-offset) pair in pixels." - :type '(cons integer integer)) - -(defcustom aw-frame-size nil - "Frame size to make new ace-window frames. -Its value is a (width . height) pair in pixels or nil for the default frame size. -(0 . 0) is special and means make the frame size the same as the last selected frame size." - :type '(cons integer integer)) - -(defcustom aw-char-position 'top-left - "Window positions of the character overlay. -Consider changing this if the overlay tends to overlap with other things." - :type '(choice - (const :tag "top left corner only" 'top-left) - (const :tag "both left corners" 'left))) - -;; Must be defined before `aw-make-frame-char' since its :set function references this. -(defvar aw-dispatch-alist - '((?x aw-delete-window "Delete Window") - (?m aw-swap-window "Swap Windows") - (?M aw-move-window "Move Window") - (?c aw-copy-window "Copy Window") - (?j aw-switch-buffer-in-window "Select Buffer") - (?n aw-flip-window) - (?u aw-switch-buffer-other-window "Switch Buffer Other Window") - (?e aw-execute-command-other-window "Execute Command Other Window") - (?F aw-split-window-fair "Split Fair Window") - (?v aw-split-window-vert "Split Vert Window") - (?b aw-split-window-horz "Split Horz Window") - (?o delete-other-windows "Delete Other Windows") - (?T aw-transpose-frame "Transpose Frame") - ;; ?i ?r ?t are used by hyperbole.el - (?? aw-show-dispatch-help)) - "List of actions for `aw-dispatch-default'. -Each action is a list of either: - (char function description) where function takes a single window argument -or - (char function) where function takes no argument and the description is omitted.") - -(defun aw-set-make-frame-char (option value) - ;; Signal an error if `aw-make-frame-char' is ever set to an invalid - ;; or conflicting value. - (when value - (cond ((not (characterp value)) - (user-error "`aw-make-frame-char' must be a character, not `%s'" value)) - ((memq value aw-keys) - (user-error "`aw-make-frame-char' is `%c'; this conflicts with the same character in `aw-keys'" value)) - ((assq value aw-dispatch-alist) - (user-error "`aw-make-frame-char' is `%c'; this conflicts with the same character in `aw-dispatch-alist'" value)))) - (set option value)) - -(defcustom aw-make-frame-char ?z - "Non-existing ace window label character that triggers creation of a new single-window frame for display." - :set 'aw-set-make-frame-char - :type 'character) - -(defface aw-leading-char-face - '((((class color)) (:foreground "red")) - (((background dark)) (:foreground "gray100")) - (((background light)) (:foreground "gray0")) - (t (:foreground "gray100" :underline nil))) - "Face for each window's leading char.") - -(defface aw-minibuffer-leading-char-face - '((t :inherit aw-leading-char-face)) - "Face for minibuffer leading char.") - -(defface aw-background-face - '((t (:foreground "gray40"))) - "Face for whole window background during selection.") - -(defface aw-mode-line-face - '((t (:inherit mode-line-buffer-id))) - "Face used for displaying the ace window key in the mode-line.") - -(defface aw-key-face - '((t :inherit font-lock-builtin-face)) - "Face used by `aw-show-dispatch-help'.") - -;;* Implementation -(defun aw-ignored-p (window) - "Return t if WINDOW should be ignored when choosing from the window list." - (or (and aw-ignore-on - ;; Ignore major-modes and buffer-names in `aw-ignored-buffers'. - (or (memq (buffer-local-value 'major-mode (window-buffer window)) - aw-ignored-buffers) - (member (buffer-name (window-buffer window)) aw-ignored-buffers))) - ;; ignore child frames - (and (fboundp 'frame-parent) (frame-parent (window-frame window))) - ;; Ignore selected window if `aw-ignore-current' is non-nil. - (and aw-ignore-current - (equal window (selected-window))) - ;; When `ignore-window-parameters' is nil, ignore windows whose - ;; `no-other-window’ or `no-delete-other-windows' parameter is non-nil. - (unless ignore-window-parameters - (cl-case this-command - (ace-select-window (window-parameter window 'no-other-window)) - (ace-delete-window (window-parameter window 'no-delete-other-windows)) - (ace-delete-other-windows (window-parameter - window 'no-delete-other-windows)))))) - -(defun aw-window-list () - "Return the list of interesting windows." - (sort - (cl-remove-if - (lambda (w) - (let ((f (window-frame w))) - (or (not (and (frame-live-p f) - (frame-visible-p f))) - (string= "initial_terminal" (terminal-name f)) - (aw-ignored-p w)))) - (cl-case aw-scope - (visible - (cl-mapcan #'window-list (visible-frame-list))) - (global - (cl-mapcan #'window-list (frame-list))) - (frame - (window-list)) - (t - (error "Invalid `aw-scope': %S" aw-scope)))) - 'aw-window<)) - -(defvar aw-overlays-back nil - "Hold overlays for when `aw-background' is t.") - -(defvar ace-window-mode nil - "Minor mode during the selection process.") - -;; register minor mode -(or (assq 'ace-window-mode minor-mode-alist) - (nconc minor-mode-alist - (list '(ace-window-mode ace-window-mode)))) - -(defvar aw-empty-buffers-list nil - "Store the read-only empty buffers which had to be modified. -Modify them back eventually.") - -(defvar aw--windows-hscroll nil - "List of (window . hscroll-columns) items, each listing a window whose - horizontal scroll will be restored upon ace-window action completion.") - -(defvar aw--windows-points nil - "List of (window . point) items. The point position had to be - moved in order to display the overlay.") - -(defun aw--done () - "Clean up mode line and overlays." - ;; mode line - (aw-set-mode-line nil) - ;; background - (mapc #'delete-overlay aw-overlays-back) - (setq aw-overlays-back nil) - (avy--remove-leading-chars) - (dolist (b aw-empty-buffers-list) - (with-current-buffer b - (when (string= (buffer-string) " ") - (let ((inhibit-read-only t)) - (delete-region (point-min) (point-max)))))) - (setq aw-empty-buffers-list nil) - (aw--restore-windows-hscroll) - (let (c) - (while (setq c (pop aw--windows-points)) - (with-selected-window (car c) - (goto-char (cdr c)))))) - -(defun aw--restore-windows-hscroll () - "Restore horizontal scroll of windows from `aw--windows-hscroll' list." - (let (wnd hscroll) - (mapc (lambda (wnd-and-hscroll) - (setq wnd (car wnd-and-hscroll) - hscroll (cdr wnd-and-hscroll)) - (when (window-live-p wnd) - (set-window-hscroll wnd hscroll))) - aw--windows-hscroll)) - (setq aw--windows-hscroll nil)) - -(defun aw--overlay-str (wnd pos path) - "Return the replacement text for an overlay in WND at POS, -accessible by typing PATH." - (let ((old-str (or - (ignore-errors - (with-selected-window wnd - (buffer-substring pos (1+ pos)))) - ""))) - (concat - (cl-case aw-leading-char-style - (char - (string (avy--key-to-char (car (last path))))) - (path - (mapconcat - (lambda (x) (string (avy--key-to-char x))) - (reverse path) - "")) - (t - (error "Bad `aw-leading-char-style': %S" - aw-leading-char-style))) - (cond ((string-equal old-str "\t") - (make-string (1- tab-width) ?\ )) - ((string-equal old-str "\n") - "\n") - (t - (make-string - (max 0 (1- (string-width old-str))) - ?\ )))))) - -(defun aw--point-visible-p () - "Return non-nil if point is visible in the selected window. -Return nil when horizontal scrolling has moved it off screen." - (and (>= (- (current-column) (window-hscroll)) 0) - (< (- (current-column) (window-hscroll)) - (window-width)))) - -(defun aw--lead-overlay (path leaf) - "Create an overlay using PATH at LEAF. -LEAF is (PT . WND)." - ;; Properly adds overlay in visible region of most windows except for any one - ;; receiving output while this function is executing, since that moves point, - ;; potentially shifting the added overlay outside the window's visible region. - (let ((wnd (cdr leaf)) - ;; Prevent temporary movement of point from scrolling any window. - (scroll-margin 0)) - (with-selected-window wnd - (when (= 0 (buffer-size)) - (push (current-buffer) aw-empty-buffers-list) - (let ((inhibit-read-only t)) - (insert " "))) - ;; If point is not visible due to horizontal scrolling of the - ;; window, this next expression temporarily scrolls the window - ;; right until point is visible, so that the leading-char can be - ;; seen when it is inserted. When ace-window's action finishes, - ;; the horizontal scroll is restored by (aw--done). - (while (and (not (aw--point-visible-p)) - (not (zerop (window-hscroll))) - (progn (push (cons (selected-window) (window-hscroll)) aw--windows-hscroll) t) - (not (zerop (scroll-right))))) - (let* ((ws (window-start)) - (prev nil) - (vertical-pos (if (eq aw-char-position 'left) -1 0)) - (horizontal-pos (if (zerop (window-hscroll)) 0 (1+ (window-hscroll)))) - (old-pt (point)) - (pt - (progn - ;; If leading-char is to be displayed at the top-left, move - ;; to the first visible line in the window, otherwise, move - ;; to the last visible line. - (move-to-window-line vertical-pos) - (move-to-column horizontal-pos) - ;; Find a nearby point that is not at the end-of-line but - ;; is visible so have space for the overlay. - (setq prev (1- (point))) - (while (and (>= prev ws) (/= prev (point)) (eolp)) - (setq prev (point)) - (unless (bobp) - (line-move -1 t) - (move-to-column horizontal-pos))) - (recenter vertical-pos) - (point))) - (ol (make-overlay pt (1+ pt) (window-buffer wnd)))) - (if (= (aw--face-rel-height) 1) - (goto-char old-pt) - (when (/= pt old-pt) - (goto-char (+ pt 1)) - (push (cons wnd old-pt) aw--windows-points))) - (overlay-put ol 'display (aw--overlay-str wnd pt path)) - (if (window-minibuffer-p wnd) - (overlay-put ol 'face 'aw-minibuffer-leading-char-face) - (overlay-put ol 'face 'aw-leading-char-face)) - (overlay-put ol 'window wnd) - (push ol avy--overlays-lead))))) - -(defun aw--make-backgrounds (wnd-list) - "Create a dim background overlay for each window on WND-LIST." - (when aw-background - (setq aw-overlays-back - (mapcar (lambda (w) - (let ((ol (make-overlay - (window-start w) - (window-end w) - (window-buffer w)))) - (overlay-put ol 'face 'aw-background-face) - ol)) - wnd-list)))) - -(defvar aw-dispatch-function 'aw-dispatch-default - "Function to call when a character not in `aw-keys' is pressed.") - -(defvar aw-action nil - "Function to call at the end of `aw-select'.") - -(defun aw-set-mode-line (str) - "Set mode line indicator to STR." - (setq ace-window-mode str) - (when (and aw-minibuffer-flag ace-window-mode) - (message "%s" (string-trim-left str))) - (force-mode-line-update)) - -(defun aw--dispatch-action (char) - "Return item from `aw-dispatch-alist' matching CHAR." - (assoc char aw-dispatch-alist)) - -(defun aw-make-frame () - "Make a new Emacs frame using the values of `aw-frame-size' and `aw-frame-offset'." - (make-frame - (delq nil - (list - ;; This first parameter is important because an - ;; aw-dispatch-alist command may not want to leave this - ;; frame with input focus. If it is given focus, the - ;; command may not be able to return focus to a different - ;; frame since this is done asynchronously by the window - ;; manager. - '(no-focus-on-map . t) - (when aw-frame-size - (cons 'width - (if (zerop (car aw-frame-size)) - (frame-width) - (car aw-frame-size)))) - (when aw-frame-size - (cons 'height - (if (zerop (cdr aw-frame-size)) - (frame-height) - (car aw-frame-size)))) - (cons 'left (+ (car aw-frame-offset) - (car (frame-position)))) - (cons 'top (+ (cdr aw-frame-offset) - (cdr (frame-position)))))))) - -(defun aw-use-frame (window) - "Create a new frame using the contents of WINDOW. - -The new frame is set to the same size as the previous frame, offset by -`aw-frame-offset' (x . y) pixels." - (aw-switch-to-window window) - (aw-make-frame)) - -(defun aw-clean-up-avy-current-path () - "Edit `avy-current-path' so only window label characters remain." - ;; Remove any possible ace-window command char that may - ;; precede the last specified window label, so - ;; functions can use `avy-current-path' as the chosen - ;; window label. - (when (and (> (length avy-current-path) 0) - (assq (aref avy-current-path 0) aw-dispatch-alist)) - (setq avy-current-path (substring avy-current-path 1)))) - -(defun aw-dispatch-default (char) - "Perform an action depending on CHAR." - (cond ((and (fboundp 'avy-mouse-event-window) - (avy-mouse-event-window char))) - ((= char (aref (kbd "C-g") 0)) - (throw 'done 'exit)) - ((and aw-make-frame-char (= char aw-make-frame-char)) - ;; Make a new frame and perform any action on its window. - (let ((start-win (selected-window)) - (end-win (frame-selected-window (aw-make-frame)))) - (if aw-action - ;; Action must be called from the start-win. The action - ;; determines which window to leave selected. - (progn (select-frame-set-input-focus (window-frame start-win)) - (funcall aw-action end-win)) - ;; Select end-win when no action - (aw-switch-to-window end-win))) - (throw 'done 'exit)) - (t - (let ((action (aw--dispatch-action char))) - (if action - (cl-destructuring-bind (_key fn &optional description) action - (if (and fn description) - (prog1 (setq aw-action fn) - (aw-set-mode-line (format " Ace - %s" description))) - (if (commandp fn) - (call-interactively fn) - (funcall fn)) - (throw 'done 'exit))) - (aw-clean-up-avy-current-path) - ;; Prevent any char from triggering an avy dispatch command. - (let ((avy-dispatch-alist)) - (avy-handler-default char))))))) - -(defcustom aw-display-mode-overlay t - "When nil, don't display overlays. Rely on the mode line instead." - :type 'boolean) - -(defvar ace-window-display-mode) - -(defun aw-select (mode-line &optional action) - "Return a selected other window. -Amend MODE-LINE to the mode line for the duration of the selection." - (setq aw-action action) - (let ((start-window (selected-window)) - (next-window-scope (cl-case aw-scope - ('visible 'visible) - ('global 'visible) - ('frame 'frame))) - (wnd-list (aw-window-list)) - window) - (setq window - (cond ((<= (length wnd-list) 1) - (when aw-dispatch-always - (setq aw-action - (unwind-protect - (catch 'done - (funcall aw-dispatch-function (read-char))) - (aw--done))) - (when (eq aw-action 'exit) - (setq aw-action nil))) - (or (car wnd-list) start-window)) - ((and (<= (+ (length wnd-list) (if (aw-ignored-p start-window) 1 0)) - aw-dispatch-when-more-than) - (not aw-dispatch-always) - (not aw-ignore-current)) - (let ((wnd (next-window nil nil next-window-scope))) - (while (and (or (not (memq wnd wnd-list)) - (aw-ignored-p wnd)) - (not (equal wnd start-window))) - (setq wnd (next-window wnd nil next-window-scope))) - wnd)) - (t - (let ((candidate-list - (mapcar (lambda (wnd) - (cons (aw-offset wnd) wnd)) - wnd-list))) - (aw--make-backgrounds wnd-list) - (aw-set-mode-line mode-line) - ;; turn off helm transient map - (remove-hook 'post-command-hook 'helm--maybe-update-keymap) - (unwind-protect - (let* ((avy-handler-function aw-dispatch-function) - (avy-translate-char-function aw-translate-char-function) - (transient-mark-mode nil) - (res (avy-read (avy-tree candidate-list aw-keys) - (if (and ace-window-display-mode - (null aw-display-mode-overlay)) - (lambda (_path _leaf)) - #'aw--lead-overlay) - #'avy--remove-leading-chars))) - (if (eq res 'exit) - (setq aw-action nil) - (or (cdr res) - start-window))) - (aw--done)))))) - (if aw-action - (funcall aw-action window) - window))) - -;;* Interactive -;;;###autoload -(defun ace-select-window () - "Ace select window." - (interactive) - (aw-select " Ace - Window" - #'aw-switch-to-window)) - -;;;###autoload -(defun ace-delete-window () - "Ace delete window." - (interactive) - (aw-select " Ace - Delete Window" - #'aw-delete-window)) - -;;;###autoload -(defun ace-swap-window () - "Ace swap window." - (interactive) - (aw-select " Ace - Swap Window" - #'aw-swap-window)) - -;;;###autoload -(defun ace-delete-other-windows () - "Ace delete other windows." - (interactive) - (aw-select " Ace - Delete Other Windows" - #'delete-other-windows)) - -;;;###autoload -(defun ace-display-buffer (buffer alist) - "Make `display-buffer' and `pop-to-buffer' select using `ace-window'. -See sample config for `display-buffer-base-action' and `display-buffer-alist': -https://github.com/abo-abo/ace-window/wiki/display-buffer." - (let* ((aw-ignore-current (cdr (assq 'inhibit-same-window alist))) - (rf (cdr (assq 'reusable-frames alist))) - (aw-scope (cl-case rf - ((nil) 'frame) - (visible 'visible) - ((0 t) 'global)))) - (unless (or (<= (length (aw-window-list)) 1) - (not aw-scope)) - (window--display-buffer - buffer (aw-select "Ace - Display Buffer") 'reuse)))) - -(declare-function transpose-frame "ext:transpose-frame") -(defun aw-transpose-frame (w) - "Select any window on frame and `tranpose-frame'." - (transpose-frame (window-frame w))) - -;;;###autoload -(defun ace-window (arg) - "Select a window. -Perform an action based on ARG described below. - -By default, behaves like extended `other-window'. -See `aw-scope' which extends it to work with frames. - -Prefixed with one \\[universal-argument], does a swap between the -selected window and the current window, so that the selected -buffer moves to current window (and current buffer moves to -selected window). - -Prefixed with two \\[universal-argument]'s, deletes the selected -window." - (interactive "p") - (setq avy-current-path "") - (cl-case arg - (0 - (let ((aw-ignore-on (not aw-ignore-on))) - (ace-select-window))) - (4 (ace-swap-window)) - (16 (ace-delete-window)) - (t (ace-select-window)))) - -;;* Utility -(unless (fboundp 'frame-position) - (defun frame-position (&optional frame) - (let ((pl (frame-parameter frame 'left)) - (pt (frame-parameter frame 'top))) - (when (consp pl) - (setq pl (eval pl))) - (when (consp pt) - (setq pt (eval pt))) - (cons pl pt)))) - -(defun aw-window< (wnd1 wnd2) - "Return true if WND1 is less than WND2. -This is determined by their respective window coordinates. -Windows are numbered top down, left to right." - (let* ((f1 (window-frame wnd1)) - (f2 (window-frame wnd2)) - (e1 (window-edges wnd1)) - (e2 (window-edges wnd2)) - (p1 (frame-position f1)) - (p2 (frame-position f2)) - (nl (or (null (car p1)) (null (car p2))))) - (cond ((and (not nl) (< (car p1) (car p2))) - (not aw-reverse-frame-list)) - ((and (not nl) (> (car p1) (car p2))) - aw-reverse-frame-list) - ((< (car e1) (car e2)) - t) - ((> (car e1) (car e2)) - nil) - ((< (cadr e1) (cadr e2)) - t)))) - -(defvar aw--window-ring (make-ring 10) - "Hold the window switching history.") - -(defun aw--push-window (window) - "Store WINDOW to `aw--window-ring'." - (when (or (zerop (ring-length aw--window-ring)) - (not (equal - (ring-ref aw--window-ring 0) - window))) - (ring-insert aw--window-ring (selected-window)))) - -(defun aw--pop-window () - "Return the removed top of `aw--window-ring'." - (let (res) - (condition-case nil - (while (or (not (window-live-p - (setq res (ring-remove aw--window-ring 0)))) - (equal res (selected-window)))) - (error - (if (= (length (aw-window-list)) 2) - (progn - (other-window 1) - (setq res (selected-window))) - (error "No previous windows stored")))) - res)) - -(defun aw-switch-to-window (window) - "Switch to the window WINDOW." - (let ((frame (window-frame window))) - (aw--push-window (selected-window)) - (when (and (frame-live-p frame) - (not (eq frame (selected-frame)))) - (select-frame-set-input-focus frame)) - (if (window-live-p window) - (select-window window) - (error "Got a dead window %S" window)))) - -(defun aw-flip-window () - "Switch to the window you were previously in." - (interactive) - (aw-switch-to-window (aw--pop-window))) - -(defun aw-show-dispatch-help () - "Display action shortucts in echo area." - (interactive) - (message "%s" (mapconcat - (lambda (action) - (cl-destructuring-bind (key fn &optional description) action - (format "%s: %s" - (propertize - (char-to-string key) - 'face 'aw-key-face) - (or description fn)))) - aw-dispatch-alist - "\n")) - ;; Prevent this from replacing any help display - ;; in the minibuffer. - (let (aw-minibuffer-flag) - (mapc #'delete-overlay aw-overlays-back) - (call-interactively 'ace-window))) - -(defun aw-delete-window (window &optional kill-buffer) - "Delete window WINDOW. -When KILL-BUFFER is non-nil, also kill the buffer." - (let ((frame (window-frame window))) - (when (and (frame-live-p frame) - (not (eq frame (selected-frame)))) - (select-frame-set-input-focus (window-frame window))) - (if (= 1 (length (window-list))) - (delete-frame frame) - (if (window-live-p window) - (let ((buffer (window-buffer window))) - (delete-window window) - (when kill-buffer - (kill-buffer buffer))) - (error "Got a dead window %S" window))))) - -(defun aw-switch-buffer-in-window (window) - "Select buffer in WINDOW." - (aw-switch-to-window window) - (aw--switch-buffer)) - -(declare-function ivy-switch-buffer "ext:ivy") - -(defun aw--switch-buffer () - (cond ((bound-and-true-p ivy-mode) - (ivy-switch-buffer)) - ((bound-and-true-p ido-mode) - (ido-switch-buffer)) - (t - (call-interactively 'switch-to-buffer)))) - -(defcustom aw-swap-invert nil - "When non-nil, the other of the two swapped windows gets the point." - :type 'boolean) - -(defun aw-swap-window (window) - "Swap buffers of current window and WINDOW." - (cl-labels ((swap-windows (window1 window2) - "Swap the buffers of WINDOW1 and WINDOW2." - (let ((buffer1 (window-buffer window1)) - (buffer2 (window-buffer window2))) - (set-window-buffer window1 buffer2) - (set-window-buffer window2 buffer1) - (select-window window2)))) - (let ((frame (window-frame window)) - (this-window (selected-window))) - (when (and (frame-live-p frame) - (not (eq frame (selected-frame)))) - (select-frame-set-input-focus (window-frame window))) - (when (and (window-live-p window) - (not (eq window this-window))) - (aw--push-window this-window) - (if aw-swap-invert - (swap-windows window this-window) - (swap-windows this-window window)))))) - -(defun aw-move-window (window) - "Move the current buffer to WINDOW. -Switch the current window to the previous buffer." - (let ((buffer (current-buffer))) - (switch-to-buffer (other-buffer)) - (aw-switch-to-window window) - (switch-to-buffer buffer))) - -(defun aw-copy-window (window) - "Copy the current buffer to WINDOW." - (let ((buffer (current-buffer))) - (aw-switch-to-window window) - (switch-to-buffer buffer))) - -(defun aw-split-window-vert (window) - "Split WINDOW vertically." - (select-window window) - (split-window-vertically)) - -(defun aw-split-window-horz (window) - "Split WINDOW horizontally." - (select-window window) - (split-window-horizontally)) - -(defcustom aw-fair-aspect-ratio 2 - "The aspect ratio to aim for when splitting windows. -Sizes are based on the number of characters, not pixels. -Increase to prefer wider windows, or decrease for taller windows." - :type 'number) - -(defun aw-split-window-fair (window) - "Split WINDOW vertically or horizontally, based on its current dimensions. -Modify `aw-fair-aspect-ratio' to tweak behavior." - (let ((w (window-body-width window)) - (h (window-body-height window))) - (if (< (* h aw-fair-aspect-ratio) w) - (aw-split-window-horz window) - (aw-split-window-vert window)))) - -(defun aw-switch-buffer-other-window (window) - "Switch buffer in WINDOW." - (aw-switch-to-window window) - (unwind-protect - (aw--switch-buffer) - (aw-flip-window))) - -(defun aw-execute-command-other-window (window) - "Execute a command in WINDOW." - (aw-switch-to-window window) - (unwind-protect - (funcall - (key-binding - (read-key-sequence - "Enter key sequence: "))) - (aw-flip-window))) - -(defun aw--face-rel-height () - (let ((h (face-attribute 'aw-leading-char-face :height))) - (cond - ((eq h 'unspecified) - 1) - ((floatp h) - (max (floor h) 1)) - ((integerp h) - 1) - (t - (error "unexpected: %s" h))))) - -(defun aw-offset (window) - "Return point in WINDOW that's closest to top left corner. -The point is writable, i.e. it's not part of space after newline." - (let ((h (window-hscroll window)) - (beg (window-start window)) - (end (window-end window)) - (inhibit-field-text-motion t)) - (with-current-buffer (window-buffer window) - (save-excursion - (goto-char beg) - (forward-line (1- - (min - (count-lines - (point) - (point-max)) - (aw--face-rel-height)))) - (while (and (< (point) end) - (< (- (line-end-position) - (line-beginning-position)) - h)) - (forward-line)) - (+ (point) h))))) - -(defun aw--after-make-frame (f) - (aw-update) - (make-frame-visible f)) - -;;* Mode line -;;;###autoload -(define-minor-mode ace-window-display-mode - "Minor mode for showing the ace window key in the mode line." - :global t - (if ace-window-display-mode - (progn - (aw-update) - (set-default - 'mode-line-format - `((ace-window-display-mode - (:eval (window-parameter (selected-window) 'ace-window-path))) - ,@(assq-delete-all - 'ace-window-display-mode - (default-value 'mode-line-format)))) - (force-mode-line-update t) - (add-hook 'window-configuration-change-hook 'aw-update) - ;; Add at the end so does not precede select-frame call. - (add-hook 'after-make-frame-functions #'aw--after-make-frame t)) - (set-default - 'mode-line-format - (assq-delete-all - 'ace-window-display-mode - (default-value 'mode-line-format))) - (remove-hook 'window-configuration-change-hook 'aw-update) - (remove-hook 'after-make-frame-functions 'aw--after-make-frame))) - -(defun aw-update () - "Update ace-window-path window parameter for all windows. - -Ensure all windows are labeled so the user can select a specific -one, even from the set of windows typically ignored when making a -window list." - (let ((aw-ignore-on) - (aw-ignore-current) - (ignore-window-parameters t)) - (avy-traverse - (avy-tree (aw-window-list) aw-keys) - (lambda (path leaf) - (set-window-parameter - leaf 'ace-window-path - (propertize - (apply #'string (reverse path)) - 'face 'aw-mode-line-face)))))) - -(provide 'ace-window) - -;;; ace-window.el ends here blob - a5dac47f773b565388602cfdf44dec180aec685a (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/test/elpa.el +++ /dev/null @@ -1,4 +0,0 @@ -(setq package-user-dir - (expand-file-name (format "~/.elpa/%s/elpa" emacs-version))) -(package-initialize) -(add-to-list 'load-path default-directory) blob - 790853c144b385a4c9b0c603abd45723d4eb7f17 (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/test/make-compile.el +++ /dev/null @@ -1,3 +0,0 @@ -(setq files '("ace-window.el")) -(setq byte-compile--use-old-handlers nil) -(mapc #'byte-compile-file files) blob - 77bbf145b152053184b417e81ab524b310c45c61 (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/test/make-plain.el +++ /dev/null @@ -1,2 +0,0 @@ -(require 'ace-window) -(global-set-key (kbd "M-o") 'ace-window) blob - 634679a8d1c00e79cff1f87e772d4cb25d70b55a (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0/test/make-update.el +++ /dev/null @@ -1,28 +0,0 @@ -;;* list of the all dependencies -(defvar dev-packages '(avy)) - -;;* initialize package.el -(setq package-user-dir - (expand-file-name (format "~/.elpa/%s/elpa" emacs-version))) -(message "installing in %s ...\n" package-user-dir) -(package-initialize) -(setq package-archives - '(("melpa" . "https://melpa.org/packages/") - ("gnu" . "http://elpa.gnu.org/packages/"))) -(package-refresh-contents) - -;;* install dependencies -(dolist (package dev-packages) - (unless (package-installed-p package) - (ignore-errors - (package-install package)))) - -;;* upgrade dependencies -(save-window-excursion - (package-list-packages t) - (condition-case nil - (progn - (package-menu-mark-upgrades) - (package-menu-execute t)) - (error - (message "All packages up to date")))) blob - f8734d65857361b5ba773d2d4d0f5dad03e6a794 (mode 644) blob + /dev/null --- elpa/ace-window-0.10.0.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2021-03-18T10:05:02+0100 using RSA \ No newline at end of file blob - 3c0cb485b10289ea8f8a826832496c4597cc2cd3 (mode 644) blob + /dev/null --- elpa/aggressive-indent-1.10.0/README.md +++ /dev/null @@ -1,65 +0,0 @@ -aggressive-indent-mode [![Melpa](http://melpa.org/packages/aggressive-indent-badge.svg)](http://melpa.org/#/aggressive-indent) [![Melpa-Stable](http://stable.melpa.org/packages/aggressive-indent-badge.svg)](http://stable.melpa.org/#/aggressive-indent) -====================== - -`electric-indent-mode` is enough to keep your code nicely aligned when -all you do is type. However, once you start shifting blocks around, -transposing lines, or slurping and barfing sexps, indentation is bound -to go wrong. - -**`aggressive-indent-mode`** is a minor mode that keeps your code **always** -indented. It reindents after every change, making it more reliable -than `electric-indent-mode`. - -### Demonstration ### - -- An example of Lisp mode (Emacs Lisp): -![Lisp Code Example](lisp-example.gif) - -- An example of non-Lisp mode (C): -![C Code Example](c-example.gif) - -### Instructions ### - -This package is available from Melpa, you may install it by calling - - M-x package-install RET aggressive-indent - -Then activate it with - - (add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode) - (add-hook 'css-mode-hook #'aggressive-indent-mode) - -You can use this hook on any mode you want, `aggressive-indent` is not -exclusive to emacs-lisp code. In fact, if you want to turn it on for -every programming mode, you can do something like: - - (global-aggressive-indent-mode 1) - (add-to-list 'aggressive-indent-excluded-modes 'html-mode) - -#### Manual Installation #### - -If you don't want to install from Melpa, you can download it manually, -place it in your `load-path` along with its dependency `cl-lib` (which -you should already have if your `emacs-version` is at least 24.3). - -Then require it with: - - (require 'aggressive-indent) - -### Customization ### - -The variable `aggressive-indent-dont-indent-if` lets you customize -when you **don't** want indentation to happen. -For instance, if you think it's annoying that lines jump around in -`c++-mode` because you haven't typed the `;` yet, you could add the -following clause: - - (add-to-list - 'aggressive-indent-dont-indent-if - '(and (derived-mode-p 'c++-mode) - (null (string-match "\\([;{}]\\|\\b\\(if\\|for\\|while\\)\\b\\)" - (thing-at-point 'line))))) - -## Contribute ## - -[![Gratipay](https://cdn.rawgit.com/gratipay/gratipay-badge/2.1.3/dist/gratipay.png)](https://gratipay.com/Malabarba) blob - acac89a68967f2cb5c60ae0f8607ed5ece0310dd (mode 644) blob + /dev/null --- elpa/aggressive-indent-1.10.0/aggressive-indent-autoloads.el +++ /dev/null @@ -1,85 +0,0 @@ -;;; aggressive-indent-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from aggressive-indent.el - -(autoload 'aggressive-indent-indent-defun "aggressive-indent" "\ -Indent current defun. -Throw an error if parentheses are unbalanced. -If L and R are provided, use them for finding the start and end of defun. - -(fn &optional L R)" t) -(autoload 'aggressive-indent-indent-region-and-on "aggressive-indent" "\ -Indent region between L and R, and then some. -Call `aggressive-indent-region-function' between L and R, and -then keep indenting until nothing more happens. - -(fn L R)" t) -(autoload 'aggressive-indent-mode "aggressive-indent" "\ -Toggle Aggressive-Indent mode on or off. - -This is a minor mode. If called interactively, toggle the -`Aggressive-Indent mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `aggressive-indent-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(put 'global-aggressive-indent-mode 'globalized-minor-mode t) -(defvar global-aggressive-indent-mode nil "\ -Non-nil if Global Aggressive-Indent mode is enabled. -See the `global-aggressive-indent-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `global-aggressive-indent-mode'.") -(custom-autoload 'global-aggressive-indent-mode "aggressive-indent" nil) -(autoload 'global-aggressive-indent-mode "aggressive-indent" "\ -Toggle Aggressive-Indent mode in all buffers. -With prefix ARG, enable Global Aggressive-Indent mode if ARG is -positive; otherwise, disable it. - -If called from Lisp, toggle the mode if ARG is `toggle'. -Enable the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -Aggressive-Indent mode is enabled in all buffers where -`aggressive-indent-mode' would do it. - -See `aggressive-indent-mode' for more information on Aggressive-Indent -mode. - -(fn &optional ARG)" t) -(defalias 'aggressive-indent-global-mode #'global-aggressive-indent-mode) -(register-definition-prefixes "aggressive-indent" '("aggressive-indent-")) - -;;; End of scraped data - -(provide 'aggressive-indent-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; aggressive-indent-autoloads.el ends here blob - d22e235eee35e4ac88c77f75300ed3ed21914352 (mode 644) blob + /dev/null --- elpa/aggressive-indent-1.10.0/aggressive-indent-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from aggressive-indent.el -*- no-byte-compile: t -*- -(define-package "aggressive-indent" "1.10.0" "Minor mode to aggressively keep your code always indented" '((emacs "24.3")) :keywords '("indent" "lisp" "maint" "tools") :authors '(("Artur Malabarba" . "emacs@endlessparentheses.com")) :maintainer '("Artur Malabarba" . "emacs@endlessparentheses.com") :url "https://github.com/Malabarba/aggressive-indent-mode") blob - 0234fcf29c7bca07dec6c55035a07272f5c614ad (mode 644) blob + /dev/null --- elpa/aggressive-indent-1.10.0/aggressive-indent.el +++ /dev/null @@ -1,541 +0,0 @@ -;;; aggressive-indent.el --- Minor mode to aggressively keep your code always indented -*- lexical-binding:t -*- - -;; Copyright (C) 2014-2021 Free Software Foundation, Inc - -;; Author: Artur Malabarba -;; URL: https://github.com/Malabarba/aggressive-indent-mode -;; Version: 1.10.0 -;; Package-Requires: ((emacs "24.3")) -;; Keywords: indent lisp maint tools -;; Prefix: aggressive-indent -;; Separator: - - -;;; Commentary: -;; -;; `electric-indent-mode' is enough to keep your code nicely aligned when -;; all you do is type. However, once you start shifting blocks around, -;; transposing lines, or slurping and barfing sexps, indentation is bound -;; to go wrong. -;; -;; `aggressive-indent-mode' is a minor mode that keeps your code always -;; indented. It reindents after every change, making it more reliable -;; than `electric-indent-mode'. -;; -;; ### Instructions ### -;; -;; This package is available fom Melpa, you may install it by calling -;; -;; M-x package-install RET aggressive-indent -;; -;; Then activate it with -;; -;; (add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode) -;; (add-hook 'css-mode-hook #'aggressive-indent-mode) -;; -;; You can use this hook on any mode you want, `aggressive-indent' is not -;; exclusive to emacs-lisp code. In fact, if you want to turn it on for -;; every programming mode, you can do something like: -;; -;; (global-aggressive-indent-mode 1) -;; (add-to-list 'aggressive-indent-excluded-modes 'html-mode) -;; -;; ### Manual Installation ### -;; -;; If you don't want to install from Melpa, you can download it manually, -;; place it in your `load-path' and require it with -;; -;; (require 'aggressive-indent) - -;;; Instructions: -;; -;; INSTALLATION -;; -;; This package is available fom Melpa, you may install it by calling -;; M-x package-install RET aggressive-indent. -;; -;; Then activate it with -;; (add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode) -;; -;; You can also use an equivalent hook for another mode, -;; `aggressive-indent' is not exclusive to emacs-lisp code. -;; -;; Alternatively, you can download it manually, place it in your -;; `load-path' and require it with -;; -;; (require 'aggressive-indent) - -;;; License: -;; -;; This file is NOT part of GNU Emacs. -;; -;; This program 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 -;; of the License, or (at your option) any later version. -;; -;; This program 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. -;; - -;;; Code: - -(require 'cl-lib) - -(defgroup aggressive-indent nil - "Customization group for aggressive-indent." - :prefix "aggressive-indent-" - :group 'electricity - :group 'indent) - -(defun aggressive-indent-bug-report () - "Opens github issues page in a web browser. Please send any bugs you find. -Please include your Emacs and `aggressive-indent' versions." - (interactive) - (message "Your `aggressive-indent-version' is: %s, and your emacs version is: %s. -Please include this in your report!" - (eval-when-compile - (ignore-errors - (require 'lisp-mnt) - (lm-version))) - emacs-version) - (browse-url "https://github.com/Malabarba/aggressive-indent-mode/issues/new")) - -(defvar aggressive-indent-mode) - -;;; Configuring indentation -(defcustom aggressive-indent-dont-electric-modes nil - "List of major-modes where `electric-indent' should be disabled." - :type '(choice - (const :tag "Never use `electric-indent-mode'." t) - (repeat :tag "List of major-modes to avoid `electric-indent-mode'." symbol)) - :package-version '(aggressive-indent . "0.3.1")) - -(defcustom aggressive-indent-excluded-modes - '(elm-mode - haskell-mode - inf-ruby-mode - makefile-mode - makefile-gmake-mode - python-mode - sql-interactive-mode - text-mode - yaml-mode) - "Modes in which `aggressive-indent-mode' should not be activated. -This variable is only used if `global-aggressive-indent-mode' is -active. If the minor mode is turned on with the local command, -`aggressive-indent-mode', this variable is ignored." - :type '(repeat symbol) - :package-version '(aggressive-indent . "1.8.4")) - -(defcustom aggressive-indent-protected-commands '(undo undo-tree-undo undo-tree-redo whitespace-cleanup) - "Commands after which indentation will NOT be performed. -Aggressive indentation could break things like `undo' by locking -the user in a loop, so this variable is used to control which -commands will NOT be followed by a re-indent." - :type '(repeat symbol) - :package-version '(aggressive-indent . "0.1")) - -(defcustom aggressive-indent-protected-current-commands - '(query-replace-regexp query-replace) - "Like `aggressive-indent-protected-commands', but for the current command. -For instance, with the default value, this variable prevents -indentation during `query-replace' (but not after)." - :type '(repeat symbol) - :package-version '(aggressive-indent . "1.8.4")) - -(defcustom aggressive-indent-comments-too nil - "If non-nil, aggressively indent in comments as well." - :type 'boolean - :package-version '(aggressive-indent . "0.3")) - -(defcustom aggressive-indent-modes-to-prefer-defun - '(emacs-lisp-mode lisp-mode scheme-mode clojure-mode) - "List of major-modes in which indenting defun is preferred. -Add here any major modes with very good definitions of -`end-of-defun' and `beginning-of-defun', or modes which bug out -if you have `after-change-functions' (such as paredit). - -If current major mode is derived from one of these, -`aggressive-indent' will call `aggressive-indent-indent-defun' -after every command. Otherwise, it will call -`aggressive-indent-indent-region-and-on' after every buffer -change." - :type '(repeat symbol) - :package-version '(aggressive-indent . "0.3")) - -;;; Preventing indentation -(defconst aggressive-indent--internal-dont-indent-if - '((memq last-command aggressive-indent-protected-commands) - (memq this-command aggressive-indent-protected-current-commands) - (region-active-p) - buffer-read-only - undo-in-progress - (null (buffer-modified-p)) - (and (boundp 'smerge-mode) smerge-mode) - (equal (buffer-name) "*ediff-merge*") - (let ((line (thing-at-point 'line))) - (and (stringp line) - ;; If the user is starting to type a comment. - (stringp comment-start) - (string-match (concat "\\`[[:blank:]]*" - (substring comment-start 0 1) - "[[:blank:]]*$") - line))) - (let ((sp (syntax-ppss))) - ;; Comments. - (or (and (not aggressive-indent-comments-too) (elt sp 4)) - ;; Strings. - (elt sp 3)))) - "List of forms which prevent indentation when they evaluate to non-nil. -This is for internal use only. For user customization, use -`aggressive-indent-dont-indent-if' instead.") - -(eval-after-load 'yasnippet - '(when (boundp 'yas--active-field-overlay) - (add-to-list 'aggressive-indent--internal-dont-indent-if - '(and - (overlayp yas--active-field-overlay) - (overlay-end yas--active-field-overlay)) - 'append))) -(eval-after-load 'company - '(when (boundp 'company-candidates) - (add-to-list 'aggressive-indent--internal-dont-indent-if - 'company-candidates))) -(eval-after-load 'auto-complete - '(when (boundp 'ac-completing) - (add-to-list 'aggressive-indent--internal-dont-indent-if - 'ac-completing))) -(eval-after-load 'multiple-cursors-core - '(when (boundp 'multiple-cursors-mode) - (add-to-list 'aggressive-indent--internal-dont-indent-if - 'multiple-cursors-mode))) -(eval-after-load 'iedit - '(when (boundp 'iedit-mode) - (add-to-list 'aggressive-indent--internal-dont-indent-if - 'iedit-mode))) -(eval-after-load 'evil - '(when (boundp 'iedit-mode) - (add-to-list 'aggressive-indent--internal-dont-indent-if - 'iedit-mode))) -(eval-after-load 'coq - '(add-to-list 'aggressive-indent--internal-dont-indent-if - '(and (derived-mode-p 'coq-mode) - (not (string-match "\\.[[:space:]]*$" - (thing-at-point 'line)))))) -(eval-after-load 'ruby-mode - '(add-to-list 'aggressive-indent--internal-dont-indent-if - '(when (derived-mode-p 'ruby-mode) - (let ((line (thing-at-point 'line))) - (and (stringp line) - (string-match "\\b\\(begin\\|case\\|d\\(?:ef\\|o\\)\\|if\\) *$" line)))))) - -(defcustom aggressive-indent-dont-indent-if '() - "List of variables and functions to prevent aggressive indenting. -This variable is a list where each element is a Lisp form. -As long as any one of these forms returns non-nil, -aggressive-indent will not perform any indentation. - -See `aggressive-indent--internal-dont-indent-if' for usage examples. - -Note that this is only used once, and only on the line where the -point is when we're about to start indenting. In order to -prevent indentation of further lines, see -`aggressive-indent-stop-here-hook'." - :type '(repeat sexp) - :package-version '(aggressive-indent . "0.2")) - -(defcustom aggressive-indent-stop-here-hook nil - "A hook that runs on each line before it is indented. -If any function on this hook returns non-nil, it immediately -prevents indentation of the current line and any further -lines. - -Note that aggressive-indent does indentation in two stages. The -first stage indents the entire edited region, while the second -stage keeps indenting further lines until its own logic decide to -stop. This hook only affects the second stage. That is, it -effectly lets you add your own predicates to the logic that -decides when to stop. - -In order to prevent indentation before the first stage, see -`aggressive-indent-dont-indent-if' instead." - :type 'hook) - -(defvar aggressive-indent--error-message "One of the forms in `aggressive-indent-dont-indent-if' had the following error, I've disabled it until you fix it: %S" - "Error message thrown by `aggressive-indent-dont-indent-if'.") - -(defvar aggressive-indent--has-errored nil - "Keep track of whether `aggressive-indent-dont-indent-if' is throwing. -This is used to prevent an infinite error loop on the user.") - -(defun aggressive-indent--run-user-hooks () - "Safely run forms in `aggressive-indent-dont-indent-if'. -If any of them errors out, we only report it once until it stops -erroring again." - (and aggressive-indent-dont-indent-if - (condition-case er - (prog1 (eval (cons 'or aggressive-indent-dont-indent-if)) - (setq aggressive-indent--has-errored nil)) - (error (unless aggressive-indent--has-errored - (setq aggressive-indent--has-errored t) - (message aggressive-indent--error-message er)))))) - -;;; Indenting defun -(defcustom aggressive-indent-region-function #'indent-region - "Function called to indent a region. -It is called with two arguments, the region beginning and end." - :risky t - :type 'function) - -;;;###autoload -(defun aggressive-indent-indent-defun (&optional l r) - "Indent current defun. -Throw an error if parentheses are unbalanced. -If L and R are provided, use them for finding the start and end of defun." - (interactive) - (let ((p (point-marker))) - (set-marker-insertion-type p t) - (funcall aggressive-indent-region-function - (save-excursion - (when l (goto-char l)) - (beginning-of-defun 1) (point)) - (save-excursion - (when r (goto-char r)) - (end-of-defun 1) (point))) - (goto-char p))) - -(defun aggressive-indent--softly-indent-defun (&optional l r) - "Indent current defun unobstrusively. -Like `aggressive-indent-indent-defun', but without errors or -messages. L and R passed to `aggressive-indent-indent-defun'." - (cl-letf (((symbol-function 'message) #'ignore)) - (ignore-errors (aggressive-indent-indent-defun l r)))) - -;;; Indenting region -(defun aggressive-indent--indent-current-balanced-line (column) - "Indent current balanced line, if it starts at COLUMN. -Balanced line means anything contained in a sexp that starts at -the current line, or starts at the same line that one of these -sexps ends. - -Return non-nil only if the line's indentation actually changed." - (when (= (current-column) column) - (unless (= (point) - (progn (indent-according-to-mode) - (point))) - (let ((line-end (line-end-position))) - (forward-sexp 1) - (comment-forward (point-max)) - ;; We know previous sexp finished on a previous line when - ;; there's only be whitespace behind point. - (while (progn - (skip-chars-backward "[:blank:]") - (not (looking-at "^"))) - (forward-sexp 1) - (comment-forward (point-max))) - (when (looking-at "^") - (funcall aggressive-indent-region-function line-end (1- (point)))) - (skip-chars-forward "[:blank:]"))))) - -(defun aggressive-indent--extend-end-to-whole-sexps (beg end) - "Return a point >= END, so that it covers whole sexps from BEG." - (save-excursion - (goto-char beg) - (while (and (< (point) end) - (not (eobp))) - (forward-sexp 1)) - (point))) - -;;;###autoload -(defun aggressive-indent-indent-region-and-on (l r) - "Indent region between L and R, and then some. -Call `aggressive-indent-region-function' between L and R, and -then keep indenting until nothing more happens." - (interactive "r") - (let ((p (point-marker))) - (set-marker-insertion-type p t) - (unwind-protect - (progn - (unless (= l r) - (when (= (char-before r) ?\n) - (cl-decf r))) - ;; If L is at the end of a line, skip that line. - (unless (= l r) - (when (= (char-after l) ?\n) - (cl-incf l))) - ;; Indent the affected region. - (goto-char r) - (unless (= l r) (funcall aggressive-indent-region-function l r)) - ;; And then we indent each following line until nothing happens. - (forward-line 1) - (skip-chars-forward "[:blank:]\n\r\xc") - (let ((base-column (current-column))) - (while (and (not (eobp)) - (not (run-hook-with-args-until-success 'aggressive-indent-stop-here-hook)) - (aggressive-indent--indent-current-balanced-line base-column))))) - (goto-char p)))) - -(defun aggressive-indent--softly-indent-region-and-on (l r &rest _) - "Indent region between L and R, and a bit more. -Like `aggressive-indent-indent-region-and-on', but without errors -or messages." - (cl-letf (((symbol-function 'message) #'ignore)) - (ignore-errors (aggressive-indent-indent-region-and-on l r)))) - -;;; Tracking changes -(defvar aggressive-indent--changed-list nil - "List of (left right) limit of regions changed in the last command loop.") -(make-variable-buffer-local 'aggressive-indent--changed-list) - -(defun aggressive-indent--process-changed-list-and-indent () - "Indent the regions in `aggressive-indent--changed-list'." - (unless (or (run-hook-wrapped 'aggressive-indent--internal-dont-indent-if #'eval) - (aggressive-indent--run-user-hooks)) - (let ((after-change-functions (remove 'aggressive-indent--keep-track-of-changes after-change-functions)) - (inhibit-point-motion-hooks t) - (indent-function - (if (cl-member-if #'derived-mode-p aggressive-indent-modes-to-prefer-defun) - #'aggressive-indent--softly-indent-defun #'aggressive-indent--softly-indent-region-and-on))) - ;; Take the 10 most recent changes. - (let ((cell (nthcdr 10 aggressive-indent--changed-list))) - (when cell (setcdr cell nil))) - ;; (message "----------") - (while aggressive-indent--changed-list - ;; (message "%S" (car aggressive-indent--changed-list)) - (apply indent-function (car aggressive-indent--changed-list)) - (setq aggressive-indent--changed-list - (cdr aggressive-indent--changed-list)))))) - -(defun aggressive-indent--clear-change-list () - "Clear cache of all changed regions. " - (setq aggressive-indent--changed-list nil)) - -(defcustom aggressive-indent-sit-for-time 0.05 - "Time, in seconds, to wait before indenting. -If you feel aggressive-indent is causing Emacs to hang while -typing, try tweaking this number." - :type 'float) - -(defvar-local aggressive-indent--idle-timer nil - "Idle timer used for indentation") - -;; Ripped from Emacs 27.0 subr.el. -;; See Github Issue#111 and Emacs bug#31692. -(defmacro aggressive-indent--while-no-input (&rest body) - "Execute BODY only as long as there's no pending input. -If input arrives, that ends the execution of BODY, -and `while-no-input' returns t. Quitting makes it return nil. -If BODY finishes, `while-no-input' returns whatever value BODY produced." - (declare (debug t) (indent 0)) - (let ((catch-sym (make-symbol "input"))) - `(with-local-quit - (catch ',catch-sym - (let ((throw-on-input ',catch-sym) - val) - (setq val (or (input-pending-p) - (progn ,@body))) - (cond - ;; When input arrives while throw-on-input is non-nil, - ;; kbd_buffer_store_buffered_event sets quit-flag to the - ;; value of throw-on-input. If, when BODY finishes, - ;; quit-flag still has the same value as throw-on-input, it - ;; means BODY never tested quit-flag, and therefore ran to - ;; completion even though input did arrive before it - ;; finished. In that case, we must manually simulate what - ;; 'throw' in process_quit_flag would do, and we must - ;; reset quit-flag, because leaving it set will cause us - ;; quit to top-level, which has undesirable consequences, - ;; such as discarding input etc. We return t in that case - ;; because input did arrive during execution of BODY. - ((eq quit-flag throw-on-input) - (setq quit-flag nil) - t) - ;; This is for when the user actually QUITs during - ;; execution of BODY. - (quit-flag - nil) - (t val))))))) - -(defun aggressive-indent--maybe-cancel-timer () - "Cancel and remove the timer if it is set." - (when (timerp aggressive-indent--idle-timer) - (cancel-timer aggressive-indent--idle-timer) - (setq aggressive-indent--idle-timer nil))) - -(defun aggressive-indent--indent-if-changed (buffer) - "Indent any region that changed in BUFFER in the last command loop." - (if (not (buffer-live-p buffer)) - (aggressive-indent--maybe-cancel-timer) - (with-current-buffer buffer - (when (and aggressive-indent-mode aggressive-indent--changed-list) - (save-excursion - (save-selected-window - (aggressive-indent--while-no-input - (aggressive-indent--process-changed-list-and-indent)))) - (aggressive-indent--maybe-cancel-timer))))) - -(defun aggressive-indent--keep-track-of-changes (l r &rest _) - "Store the limits (L and R) of each change in the buffer." - (when aggressive-indent-mode - (push (list l r) aggressive-indent--changed-list) - (aggressive-indent--maybe-cancel-timer) - (setq aggressive-indent--idle-timer - (run-with-idle-timer aggressive-indent-sit-for-time t #'aggressive-indent--indent-if-changed (current-buffer))))) - -;;; Minor modes -;;;###autoload -(define-minor-mode aggressive-indent-mode - nil nil " =>" - `((,(kbd "C-c C-q") . aggressive-indent-indent-defun) - ([backspace] - menu-item "maybe-delete-indentation" ignore :filter - (lambda (&optional _) - (when (and (looking-back "^[[:blank:]]+") - ;; Wherever we don't want to indent, we probably also - ;; want the default backspace behavior. - (not (run-hook-wrapped 'aggressive-indent--internal-dont-indent-if #'eval)) - (not (aggressive-indent--run-user-hooks))) - #'delete-indentation)))) - (if aggressive-indent-mode - (if (and global-aggressive-indent-mode - (or (cl-member-if #'derived-mode-p aggressive-indent-excluded-modes) - (equal indent-line-function #'indent-relative) - (derived-mode-p 'text-mode) - (eq major-mode 'fundamental-mode) - buffer-read-only)) - (aggressive-indent-mode -1) - ;; Should electric indent be ON or OFF? - (if (or (eq aggressive-indent-dont-electric-modes t) - (cl-member-if #'derived-mode-p aggressive-indent-dont-electric-modes)) - (aggressive-indent--local-electric nil) - (aggressive-indent--local-electric t)) - (add-hook 'after-change-functions #'aggressive-indent--keep-track-of-changes nil 'local) - (add-hook 'after-revert-hook #'aggressive-indent--clear-change-list nil 'local) - (add-hook 'before-save-hook #'aggressive-indent--process-changed-list-and-indent nil 'local) - (add-hook 'kill-buffer-hook #'aggressive-indent--maybe-cancel-timer nil 'local)) - ;; Clean the hooks - (aggressive-indent--maybe-cancel-timer) - (remove-hook 'after-change-functions #'aggressive-indent--keep-track-of-changes 'local) - (remove-hook 'after-revert-hook #'aggressive-indent--clear-change-list 'local) - (remove-hook 'before-save-hook #'aggressive-indent--process-changed-list-and-indent 'local) - (remove-hook 'post-command-hook #'aggressive-indent--softly-indent-defun 'local) - (remove-hook 'kill-buffer-hook #'aggressive-indent--maybe-cancel-timer 'local))) - -(defun aggressive-indent--local-electric (on) - "Turn variable `electric-indent-mode' on or off locally, as per boolean ON." - (if (fboundp 'electric-indent-local-mode) - (electric-indent-local-mode (if on 1 -1)) - (set (make-local-variable 'electric-indent-mode) on))) - -;;;###autoload -(define-globalized-minor-mode global-aggressive-indent-mode - aggressive-indent-mode aggressive-indent-mode) - -;;;###autoload -(defalias 'aggressive-indent-global-mode - #'global-aggressive-indent-mode) - -(provide 'aggressive-indent) -;;; aggressive-indent.el ends here blob - f72e143b3082142de893b22717d28359d38233f1 (mode 644) blob + /dev/null Binary files elpa/aggressive-indent-1.10.0/c-example.gif and /dev/null differ blob - 220289a4785da7c90185b5383f1c6ad1210cc8bf (mode 644) blob + /dev/null Binary files elpa/aggressive-indent-1.10.0/lisp-example.gif and /dev/null differ blob - 0ad7323c999b095e79d7c934bdb6585d84d31467 (mode 644) blob + /dev/null --- elpa/aggressive-indent-1.10.0.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2021-07-08T11:05:01+0200 using RSA \ No newline at end of file blob - 5b61f48365367a3e207f7c469a7d42c79fa68eb2 (mode 644) blob + /dev/null --- elpa/aio-1.0/README.md +++ /dev/null @@ -1,180 +0,0 @@ -# aio: async/await for Emacs Lisp - -`aio` is to Emacs Lisp as [`asyncio`][asyncio] is to Python. This -package builds upon Emacs 25 generators to provide functions that -pause while they wait on asynchronous events. They do not block any -thread while paused. - -See also: [An Async / Await Library for Emacs Lisp][post] - -Because it uses `record`, this package requires Emacs 26 or later. - -## Usage - -An async function is defined using `aio-defun` or `aio-lambda`. The -body of such functions can use `aio-await` to pause the function and -wait on a given promise. The function continues with the promise's -resolved value when it's ready. The package provides a number of -functions that return promises, and every async function returns a -promise representing its future return value. - -For example: - -```el -(aio-defun foo (url) - (aio-await (aio-sleep 3)) - (message "Done sleeping. Now fetching %s" url) - (let* ((result (aio-await (aio-url-retrieve url))) - (contents (with-current-buffer (cdr result) - (prog1 (buffer-string) - (kill-buffer))))) - (message "Result: %s" contents))) -``` - -If an uncaught signal terminates an asynchronous function, that signal -is captured by its return value promise and propagated into any -function that awaits on that function. - -```el -(aio-defun divide (a b) - (aio-await (aio-sleep 1)) - (/ a b)) - -(aio-defun divide-safe (a b) - (condition-case error - (aio-await (divide a b)) - (arith-error :arith-error))) - -(aio-wait-for (divide-safe 1.0 2.0)) -;; => 0.5 - -(aio-wait-for (divide-safe 0 0)) -;; => :arith-error -``` - -To convert a callback-based function into an awaitable, async-friendly -function, create a new promise object with `aio-promise`, then -`aio-resolve` that promise in the callback. The helper function, -`aio-make-callback`, makes this easy. - -## Utility macros and functions - -```el -(aio-wait-for promise) -;; Synchronously wait for PROMISE, blocking the current thread. - -(aio-cancel promise) -;; Attempt to cancel PROMISE, returning non-nil if successful. - -(aio-with-promise promise &rest body) [macro] -;; Evaluate BODY and resolve PROMISE with the result. - -(aio-with-async &rest body) [macro] -;; Evaluate BODY asynchronously as if it was inside `aio-lambda'. - -(aio-make-callback &key tag once) -;; Return a new callback function and its first promise. - -(aio-chain expr) [macro] -;; `aio-await' on EXPR and replace place EXPR with the next promise. -``` - -The `aio-make-callback` function is especially useful for callbacks -that are invoked repeatedly, such as process filters and sentinels. -The `aio-chain` macro works in conjunction. - -## Awaitable functions - -Here are some useful promise-returning — i.e. awaitable — functions -defined by this package. - -```el -(aio-sleep seconds &optional result) -;; Return a promise that is resolved after SECONDS with RESULT. - -(aio-idle seconds &optional result) -;; Return a promise that is resolved after idle SECONDS with RESULT. - -(aio-url-retrieve url &optional silent inhibit-cookies) -;; Wraps `url-retrieve' in a promise. - -(aio-all promises) -;; Return a promise that resolves when all PROMISES are resolved." -``` - -## Select API - -This package includes a select()-like, level-triggered API for waiting -on multiple promises at once. Create a "select" object, add promises -to it, and await on it. Resolved and returned promises are -automatically removed, and the "select" object can be reused. - -```el -(aio-make-select &optional promises) -;; Create a new `aio-select' object for waiting on multiple promises. - -(aio-select-add select promise) -;; Add PROMISE to the set of promises in SELECT. - -(aio-select-remove select promise) -;; Remove PROMISE form the set of promises in SELECT. - -(aio-select-promises select) -;; Return a list of promises in SELECT. - -(aio-select select) -;; Return a promise that resolves when any promise in SELECT resolves. -``` - -For example, here's an implementation of sleep sort: - -```el -(aio-defun sleep-sort (values) - (let* ((promises (mapcar (lambda (v) (aio-sleep v v)) values)) - (select (aio-make-select promises))) - (cl-loop repeat (length promises) - for next = (aio-await (aio-select select)) - collect (aio-await next)))) -``` - -## Semaphore API - -Semaphores work just as they would as a thread synchronization -primitive. There's an internal counter that cannot drop below zero, -and `aio-sem-wait` is an awaitable function that may block the -asynchronous function until another asynchronous function calls -`aio-sem-post`. Blocked functions wait in a FIFO queue and are awoken -in the same order that they awaited. - -```el -(aio-sem init) -;; Create a new semaphore with initial value INIT. - -(aio-sem-post sem) -;; Increment the value of SEM. - -(aio-sem-wait sem) -;; Decrement the value of SEM. -``` - -This can be used to create a work queue. For example, here's a -configurable download queue for `url-retrieve`: - -```el -(defun fetch (url-list max-parallel callback) - (let ((sem (aio-sem max-parallel))) - (dolist (url url-list) - (aio-with-async - (aio-await (aio-sem-wait sem)) - (cl-destructuring-bind (status . buffer) - (aio-await (aio-url-retrieve url)) - (aio-sem-post sem) - (funcall callback - (with-current-buffer buffer - (prog1 (buffer-string) - (kill-buffer))))))))) -``` - - -[asyncio]: https://docs.python.org/3/library/asyncio.html -[post]: https://nullprogram.com/blog/2019/03/10/ blob - 68a49daad8ff7e35068f2b7a97d643aab440eaec (mode 644) blob + /dev/null --- elpa/aio-1.0/UNLICENSE +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to blob - 192fee9ba2dda136830572400c115497572a8e54 (mode 644) blob + /dev/null --- elpa/aio-1.0/aio-autoloads.el +++ /dev/null @@ -1,28 +0,0 @@ -;;; aio-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from aio.el - -(register-definition-prefixes "aio" '("aio-")) - -;;; End of scraped data - -(provide 'aio-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; aio-autoloads.el ends here blob - 9a52c8e7f51a1a800e26ef715f2802f9542bd5cb (mode 644) blob + /dev/null --- elpa/aio-1.0/aio-pkg.el +++ /dev/null @@ -1,12 +0,0 @@ -(define-package "aio" "1.0" "async/await for Emacs Lisp" - '((emacs "26.1")) - :commit "077722896e649e7a33dcafbc4585686a29423979" :authors - '(("Christopher Wellons" . "wellons@nullprogram.com")) - :maintainers - '(("Christopher Wellons" . "wellons@nullprogram.com")) - :maintainer - '("Christopher Wellons" . "wellons@nullprogram.com") - :url "https://github.com/skeeto/emacs-aio") -;; Local Variables: -;; no-byte-compile: t -;; End: blob - 203f441aa1034509cf97a7a985e7621354d16379 (mode 644) blob + /dev/null --- elpa/aio-1.0/aio.el +++ /dev/null @@ -1,425 +0,0 @@ -;;; aio.el --- async/await for Emacs Lisp -*- lexical-binding: t; -*- - -;; This is free and unencumbered software released into the public domain. - -;; Author: Christopher Wellons -;; URL: https://github.com/skeeto/emacs-aio -;; Version: 1.0 -;; Package-Requires: ((emacs "26.1")) - -;;; Commentary: - -;; The main components of this package are `aio-defun' / `aio-lambda' -;; to define async function, and `aio-await' to pause these functions -;; while they wait on asynchronous events. When an asynchronous -;; function is paused, the main thread is not blocked. It is no more -;; or less powerful than callbacks, but is nicer to use. - -;; This is implementation is based on Emacs 25 generators, and -;; asynchronous functions are actually iterators in disguise, operated -;; as stackless, asymmetric coroutines. - -;;; Code: - -(require 'cl-lib) -(require 'generator) - -;; Register new error types -(define-error 'aio-cancel "Promise was canceled") -(define-error 'aio-timeout "Timeout was reached") - -(defun aio-promise () - "Create a new promise object." - (record 'aio-promise nil ())) - -(defsubst aio-promise-p (object) - "Return non-nil if OBJECT is a promise." - (and (eq 'aio-promise (type-of object)) - (= 3 (length object)))) - -(defsubst aio-result (promise) - "Return the result of PROMISE, or nil if it is unresolved. - -Promise results are wrapped in a function. The result must be -called (e.g. `funcall') in order to retrieve the value." - (unless (aio-promise-p promise) - (signal 'wrong-type-argument (list 'aio-promise-p promise))) - (aref promise 1)) - -(defun aio-listen (promise callback) - "Add CALLBACK to PROMISE. - -If the promise has already been resolved, the callback will be -scheduled for the next event loop turn." - (let ((result (aio-result promise))) - (if result - (run-at-time 0 nil callback result) - (push callback (aref promise 2))))) - -(defun aio-resolve (promise value-function) - "Resolve this PROMISE with VALUE-FUNCTION. - -A promise can only be resolved once, and any further calls to -`aio-resolve' are silently ignored. The VALUE-FUNCTION must be a -function that takes no arguments and either returns the result -value or rethrows a signal." - (unless (functionp value-function) - (signal 'wrong-type-argument (list 'functionp value-function))) - (unless (aio-result promise) - (let ((callbacks (nreverse (aref promise 2)))) - (setf (aref promise 1) value-function - (aref promise 2) ()) - (dolist (callback callbacks) - (run-at-time 0 nil callback value-function))))) - -(defun aio--step (iter promise yield-result) - "Advance ITER to the next promise. - -PROMISE is the return promise of the iterator, which was returned -by the originating async function. YIELD-RESULT is the value -function result directly from the previously yielded promise." - (condition-case _ - (cl-loop for result = (iter-next iter yield-result) - then (iter-next iter (lambda () result)) - until (aio-promise-p result) - finally (aio-listen result - (lambda (value) - (aio--step iter promise value)))) - (iter-end-of-sequence))) - -(defmacro aio-with-promise (promise &rest body) - "Evaluate BODY and resolve PROMISE with the result. - -If the body signals an error, this error will be stored in the -promise and rethrown in the promise's listeners." - (declare (indent defun)) - (cl-assert (eq lexical-binding t)) - `(aio-resolve ,promise - (condition-case error - (let ((result (progn ,@body))) - (lambda () result)) - (error (lambda () - (signal (car error) (cdr error))))))) - -(defmacro aio-await (expr) - "If EXPR evaluates to a promise, pause until the promise is resolved. - -Pausing an async function does not block Emacs' main thread. If -EXPR doesn't evaluate to a promise, the value is returned -immediately and the function is not paused. Since async functions -return promises, async functions can await directly on other -async functions using this macro. - -This macro can only be used inside an async function, either -`aio-lambda' or `aio-defun'." - `(funcall (iter-yield ,expr))) - -(defmacro aio-lambda (arglist &rest body) - "Like `lambda', but defines an async function. - -The body of this function may use `aio-await' to wait on -promises. When an async function is called, it immediately -returns a promise that will resolve to the function's return -value, or any uncaught error signal." - (declare (indent defun) - (doc-string 3)) - (let ((args (make-symbol "args")) - (promise (make-symbol "promise")) - (split-body (macroexp-parse-body body))) - `(lambda (&rest ,args) - ,@(car split-body) - (let* ((,promise (aio-promise)) - (iter (apply (iter-lambda ,arglist - (aio-with-promise ,promise - ,@(cdr split-body))) - ,args))) - (prog1 ,promise - (aio--step iter ,promise nil)))))) - -(defmacro aio-defun (name arglist &rest body) - "Like `aio-lambda' but gives the function a name like `defun'." - (declare (indent defun) - (doc-string 3)) - `(defalias ',name (aio-lambda ,arglist ,@body))) - -(defun aio-wait-for (promise) - "Synchronously wait for PROMISE, blocking the current thread." - (while (null (aio-result promise)) - (accept-process-output)) - (funcall (aio-result promise))) - -(defun aio-cancel (promise &optional reason) - "Attempt to cancel PROMISE, returning non-nil if successful. - -All awaiters will receive an aio-cancel signal. The actual -underlying asynchronous operation will not actually be canceled." - (unless (aio-result promise) - (aio-resolve promise (lambda () (signal 'aio-cancel reason))))) - -(defmacro aio-with-async (&rest body) - "Evaluate BODY asynchronously as if it was inside `aio-lambda'. - -Since BODY is evalued inside an asynchronous lambda, `aio-await' -is available here. This macro evaluates to a promise for BODY's -eventual result." - (declare (indent 0)) - `(let ((promise (funcall (aio-lambda () - (aio-await (aio-sleep 0)) - ,@body)))) - (prog1 promise - ;; The is the main feature: Force the final result to be - ;; realized so that errors are reported. - (aio-listen promise #'funcall)))) - -(defmacro aio-chain (expr) - "`aio-await' on EXPR and replace place EXPR with the next promise. - -EXPR must be setf-able. Returns (cdr result). This macro is -intended to be used with `aio-make-callback' in order to follow -a chain of promise-yielding promises." - (let ((result (make-symbol "result"))) - `(let ((,result (aio-await ,expr))) - (setf ,expr (car ,result)) - (cdr ,result)))) - -;; Useful promise-returning functions: - -(require 'url) - -(aio-defun aio-all (promises) - "Return a promise that resolves when all PROMISES are resolved." - (dolist (promise promises) - (aio-await promise))) - -(defun aio-catch (promise) - "Return a new promise that wraps PROMISE but will never signal. - -The promise value is a cons where the car is either :success or -:error. For :success, the cdr will be the result value. For -:error, the cdr will be the error data." - (let ((result (aio-promise))) - (cl-flet ((callback (value) - (aio-resolve result - (lambda () - (condition-case error - (cons :success (funcall value)) - (error (cons :error error))))))) - (prog1 result - (aio-listen promise #'callback))))) - -(defun aio-sleep (seconds &optional result) - "Create a promise that is resolved after SECONDS with RESULT. - -The result is a value, not a value function, and it will be -automatically wrapped with a value function (see `aio-resolve')." - (let ((promise (aio-promise))) - (prog1 promise - (run-at-time seconds nil - #'aio-resolve promise (lambda () result))))) - -(defun aio-idle (seconds &optional result) - "Create a promise that is resolved after idle SECONDS with RESULT. - -The result is a value, not a value function, and it will be -automatically wrapped with a value function (see `aio-resolve')." - (let ((promise (aio-promise))) - (prog1 promise - (run-with-idle-timer seconds nil - #'aio-resolve promise (lambda () result))))) - -(defun aio-timeout (seconds) - "Create a promise with a timeout error after SECONDS." - (let ((timeout (aio-promise))) - (prog1 timeout - (run-at-time seconds nil#'aio-resolve timeout - (lambda () (signal 'aio-timeout seconds)))))) - -(defun aio-url-retrieve (url &optional silent inhibit-cookies) - "Wraps `url-retrieve' in a promise. - -This function will never directly signal an error. Instead any -errors will be delivered via the returned promise. The promise -result is a cons of (status . buffer). This buffer is a clone of -the buffer created by `url-retrieve' and should be killed by the -caller." - (let ((promise (aio-promise))) - (prog1 promise - (condition-case error - (url-retrieve url (lambda (status) - (let ((value (cons status (clone-buffer)))) - (aio-resolve promise (lambda () value)))) - silent inhibit-cookies) - (error (aio-resolve promise - (lambda () - (signal (car error) (cdr error))))))))) - -(cl-defun aio-make-callback (&key tag once) - "Return a new callback function and its first promise. - -Returns a cons (callback . promise) where callback is function -suitable for repeated invocation. This makes it useful for -process filters and sentinels. The promise is the first promise -to be resolved by the callback. - -The promise resolves to: - (next-promise . callback-args) -Or when TAG is supplied: - (next-promise TAG . callback-args) -Or if ONCE is non-nil: - callback-args - -The callback resolves next-promise on the next invocation. This -creates a chain of promises representing the sequence of calls. -Note: To avoid keeping lots of garbage in memory, avoid holding -onto the first promise (i.e. capturing it in a closure). - -The `aio-chain' macro makes it easier to use these promises." - (let* ((promise (aio-promise)) - (callback - (if once - (lambda (&rest args) - (let ((result (if tag - (cons tag args) - args))) - (aio-resolve promise (lambda () result)))) - (lambda (&rest args) - (let* ((next-promise (aio-promise)) - (result (if tag - (cons next-promise (cons tag args)) - (cons next-promise args)))) - (aio-resolve promise (lambda () result)) - (setf promise next-promise)))))) - (cons callback promise))) - -;; A simple little queue - -(defsubst aio--queue-empty-p (queue) - "Return non-nil if QUEUE is empty. -An empty queue is (nil . nil)." - (null (caar queue))) - -(defsubst aio--queue-get (queue) - "Get the next item from QUEUE, or nil for empty." - (let ((head (car queue))) - (cond ((null head) - nil) - ((eq head (cdr queue)) - (prog1 (car head) - (setf (car queue) nil - (cdr queue) nil))) - ((prog1 (car head) - (setf (car queue) (cdr head))))))) - -(defsubst aio--queue-put (queue element) - "Append ELEMENT to QUEUE, returning ELEMENT." - (let ((new (list element))) - (prog1 element - (if (null (car queue)) - (setf (car queue) new - (cdr queue) new) - (setf (cdr (cdr queue)) new - (cdr queue) new))))) - -;; An efficient select()-like interface for promises - -(defun aio-make-select (&optional promises) - "Create a new `aio-select' object for waiting on multiple promises." - (let ((select (record 'aio-select - ;; Membership table - (make-hash-table :test 'eq) - ;; "Seen" table (avoid adding multiple callback) - (make-hash-table :test 'eq :weakness 'key) - ;; Queue of pending resolved promises - (cons nil nil) - ;; Callback to resolve select's own promise - nil))) - (prog1 select - (dolist (promise promises) - (aio-select-add select promise))))) - -(defun aio-select-add (select promise) - "Add PROMISE to the set of promises in SELECT. - -SELECT is created with `aio-make-select'. It is valid to add a -promise that was previously removed." - (let ((members (aref select 1)) - (seen (aref select 2))) - (prog1 promise - (unless (gethash promise seen) - (setf (gethash promise seen) t - (gethash promise members) t) - (aio-listen promise - (lambda (_) - (when (gethash promise members) - (aio--queue-put (aref select 3) promise) - (remhash promise members) - (let ((callback (aref select 4))) - (when callback - (setf (aref select 4) nil) - (funcall callback)))))))))) - -(defun aio-select-remove (select promise) - "Remove PROMISE form the set of promises in SELECT. - -SELECT is created with `aio-make-select'." - (remhash promise (aref select 1))) - -(defun aio-select-promises (select) - "Return a list of promises in SELECT. - -SELECT is created with `aio-make-select'. " - (cl-loop for key being the hash-keys of (aref select 1) - collect key)) - -(defun aio-select (select) - "Return a promise that resolves when any promise in SELECT resolves. - -SELECT is created with `aio-make-select'. This function is -level-triggered: if a promise in SELECT is already resolved, it -returns immediately with that promise. Promises returned by -`aio-select' are automatically removed from SELECT. Use this -function to repeatedly wait on a set of promises. - -Note: The promise returned by this function resolves to another -promise, not that promise's result. You will need to `aio-await' -on it, or use `aio-result'." - (let* ((result (aio-promise)) - (callback (lambda () - (let ((promise (aio--queue-get (aref select 3)))) - (aio-resolve result (lambda () promise)))))) - (prog1 result - (if (aio--queue-empty-p (aref select 3)) - (setf (aref select 4) callback) - (funcall callback))))) - -;; Semaphores - -(defun aio-sem (init) - "Create a new semaphore with initial value INIT." - (record 'aio-sem - ;; Semaphore value - init - ;; Queue of waiting async functions - (cons nil nil))) - -(defun aio-sem-post (sem) - "Increment the value of SEM. - -If asynchronous functions are awaiting on SEM, then one will be -woken up. This function is not awaitable." - (when (<= (cl-incf (aref sem 1)) 0) - (let ((waiting (aio--queue-get (aref sem 2)))) - (when waiting - (aio-resolve waiting (lambda () nil)))))) - -(defun aio-sem-wait (sem) - "Decrement the value of SEM. - -If SEM is at zero, returns a promise that will resolve when -another asynchronous function uses `aio-sem-post'." - (when (< (cl-decf (aref sem 1)) 0) - (aio--queue-put (aref sem 2) (aio-promise)))) - -(provide 'aio) - -;;; aio.el ends here blob - 633e20547938c828c3079adefe471b160215ce6e (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/all-the-icons-autoloads.el +++ /dev/null @@ -1,70 +0,0 @@ -;;; all-the-icons-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from all-the-icons.el - -(autoload 'all-the-icons-icon-for-dir "all-the-icons" "\ -Get the formatted icon for DIR. -ARG-OVERRIDES should be a plist containining `:height', -`:v-adjust' or `:face' properties like in the normal icon -inserting functions. - -Note: You want chevron, please use `all-the-icons-icon-for-dir-with-chevron'. - -(fn DIR &rest ARG-OVERRIDES)") -(autoload 'all-the-icons-icon-for-file "all-the-icons" "\ -Get the formatted icon for FILE. -ARG-OVERRIDES should be a plist containining `:height', -`:v-adjust' or `:face' properties like in the normal icon -inserting functions. - -(fn FILE &rest ARG-OVERRIDES)") -(autoload 'all-the-icons-icon-for-mode "all-the-icons" "\ -Get the formatted icon for MODE. -ARG-OVERRIDES should be a plist containining `:height', -`:v-adjust' or `:face' properties like in the normal icon -inserting functions. - -(fn MODE &rest ARG-OVERRIDES)") -(autoload 'all-the-icons-icon-for-url "all-the-icons" "\ -Get the formatted icon for URL. -If an icon for URL isn't found in `all-the-icons-url-alist', a globe is used. -ARG-OVERRIDES should be a plist containining `:height', -`:v-adjust' or `:face' properties like in the normal icon -inserting functions. - -(fn URL &rest ARG-OVERRIDES)") -(autoload 'all-the-icons-install-fonts "all-the-icons" "\ -Helper function to download and install the latests fonts based on OS. -When PFX is non-nil, ignore the prompt and just install - -(fn &optional PFX)" t) -(autoload 'all-the-icons-insert "all-the-icons" "\ -Interactive icon insertion function. -When Prefix ARG is non-nil, insert the propertized icon. -When FAMILY is non-nil, limit the candidates to the icon set matching it. - -(fn &optional ARG FAMILY)" t) -(register-definition-prefixes "all-the-icons" '("all-the-icons-")) - -;;; End of scraped data - -(provide 'all-the-icons-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; all-the-icons-autoloads.el ends here blob - 4408e97b09531753e362ba268c48418bf0242a1a (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/all-the-icons-faces.el +++ /dev/null @@ -1,230 +0,0 @@ -;;; all-the-icons-faces.el --- A module of faces for all-the-icons - -;; Copyright (C) 2016 Dominic Charlesworth - -;; Author: Dominic Charlesworth -;; Version: 1.0.0 -;; Package-Requires: ((emacs "24.3")) -;; URL: https://github.com/domtronn/all-the-icons.el -;; Keywords: convenient, lisp - -;; This program 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 -;; of the License, or (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This file contains all of the faces used by the package for -;; colouring icons - -;;; Code: - -(defgroup all-the-icons-faces nil - "Manage how All The Icons icons are coloured and themed." - :prefix "all-the-icons-" - :group 'tools - :group 'all-the-icons) - - -;; red -(defface all-the-icons-red - '((((background dark)) :foreground "#AC4142") - (((background light)) :foreground "#AC4142")) - "Face for red icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lred - '((((background dark)) :foreground "#EB595A") - (((background light)) :foreground "#EB595A")) - "Face for lred icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dred - '((((background dark)) :foreground "#843031") - (((background light)) :foreground "#843031")) - "Face for dred icons" - :group 'all-the-icons-faces) -(defface all-the-icons-red-alt - '((((background dark)) :foreground "#ce5643") - (((background light)) :foreground "#843031")) - "Face for dred icons" - :group 'all-the-icons-faces) - -;; green -(defface all-the-icons-green - '((((background dark)) :foreground "#90A959") - (((background light)) :foreground "#90A959")) - "Face for green icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lgreen - '((((background dark)) :foreground "#C6E87A") - (((background light)) :foreground "#3D6837")) - "Face for lgreen icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dgreen - '((((background dark)) :foreground "#6D8143") - (((background light)) :foreground "#6D8143")) - "Face for dgreen icons" - :group 'all-the-icons-faces) - -;; yellow -(defface all-the-icons-yellow - '((((background dark)) :foreground "#FFD446") - (((background light)) :foreground "#FFCC0E")) - "Face for yellow icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lyellow - '((((background dark)) :foreground "#FFC16D") - (((background light)) :foreground "#FF9300")) - "Face for lyellow icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dyellow - '((((background dark)) :foreground "#B48D56") - (((background light)) :foreground "#B48D56")) - "Face for dyellow icons" - :group 'all-the-icons-faces) - -;; blue -(defface all-the-icons-blue - '((((background dark)) :foreground "#6A9FB5") - (((background light)) :foreground "#6A9FB5")) - "Face for blue icons" - :group 'all-the-icons-faces) -(defface all-the-icons-blue-alt - '((((background dark)) :foreground "#2188b6") - (((background light)) :foreground "#2188b6")) - "Face for blue icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lblue - '((((background dark)) :foreground "#8FD7F4") - (((background light)) :foreground "#677174")) - "Face for lblue icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dblue - '((((background dark)) :foreground "#446674") - (((background light)) :foreground "#446674")) - "Face for dblue icons" - :group 'all-the-icons-faces) - -;; maroon -(defface all-the-icons-maroon - '((((background dark)) :foreground "#8F5536") - (((background light)) :foreground "#8F5536")) - "Face for maroon icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lmaroon - '((((background dark)) :foreground "#CE7A4E") - (((background light)) :foreground "#CE7A4E")) - "Face for lmaroon icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dmaroon - '((((background dark)) :foreground "#72584B") - (((background light)) :foreground "#72584B")) - "Face for dmaroon icons" - :group 'all-the-icons-faces) - -;; purple -(defface all-the-icons-purple - '((((background dark)) :foreground "#AA759F") - (((background light)) :foreground "#68295B")) - "Face for purple icons" - :group 'all-the-icons-faces) -(defface all-the-icons-purple-alt - '((((background dark)) :foreground "#5D54E1") - (((background light)) :foreground "#5D54E1")) - "Face for purple icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lpurple - '((((background dark)) :foreground "#E69DD6") - (((background light)) :foreground "#E69DD6")) - "Face for lpurple icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dpurple - '((((background dark)) :foreground "#694863") - (((background light)) :foreground "#694863")) - "Face for dpurple icons" - :group 'all-the-icons-faces) - -;; orange -(defface all-the-icons-orange - '((((background dark)) :foreground "#D4843E") - (((background light)) :foreground "#D4843E")) - "Face for orange icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lorange - '((((background dark)) :foreground "#FFA500") - (((background light)) :foreground "#FFA500")) - "Face for lorange icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dorange - '((((background dark)) :foreground "#915B2D") - (((background light)) :foreground "#915B2D")) - "Face for dorange icons" - :group 'all-the-icons-faces) - -;; cyan -(defface all-the-icons-cyan - '((((background dark)) :foreground "#75B5AA") - (((background light)) :foreground "#75B5AA")) - "Face for cyan icons" - :group 'all-the-icons-faces) -(defface all-the-icons-cyan-alt - '((((background dark)) :foreground "#61dafb") - (((background light)) :foreground "#0595bd")) - "Face for cyan icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lcyan - '((((background dark)) :foreground "#A5FDEC") - (((background light)) :foreground "#2C7D6E")) - "Face for lcyan icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dcyan - '((((background dark)) :foreground "#48746D") - (((background light)) :foreground "#48746D")) - "Face for dcyan icons" - :group 'all-the-icons-faces) - -;; pink -(defface all-the-icons-pink - '((((background dark)) :foreground "#F2B4B8") - (((background light)) :foreground "#FC505B")) - "Face for pink icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lpink - '((((background dark)) :foreground "#FFBDC1") - (((background light)) :foreground "#FF505B")) - "Face for lpink icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dpink - '((((background dark)) :foreground "#B18286") - (((background light)) :foreground "#7E5D5F")) - "Face for dpink icons" - :group 'all-the-icons-faces) - -;; silver -(defface all-the-icons-silver - '((((background dark)) :foreground "#716E68") - (((background light)) :foreground "#716E68")) - "Face for silver icons" - :group 'all-the-icons-faces) -(defface all-the-icons-lsilver - '((((background dark)) :foreground "#B9B6AA") - (((background light)) :foreground "#7F7869")) - "Face for lsilver icons" - :group 'all-the-icons-faces) -(defface all-the-icons-dsilver - '((((background dark)) :foreground "#838484") - (((background light)) :foreground "#838484")) - "Face for dsilver icons" - :group 'all-the-icons-faces) - - -(provide 'all-the-icons-faces) -;;; all-the-icons-faces.el ends here blob - 5f8c1b574f574019ca68442723029ee89864f192 (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/all-the-icons-pkg.el +++ /dev/null @@ -1,14 +0,0 @@ -(define-package "all-the-icons" "5.0.0" "A library for inserting Developer icons" - '((emacs "24.3")) - :commit "facbde4a7be292bf9490932cbe403b443273f45d" :authors - '(("Dominic Charlesworth" . "dgc336@gmail.com")) - :maintainers - '(("Dominic Charlesworth" . "dgc336@gmail.com")) - :maintainer - '("Dominic Charlesworth" . "dgc336@gmail.com") - :keywords - '("convenient" "lisp") - :url "https://github.com/domtronn/all-the-icons.el") -;; Local Variables: -;; no-byte-compile: t -;; End: blob - f38b3a465ecba5c59e2383dc4e1eed6243e8e56b (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/all-the-icons.el +++ /dev/null @@ -1,1159 +0,0 @@ -;;; all-the-icons.el --- A library for inserting Developer icons -*- lexical-binding: t; -*- - -;; Copyright (C) 2016 Dominic Charlesworth - -;; Author: Dominic Charlesworth -;; Version: 5.0.0 -;; Package-Requires: ((emacs "24.3")) -;; URL: https://github.com/domtronn/all-the-icons.el -;; Keywords: convenient, lisp - -;; This program 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 -;; of the License, or (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This package is a utility for using and formatting various Icon -;; fonts within Emacs. Icon Fonts allow you to propertize and format -;; icons the same way you would normal text. This enables things such -;; as better scaling of and anti aliasing of the icons. - -;; This package was inspired by - -;; - `mode-icons' for Emacs, found at https://github.com/ryuslash/mode-icons -;; - `file-icons' for Atom, found at https://atom.io/packages/file-icons - -;; Currently, this package provides an interface to the following Icon Fonts - -;; - Atom File Icons, found at https://atom.io/packages/file-icons -;; - FontAwesome Icons, found at http://fontawesome.io/ -;; - GitHub Octicons, found at http://octicons.github.com -;; - Material Design Icons, found at http://google.github.io/material-design-icons/ -;; - Weather Icons, found at https://erikflowers.github.io/weather-icons/ -;; - AllTheIcons, a custom Icon Font maintained as part of this package - -;; Requests for new icons will be accepted and added to the AllTheIcons Icon Font - -;;; Usage: - -;; The simplest usage for this package is to use the following functions; - -;; `all-the-icons-icon-for-buffer' -;; `all-the-icons-icon-for-dir' -;; `all-the-icons-icon-for-file' -;; `all-the-icons-icon-for-mode' -;; `all-the-icons-icon-for-url' - -;; Which can be used to get a formatted icon for the current buffer, a -;; file name, a major mode, or an URL respectively. e.g. - -;; (insert (all-the-icons-icon-for-file "foo.js")) - -;; Inserts a JavaScript icon formatted like this - -;; #("some-icon" 0 1 (display (raise -0.24) -;; face (:family "dev-icons" :height 1.08 :foreground "#FFD446"))) - -;; You can also insert icons directly using the individual icon family -;; functions - -;; `all-the-icons-alltheicon' // Custom font with fewest icons -;; `all-the-icons-devicon' // Developer Icons -;; `all-the-icons-faicon' // Font Awesome Icons -;; `all-the-icons-fileicon' // File Icons from the Atom File Icons package -;; `all-the-icons-octicon' // GitHub Octicons -;; `all-the-icons-material' // Material Design Icons -;; `all-the-icons-wicon' // Weather Icons - -;; You can call these functions with the icon name you want to insert, e.g. - -;; (all-the-icons-octicon "file-binary") // GitHub Octicon for Binary File -;; (all-the-icons-faicon "cogs") // FontAwesome icon for cogs -;; (all-the-icons-wicon "tornado") // Weather Icon for tornado - -;; A list of all the icon names for the different font families can be -;; found in the data directory, or by inspecting the alist variables. -;; All the alist variables are prefixed with `all-the-icons-data/' - -;;; Code: -(require 'cl-lib) - -(require 'data-alltheicons "./data/data-alltheicons.el") -(require 'data-faicons "./data/data-faicons.el") -(require 'data-fileicons "./data/data-fileicons.el") -(require 'data-octicons "./data/data-octicons.el") -(require 'data-weathericons "./data/data-weathericons.el") -(require 'data-material "./data/data-material.el") - -(require 'all-the-icons-faces) - -(defvar web-mode-content-type) ;silence byte-compiler warning -;;; Custom Variables -(defgroup all-the-icons nil - "Manage how All The Icons formats icons." - :prefix "all-the-icons-" - :group 'appearance - :group 'convenience) - -(defcustom all-the-icons-color-icons t - "Whether or not to include a foreground colour when formatting the icon." - :group 'all-the-icons - :type 'boolean) - -(defcustom all-the-icons-scale-factor 1.2 - "The base Scale Factor for the `height' face property of an icon." - :group 'all-the-icons - :type 'number) - -(defcustom all-the-icons-default-adjust -0.2 - "The default adjustment to be made to the `raise' display property of an icon." - :group 'all-the-icons - :type 'number) - -(defvar all-the-icons-font-families '() "List of defined icon font families.") -(defvar all-the-icons-font-names '() "List of defined font file names this package was built with.") - -(defvar all-the-icons-extension-icon-alist - '( - ("fish" all-the-icons-alltheicon "terminal" :face all-the-icons-lpink) - ("zsh" all-the-icons-alltheicon "terminal" :face all-the-icons-lcyan) - ("sh" all-the-icons-alltheicon "terminal" :face all-the-icons-purple) - ;; Meta - ("tags" all-the-icons-octicon "tag" :height 1.0 :v-adjust 0.0 :face all-the-icons-blue) - ("log" all-the-icons-octicon "bug" :height 1.0 :v-adjust 0.0 :face all-the-icons-maroon) - ;; Config - ("node" all-the-icons-alltheicon "nodejs" :height 1.0 :face all-the-icons-green) - ("babelrc" all-the-icons-fileicon "babel" :face all-the-icons-yellow) - ("bashrc" all-the-icons-alltheicon "script" :height 0.9 :face all-the-icons-dpink) - ("bowerrc" all-the-icons-alltheicon "bower" :height 1.0 :v-adjust 0.0 :face all-the-icons-silver) - ("ini" all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-yellow) - ("eslintignore" all-the-icons-fileicon "eslint" :height 0.9 :face all-the-icons-purple) - ("eslint" all-the-icons-fileicon "eslint" :height 0.9 :face all-the-icons-lpurple) - ("git" all-the-icons-alltheicon "git" :height 1.0 :face all-the-icons-lred) - ("mk" all-the-icons-fileicon "gnu" :face all-the-icons-dorange) - ("cmake" all-the-icons-fileicon "cmake" :face all-the-icons-red) - ("dockerignore" all-the-icons-fileicon "dockerfile" :height 1.2 :face all-the-icons-dblue) - ("xml" all-the-icons-faicon "file-code-o" :height 0.95 :face all-the-icons-lorange) - ("json" all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-yellow) - ("cson" all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-yellow) - ("yml" all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-dyellow) - ("yaml" all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-dyellow) - ;; ? - ("pkg" all-the-icons-octicon "package" :v-adjust 0.0 :face all-the-icons-dsilver) - ("rpm" all-the-icons-octicon "package" :v-adjust 0.0 :face all-the-icons-dsilver) - ("elc" all-the-icons-octicon "file-binary" :v-adjust 0.0 :face all-the-icons-dsilver) - ("gz" all-the-icons-octicon "file-binary" :v-adjust 0.0 :face all-the-icons-lmaroon) - ("zip" all-the-icons-octicon "file-zip" :v-adjust 0.0 :face all-the-icons-lmaroon) - ("7z" all-the-icons-octicon "file-zip" :v-adjust 0.0 :face all-the-icons-lmaroon) - ("dat" all-the-icons-faicon "bar-chart" :face all-the-icons-cyan :height 0.9) - ("dmg" all-the-icons-octicon "tools" :v-adjust 0.0 :face all-the-icons-lsilver) - ("dll" all-the-icons-faicon "cogs" :face all-the-icons-silver) - ("ds_store" all-the-icons-faicon "cogs" :face all-the-icons-silver) - ;; Source Codes - ("scpt" all-the-icons-fileicon "apple" :face all-the-icons-pink) - ("aup" all-the-icons-fileicon "audacity" :face all-the-icons-yellow) - ("elm" all-the-icons-fileicon "elm" :face all-the-icons-blue) - ("erl" all-the-icons-alltheicon "erlang" :face all-the-icons-red :v-adjust -0.1 :height 0.9) - ("hrl" all-the-icons-alltheicon "erlang" :face all-the-icons-dred :v-adjust -0.1 :height 0.9) - ("eex" all-the-icons-alltheicon "elixir" :face all-the-icons-lorange :v-adjust -0.1 :height 0.9) - ("leex" all-the-icons-alltheicon "elixir" :face all-the-icons-lorange :v-adjust -0.1 :height 0.9) - ("ex" all-the-icons-alltheicon "elixir" :face all-the-icons-lpurple :v-adjust -0.1 :height 0.9) - ("exs" all-the-icons-alltheicon "elixir" :face all-the-icons-lred :v-adjust -0.1 :height 0.9) - ("java" all-the-icons-alltheicon "java" :height 1.0 :face all-the-icons-purple) - ("go" all-the-icons-alltheicon "go" :height 1.0 :face all-the-icons-blue) - ("jl" all-the-icons-fileicon "julia" :face all-the-icons-purple :v-adjust 0.0) - ("matlab" all-the-icons-fileicon "matlab" :face all-the-icons-orange) - ("nix" all-the-icons-fileicon "nix" :face all-the-icons-blue) - ("pl" all-the-icons-alltheicon "perl" :face all-the-icons-lorange) - ("pm" all-the-icons-alltheicon "perl" :face all-the-icons-lorange) - ("pl6" all-the-icons-fileicon "perl6" :face all-the-icons-cyan) - ("pm6" all-the-icons-fileicon "perl6" :face all-the-icons-pink) - ("pod" all-the-icons-alltheicon "perldocs" :height 1.2 :face all-the-icons-lgreen) - ("php" all-the-icons-fileicon "php" :face all-the-icons-lsilver) - ("pony" all-the-icons-fileicon "pony" :face all-the-icons-maroon) - ("ps1" all-the-icons-fileicon "powershell" :face all-the-icons-blue) - ("pro" all-the-icons-alltheicon "prolog" :height 1.1 :face all-the-icons-lmaroon) - ("proog" all-the-icons-alltheicon "prolog" :height 1.1 :face all-the-icons-lmaroon) - ("py" all-the-icons-alltheicon "python" :height 1.0 :face all-the-icons-dblue) - ("ipynb" all-the-icons-fileicon "jupyter" :height 1.0 :face all-the-icons-dorange) - ("rkt" all-the-icons-fileicon "racket" :height 1.2 :face all-the-icons-red) - ("gem" all-the-icons-alltheicon "ruby-alt" :face all-the-icons-red) - ("rb" all-the-icons-octicon "ruby" :v-adjust 0.0 :face all-the-icons-lred) - ("rs" all-the-icons-alltheicon "rust" :height 1.2 :face all-the-icons-maroon) - ("rlib" all-the-icons-alltheicon "rust" :height 1.2 :face all-the-icons-dmaroon) - ("r" all-the-icons-fileicon "R" :face all-the-icons-lblue) - ("rd" all-the-icons-fileicon "R" :face all-the-icons-lblue) - ("rdx" all-the-icons-fileicon "R" :face all-the-icons-lblue) - ("rs" all-the-icons-fileicon "R" :face all-the-icons-lblue) - ("rsx" all-the-icons-fileicon "R" :face all-the-icons-lblue) - ;; There seems to be a a bug with this font icon which does not - ;; let you propertise it without it reverting to being a lower - ;; case phi - ("c" all-the-icons-alltheicon "c-line" :face all-the-icons-blue) - ("h" all-the-icons-alltheicon "c-line" :face all-the-icons-purple) - ("m" all-the-icons-fileicon "apple" :v-adjust 0.0 :height 1.0) - ("mm" all-the-icons-fileicon "apple" :v-adjust 0.0 :height 1.0) - ;; - ("cc" all-the-icons-alltheicon "cplusplus-line" :v-adjust -0.2 :face all-the-icons-blue) - ("cpp" all-the-icons-alltheicon "cplusplus-line" :v-adjust -0.2 :face all-the-icons-blue) - ("cxx" all-the-icons-alltheicon "cplusplus-line" :v-adjust -0.2 :face all-the-icons-blue) - ("hh" all-the-icons-alltheicon "cplusplus-line" :v-adjust -0.2 :face all-the-icons-purple) - ("hpp" all-the-icons-alltheicon "cplusplus-line" :v-adjust -0.2 :face all-the-icons-purple) - ("hxx" all-the-icons-alltheicon "cplusplus-line" :v-adjust -0.2 :face all-the-icons-purple) - ;; Lisps - ("cl" all-the-icons-fileicon "clisp" :face all-the-icons-lorange) - ("l" all-the-icons-fileicon "lisp" :face all-the-icons-orange) - ("lisp" all-the-icons-fileicon "lisp" :face all-the-icons-orange) - ("el" all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.2 :face all-the-icons-purple) - ("clj" all-the-icons-alltheicon "clojure-line" :height 1.0 :face all-the-icons-blue :v-adjust 0.0) - ("cljc" all-the-icons-alltheicon "clojure-line" :height 1.0 :face all-the-icons-blue :v-adjust 0.0) - ("cljs" all-the-icons-fileicon "cljs" :height 1.0 :face all-the-icons-dblue :v-adjust 0.0) - ("coffee" all-the-icons-alltheicon "coffeescript" :height 1.0 :face all-the-icons-maroon) - ("iced" all-the-icons-alltheicon "coffeescript" :height 1.0 :face all-the-icons-lmaroon) - ("dart" all-the-icons-fileicon "dart" :height 1.0 :face all-the-icons-blue :v-adjust 0.0) - ;; Stylesheeting - ("css" all-the-icons-alltheicon "css3" :face all-the-icons-yellow) - ("scss" all-the-icons-alltheicon "sass" :face all-the-icons-pink) - ("sass" all-the-icons-alltheicon "sass" :face all-the-icons-dpink) - ("less" all-the-icons-alltheicon "less" :height 0.8 :face all-the-icons-dyellow) - ("postcss" all-the-icons-fileicon "postcss" :face all-the-icons-dred) - ("sss" all-the-icons-fileicon "postcss" :face all-the-icons-dred) - ("styl" all-the-icons-alltheicon "stylus" :face all-the-icons-lgreen) - ("csv" all-the-icons-octicon "graph" :v-adjust 0.0 :face all-the-icons-dblue) - ;; haskell - ("hs" all-the-icons-alltheicon "haskell" :height 1.0 :face all-the-icons-red) - ("chs" all-the-icons-alltheicon "haskell" :height 1.0 :face all-the-icons-red) - ("lhs" all-the-icons-alltheicon "haskell" :height 1.0 :face all-the-icons-red) - ("hsc" all-the-icons-alltheicon "haskell" :height 1.0 :face all-the-icons-red) - ;; Web modes - ("inky-haml" all-the-icons-fileicon "haml" :face all-the-icons-lyellow) - ("haml" all-the-icons-fileicon "haml" :face all-the-icons-lyellow) - ("htm" all-the-icons-alltheicon "html5" :face all-the-icons-orange) - ("html" all-the-icons-alltheicon "html5" :face all-the-icons-orange) - ("inky-er" all-the-icons-alltheicon "html5" :face all-the-icons-lred) - ("inky-erb" all-the-icons-alltheicon "html5" :face all-the-icons-lred) - ("erb" all-the-icons-alltheicon "html5" :face all-the-icons-lred) - ("hbs" all-the-icons-fileicon "moustache" :face all-the-icons-green) - ("inky-slim" all-the-icons-octicon "dashboard" :v-adjust 0.0 :face all-the-icons-yellow) - ("slim" all-the-icons-octicon "dashboard" :v-adjust 0.0 :face all-the-icons-yellow) - ("jade" all-the-icons-fileicon "jade" :face all-the-icons-red) - ("pug" all-the-icons-fileicon "pug-alt" :face all-the-icons-red) - ;; Javascript - ("d3js" all-the-icons-alltheicon "d3" :height 0.8 :face all-the-icons-lgreen) - ("re" all-the-icons-fileicon "reason" :height 1.0 :face all-the-icons-red-alt) - ("rei" all-the-icons-fileicon "reason" :height 1.0 :face all-the-icons-dred) - ("ml" all-the-icons-fileicon "ocaml" :height 1.0 :face all-the-icons-lpink) - ("mli" all-the-icons-fileicon "ocaml" :height 1.0 :face all-the-icons-dpink) - ("react" all-the-icons-alltheicon "react" :height 1.1 :face all-the-icons-lblue) - ("ts" all-the-icons-fileicon "typescript" :height 1.0 :v-adjust -0.1 :face all-the-icons-blue-alt) - ("js" all-the-icons-alltheicon "javascript" :height 1.0 :v-adjust 0.0 :face all-the-icons-yellow) - ("es" all-the-icons-alltheicon "javascript" :height 1.0 :v-adjust 0.0 :face all-the-icons-yellow) - ("jsx" all-the-icons-fileicon "jsx-2" :height 1.0 :v-adjust -0.1 :face all-the-icons-cyan-alt) - ("njs" all-the-icons-alltheicon "nodejs" :height 1.2 :face all-the-icons-lgreen) - ("vue" all-the-icons-fileicon "vue" :face all-the-icons-lgreen) - - ("sbt" all-the-icons-fileicon "sbt" :face all-the-icons-red) - ("scala" all-the-icons-alltheicon "scala" :face all-the-icons-red) - ("scm" all-the-icons-fileicon "scheme" :height 1.2 :face all-the-icons-red) - ("swift" all-the-icons-alltheicon "swift" :height 1.0 :v-adjust -0.1 :face all-the-icons-green) - - ("tcl" all-the-icons-fileicon "tcl" :height 1.0 :face all-the-icons-dred) - - ("tf" all-the-icons-fileicon "terraform" :height 1.0 :face all-the-icons-purple-alt) - ("tfvars" all-the-icons-fileicon "terraform" :height 1.0 :face all-the-icons-purple-alt) - ("tfstate" all-the-icons-fileicon "terraform" :height 1.0 :face all-the-icons-purple-alt) - - ("asm" all-the-icons-fileicon "assembly" :height 1.0 :face all-the-icons-blue) - ;; Verilog(-AMS) and SystemVerilog(-AMS) - ("v" all-the-icons-fileicon "verilog" :height 1.0 :v-adjust -0.2 :face all-the-icons-red) - ("vams" all-the-icons-fileicon "verilog" :height 1.0 :v-adjust -0.2 :face all-the-icons-red) - ("sv" all-the-icons-fileicon "verilog" :height 1.0 :v-adjust -0.2 :face all-the-icons-red) - ("sva" all-the-icons-fileicon "verilog" :height 1.0 :v-adjust -0.2 :face all-the-icons-red) - ("svh" all-the-icons-fileicon "verilog" :height 1.0 :v-adjust -0.2 :face all-the-icons-red) - ("svams" all-the-icons-fileicon "verilog" :height 1.0 :v-adjust -0.2 :face all-the-icons-red) - ;; VHDL(-AMS) - ("vhd" all-the-icons-fileicon "vhdl" :face all-the-icons-blue) - ("vhdl" all-the-icons-fileicon "vhdl" :face all-the-icons-blue) - ("vhms" all-the-icons-fileicon "vhdl" :face all-the-icons-blue) - ;; Cabal - ("cabal" all-the-icons-fileicon "cabal" :face all-the-icons-lblue) - ;; Kotlin - ("kt" all-the-icons-fileicon "kotlin" :face all-the-icons-orange) - ("kts" all-the-icons-fileicon "kotlin" :face all-the-icons-orange) - ;; Nimrod - ("nim" all-the-icons-fileicon "nimrod" :face all-the-icons-yellow) - ("nims" all-the-icons-fileicon "nimrod" :face all-the-icons-yellow) - ;; SQL - ("sql" all-the-icons-octicon "database" :face all-the-icons-silver) - ;; Styles - ("styles" all-the-icons-material "style" :face all-the-icons-red) - ;; Lua - ("lua" all-the-icons-fileicon "lua" :face all-the-icons-dblue) - ;; ASCII doc - ("adoc" all-the-icons-fileicon "asciidoc" :face all-the-icons-lblue) - ("asciidoc" all-the-icons-fileicon "asciidoc" :face all-the-icons-lblue) - ;; Puppet - ("pp" all-the-icons-fileicon "puppet" :face all-the-icons-yellow) - ;; Jinja - ("j2" all-the-icons-fileicon "jinja" :face all-the-icons-silver) - ("jinja2" all-the-icons-fileicon "jinja" :face all-the-icons-silver) - ;; Docker - ("dockerfile" all-the-icons-fileicon "dockerfile" :face all-the-icons-cyan) - ;; Vagrant - ("vagrantfile" all-the-icons-fileicon "vagrant" :face all-the-icons-blue) - ;; GLSL - ("glsl" all-the-icons-fileicon "vertex-shader" :face all-the-icons-blue) - ("vert" all-the-icons-fileicon "vertex-shader" :face all-the-icons-blue) - ("tesc" all-the-icons-fileicon "vertex-shader" :face all-the-icons-purple) - ("tese" all-the-icons-fileicon "vertex-shader" :face all-the-icons-dpurple) - ("geom" all-the-icons-fileicon "vertex-shader" :face all-the-icons-green) - ("frag" all-the-icons-fileicon "vertex-shader" :face all-the-icons-red) - ("comp" all-the-icons-fileicon "vertex-shader" :face all-the-icons-dblue) - ;; CUDA - ("cu" all-the-icons-fileicon "nvidia" :face all-the-icons-green) - ("cuh" all-the-icons-fileicon "nvidia" :face all-the-icons-green) - ;; C# - ("cs" all-the-icons-alltheicon "csharp-line" :face all-the-icons-dblue) - ("csx" all-the-icons-alltheicon "csharp-line" :face all-the-icons-dblue) - ;; F# - ("fs" all-the-icons-fileicon "fsharp" :face all-the-icons-blue-alt) - ("fsi" all-the-icons-fileicon "fsharp" :face all-the-icons-blue-alt) - ("fsx" all-the-icons-fileicon "fsharp" :face all-the-icons-blue-alt) - ("fsscript" all-the-icons-fileicon "fsharp" :face all-the-icons-blue-alt) - ;; zig - ("zig" all-the-icons-fileicon "zig" :face all-the-icons-orange) - ;; File Types - ("ico" all-the-icons-octicon "file-media" :v-adjust 0.0 :face all-the-icons-blue) - ("png" all-the-icons-octicon "file-media" :v-adjust 0.0 :face all-the-icons-orange) - ("gif" all-the-icons-octicon "file-media" :v-adjust 0.0 :face all-the-icons-green) - ("jpeg" all-the-icons-octicon "file-media" :v-adjust 0.0 :face all-the-icons-dblue) - ("jpg" all-the-icons-octicon "file-media" :v-adjust 0.0 :face all-the-icons-dblue) - ;; Audio - ("mp3" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("wav" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("m4a" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("ogg" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("flac" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("opus" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("au" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("aif" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("aifc" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("aiff" all-the-icons-faicon "volume-up" :face all-the-icons-dred) - ("svg" all-the-icons-alltheicon "svg" :height 0.9 :face all-the-icons-lgreen) - ;; Video - ("mov" all-the-icons-faicon "film" :face all-the-icons-blue) - ("mp4" all-the-icons-faicon "film" :face all-the-icons-blue) - ("ogv" all-the-icons-faicon "film" :face all-the-icons-dblue) - ("mpg" all-the-icons-faicon "film" :face all-the-icons-blue) - ("mpeg" all-the-icons-faicon "film" :face all-the-icons-blue) - ("flv" all-the-icons-faicon "film" :face all-the-icons-blue) - ("ogv" all-the-icons-faicon "film" :face all-the-icons-dblue) - ("mkv" all-the-icons-faicon "film" :face all-the-icons-blue) - ("webm" all-the-icons-faicon "film" :face all-the-icons-blue) - ;; Fonts - ("ttf" all-the-icons-fileicon "font" :v-adjust 0.0 :face all-the-icons-dcyan) - ("woff" all-the-icons-fileicon "font" :v-adjust 0.0 :face all-the-icons-cyan) - ("woff2" all-the-icons-fileicon "font" :v-adjust 0.0 :face all-the-icons-cyan) - ;; Doc - ("pdf" all-the-icons-octicon "file-pdf" :v-adjust 0.0 :face all-the-icons-dred) - ("text" all-the-icons-octicon "file-text" :v-adjust 0.0 :face all-the-icons-cyan) - ("txt" all-the-icons-octicon "file-text" :v-adjust 0.0 :face all-the-icons-cyan) - ("doc" all-the-icons-fileicon "word" :face all-the-icons-blue) - ("docx" all-the-icons-fileicon "word" :face all-the-icons-blue) - ("docm" all-the-icons-fileicon "word" :face all-the-icons-blue) - ("texi" all-the-icons-fileicon "tex" :face all-the-icons-lred) - ("tex" all-the-icons-fileicon "tex" :face all-the-icons-lred) - ("md" all-the-icons-octicon "markdown" :v-adjust 0.0 :face all-the-icons-lblue) - ("bib" all-the-icons-fileicon "bib" :face all-the-icons-maroon) - ("org" all-the-icons-fileicon "org" :face all-the-icons-lgreen) - ("pps" all-the-icons-fileicon "powerpoint" :face all-the-icons-orange) - ("ppt" all-the-icons-fileicon "powerpoint" :face all-the-icons-orange) - ("pptsx" all-the-icons-fileicon "powerpoint" :face all-the-icons-orange) - ("ppttx" all-the-icons-fileicon "powerpoint" :face all-the-icons-orange) - ("knt" all-the-icons-fileicon "powerpoint" :face all-the-icons-cyan) - ("xlsx" all-the-icons-fileicon "excel" :face all-the-icons-dgreen) - ("xlsm" all-the-icons-fileicon "excel" :face all-the-icons-dgreen) - ("xlsb" all-the-icons-fileicon "excel" :face all-the-icons-dgreen) - ("xltx" all-the-icons-fileicon "excel" :face all-the-icons-dgreen) - ("xltm" all-the-icons-fileicon "excel" :face all-the-icons-dgreen) - ;; - ("key" all-the-icons-octicon "key" :v-adjust 0.0 :face all-the-icons-lblue) - ("pem" all-the-icons-octicon "key" :v-adjust 0.0 :face all-the-icons-orange) - ("p12" all-the-icons-octicon "key" :v-adjust 0.0 :face all-the-icons-dorange) - ("crt" all-the-icons-octicon "key" :v-adjust 0.0 :face all-the-icons-lblue) - ("pub" all-the-icons-octicon "key" :v-adjust 0.0 :face all-the-icons-blue) - ("gpg" all-the-icons-octicon "key" :v-adjust 0.0 :face all-the-icons-lblue) - ("cache" all-the-icons-octicon "database" :height 1.0 :v-adjust 0.0 :face all-the-icons-green))) - - -(define-obsolete-variable-alias 'all-the-icons-icon-alist - 'all-the-icons-regexp-icon-alist - "5.0.0" - "`all-the-icons-icon-alist' has been split to -`all-the-icons-extension-icon-alist' and `all-the-icons-regexp-icon-alist' -for performance sake.") - -(defvar all-the-icons-regexp-icon-alist - '( - ;; - ("^TAGS$" all-the-icons-octicon "tag" :height 1.0 :v-adjust 0.0 :face all-the-icons-blue) - ("^TODO$" all-the-icons-octicon "checklist" :v-adjust 0.0 :face all-the-icons-lyellow) - ("^LICENSE$" all-the-icons-octicon "book" :height 1.0 :v-adjust 0.0 :face all-the-icons-blue) - ("^readme" all-the-icons-octicon "book" :height 1.0 :v-adjust 0.0 :face all-the-icons-lcyan) - - ;; Config - ("^bower.json$" all-the-icons-alltheicon "bower" :height 1.0 :v-adjust 0.0 :face all-the-icons-lorange) - ("nginx" all-the-icons-fileicon "nginx" :height 0.9 :face all-the-icons-dgreen) - ("apache" all-the-icons-alltheicon "apache" :height 0.9 :face all-the-icons-dgreen) - ("^Makefile$" all-the-icons-fileicon "gnu" :face all-the-icons-dorange) - ("^CMakeLists.txt$" all-the-icons-fileicon "cmake" :face all-the-icons-red) - ("^CMakeCache.txt$" all-the-icons-fileicon "cmake" :face all-the-icons-blue) - - ("^\\.?Dockerfile" all-the-icons-fileicon "dockerfile" :face all-the-icons-blue) - ("^Brewfile$" all-the-icons-faicon "beer" :face all-the-icons-lsilver) - ("\\.npmignore$" all-the-icons-fileicon "npm" :face all-the-icons-dred) - ("^package.json$" all-the-icons-fileicon "npm" :face all-the-icons-red) - ("^package.lock.json$" all-the-icons-fileicon "npm" :face all-the-icons-dred) - ("^yarn\\.lock" all-the-icons-fileicon "yarn" :face all-the-icons-blue-alt) - - ;; ;; AWS - ("^stack.*.json$" all-the-icons-alltheicon "aws" :face all-the-icons-orange) - - - ("^serverless\\.yml$" all-the-icons-faicon "bolt" :v-adjust 0.0 :face all-the-icons-yellow) - - ;; lock files - ("~$" all-the-icons-octicon "lock" :v-adjust 0.0 :face all-the-icons-maroon) - - ;; Source Codes - ("^mix.lock$" all-the-icons-alltheicon "elixir" :face all-the-icons-lyellow :v-adjust -0.1 :height 0.9) - - ("^Gemfile\\(\\.lock\\)?$" all-the-icons-alltheicon "ruby-alt" :face all-the-icons-red) - ("_?test\\.rb$" all-the-icons-fileicon "test-ruby" :height 1.0 :v-adjust 0.0 :face all-the-icons-red) - ("_?test_helper\\.rb$" all-the-icons-fileicon "test-ruby" :height 1.0 :v-adjust 0.0 :face all-the-icons-dred) - ("_?spec\\.rb$" all-the-icons-fileicon "test-ruby" :height 1.0 :v-adjust 0.0 :face all-the-icons-red) - ("_?spec_helper\\.rb$" all-the-icons-fileicon "test-ruby" :height 1.0 :v-adjust 0.0 :face all-the-icons-dred) - - ("-?spec\\.ts$" all-the-icons-fileicon "test-typescript" :height 1.0 :v-adjust 0.0 :face all-the-icons-blue) - ("-?test\\.ts$" all-the-icons-fileicon "test-typescript" :height 1.0 :v-adjust 0.0 :face all-the-icons-blue) - ("-?spec\\.js$" all-the-icons-fileicon "test-js" :height 1.0 :v-adjust 0.0 :face all-the-icons-lpurple) - ("-?test\\.js$" all-the-icons-fileicon "test-js" :height 1.0 :v-adjust 0.0 :face all-the-icons-lpurple) - ("-?spec\\.jsx$" all-the-icons-fileicon "test-react" :height 1.0 :v-adjust 0.0 :face all-the-icons-blue-alt) - ("-?test\\.jsx$" all-the-icons-fileicon "test-react" :height 1.0 :v-adjust 0.0 :face all-the-icons-blue-alt) - - ;; Git - ("^MERGE_" all-the-icons-octicon "git-merge" :v-adjust 0.0 :face all-the-icons-red) - ("^COMMIT_EDITMSG" all-the-icons-octicon "git-commit" :v-adjust 0.0 :face all-the-icons-red) - - ;; Stylesheeting - ("stylelint" all-the-icons-fileicon "stylelint" :face all-the-icons-lyellow) - ;; JavaScript - ("^gulpfile" all-the-icons-alltheicon "gulp" :height 1.0 :face all-the-icons-lred) - ("^gruntfile" all-the-icons-alltheicon "grunt" :height 1.0 :v-adjust -0.1 :face all-the-icons-lyellow) - ("^webpack" all-the-icons-fileicon "webpack" :face all-the-icons-lblue) - - ("bookmark" all-the-icons-octicon "bookmark" :height 1.1 :v-adjust 0.0 :face all-the-icons-lpink) - - ("^\\*scratch\\*$" all-the-icons-faicon "sticky-note" :face all-the-icons-lyellow) - ("^\\*scratch.*" all-the-icons-faicon "sticky-note" :face all-the-icons-yellow) - ("^\\*new-tab\\*$" all-the-icons-material "star" :face all-the-icons-cyan) - - ("^\\." all-the-icons-octicon "gear" :v-adjust 0.0) - (".?" all-the-icons-faicon "file-o" :v-adjust 0.0 :face all-the-icons-dsilver))) - -(defvar all-the-icons-dir-icon-alist - '( - ("trash" all-the-icons-faicon "trash-o" :height 1.2 :v-adjust -0.1) - ("dropbox" all-the-icons-faicon "dropbox" :height 1.0 :v-adjust -0.1) - ("google[ _-]drive" all-the-icons-alltheicon "google-drive" :height 1.0 :v-adjust -0.1) - ("^atom$" all-the-icons-alltheicon "atom" :height 1.2 :v-adjust -0.1) - ("documents" all-the-icons-faicon "book" :height 1.0 :v-adjust -0.1) - ("download" all-the-icons-faicon "cloud-download" :height 0.9 :v-adjust -0.1) - ("desktop" all-the-icons-octicon "device-desktop" :height 1.0 :v-adjust -0.1) - ("pictures" all-the-icons-faicon "picture-o" :height 0.9 :v-adjust -0.2) - ("photos" all-the-icons-faicon "camera-retro" :height 1.0 :v-adjust -0.1) - ("music" all-the-icons-faicon "music" :height 1.0 :v-adjust -0.1) - ("movies" all-the-icons-faicon "film" :height 0.9 :v-adjust -0.1) - ("code" all-the-icons-octicon "code" :height 1.1 :v-adjust -0.1) - ("workspace" all-the-icons-octicon "code" :height 1.1 :v-adjust -0.1) - ("test" all-the-icons-fileicon "test-dir" :height 0.9) - ("\\.git" all-the-icons-alltheicon "git" :height 1.0) - (".?" all-the-icons-octicon "file-directory" :height 1.0 :v-adjust -0.1) - )) - -(defvar all-the-icons-weather-icon-alist - '( - ("tornado" all-the-icons-wicon "tornado") - ("hurricane" all-the-icons-wicon "hurricane") - ("thunderstorms" all-the-icons-wicon "thunderstorm") - ("sunny" all-the-icons-wicon "day-sunny") - ("rain.*snow" all-the-icons-wicon "rain-mix") - ("rain.*hail" all-the-icons-wicon "rain-mix") - ("sleet" all-the-icons-wicon "sleet") - ("hail" all-the-icons-wicon "hail") - ("drizzle" all-the-icons-wicon "sprinkle") - ("rain" all-the-icons-wicon "showers" :height 1.1 :v-adjust 0.0) - ("showers" all-the-icons-wicon "showers") - ("blowing.*snow" all-the-icons-wicon "snow-wind") - ("snow" all-the-icons-wicon "snow") - ("dust" all-the-icons-wicon "dust") - ("fog" all-the-icons-wicon "fog") - ("haze" all-the-icons-wicon "day-haze") - ("smoky" all-the-icons-wicon "smoke") - ("blustery" all-the-icons-wicon "cloudy-windy") - ("windy" all-the-icons-wicon "cloudy-gusts") - ("cold" all-the-icons-wicon "snowflake-cold") - ("partly.*cloudy.*night" all-the-icons-wicon "night-alt-partly-cloudy") - ("partly.*cloudy" all-the-icons-wicon "day-cloudy-high") - ("cloudy.*night" all-the-icons-wicon "night-alt-cloudy") - ("cxloudy.*day" all-the-icons-wicon "day-cloudy") - ("cloudy" all-the-icons-wicon "cloudy") - ("clear.*night" all-the-icons-wicon "night-clear") - ("fair.*night" all-the-icons-wicon "stars") - ("fair.*day" all-the-icons-wicon "horizon") - ("hot" all-the-icons-wicon "hot") - ("not.*available" all-the-icons-wicon "na") - )) - -(defvar all-the-icons-mode-icon-alist - '( - (emacs-lisp-mode all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.1 :face all-the-icons-purple) - (circe-server-mode all-the-icons-faicon "commenting-o" :height 1.0 :v-adjust 0.0) - (circe-channel-mode all-the-icons-faicon "commenting-o" :height 1.0 :v-adjust 0.0) - (erc-mode all-the-icons-faicon "commenting-o" :height 1.0 :v-adjust 0.0) - (inferior-emacs-lisp-mode all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.1 :face all-the-icons-lblue) - (dired-mode all-the-icons-octicon "file-directory" :v-adjust 0.0) - (lisp-interaction-mode all-the-icons-fileicon "lisp" :v-adjust -0.1 :face all-the-icons-orange) - (sly-mrepl-mode all-the-icons-fileicon "clisp" :v-adjust -0.1 :face all-the-icons-orange) - (slime-repl-mode all-the-icons-fileicon "clisp" :v-adjust -0.1 :face all-the-icons-orange) - (org-mode all-the-icons-fileicon "org" :v-adjust 0.0 :face all-the-icons-lgreen) - (typescript-mode all-the-icons-fileicon "typescript" :v-adjust -0.1 :face all-the-icons-blue-alt) - (js-mode all-the-icons-alltheicon "javascript" :v-adjust -0.1 :face all-the-icons-yellow) - (js-jsx-mode all-the-icons-alltheicon "javascript" :v-adjust -0.1 :face all-the-icons-yellow) - (js2-mode all-the-icons-alltheicon "javascript" :v-adjust -0.1 :face all-the-icons-yellow) - (js3-mode all-the-icons-alltheicon "javascript" :v-adjust -0.1 :face all-the-icons-yellow) - (rjsx-mode all-the-icons-fileicon "jsx-2" :v-adjust -0.1 :face all-the-icons-cyan-alt) - (term-mode all-the-icons-octicon "terminal" :v-adjust 0.2) - (vterm-mode all-the-icons-octicon "terminal" :v-adjust 0.2) - (eshell-mode all-the-icons-octicon "terminal" :v-adjust 0.0 :face all-the-icons-purple) - (magit-refs-mode all-the-icons-octicon "git-branch" :v-adjust 0.0 :face all-the-icons-red) - (magit-process-mode all-the-icons-octicon "mark-github" :v-adjust 0.0) - (magit-diff-mode all-the-icons-octicon "git-compare" :v-adjust 0.0 :face all-the-icons-lblue) - (ediff-mode all-the-icons-octicon "git-compare" :v-adjust 0.0 :Face all-the-icons-red) - (comint-mode all-the-icons-faicon "terminal" :v-adjust 0.0 :face all-the-icons-lblue) - (eww-mode all-the-icons-faicon "firefox" :v-adjust -0.1 :face all-the-icons-red) - (org-agenda-mode all-the-icons-octicon "checklist" :v-adjust 0.0 :face all-the-icons-lgreen) - (cfw:calendar-mode all-the-icons-octicon "calendar" :v-adjust 0.0) - (ibuffer-mode all-the-icons-faicon "files-o" :v-adjust 0.0 :face all-the-icons-dsilver) - (messages-buffer-mode all-the-icons-faicon "file-o" :v-adjust 0.0 :face all-the-icons-dsilver) - (help-mode all-the-icons-faicon "info" :v-adjust -0.1 :face all-the-icons-purple) - (benchmark-init/tree-mode all-the-icons-octicon "dashboard" :v-adjust 0.0) - (jenkins-mode all-the-icons-fileicon "jenkins" :face all-the-icons-blue) - (magit-popup-mode all-the-icons-alltheicon "git" :face all-the-icons-red) - (magit-status-mode all-the-icons-alltheicon "git" :face all-the-icons-lred) - (magit-log-mode all-the-icons-alltheicon "git" :face all-the-icons-green) - (mu4e-compose-mode all-the-icons-octicon "pencil" :v-adjust 0.0) - (mu4e-headers-mode all-the-icons-octicon "mail" :v-adjust 0.0) - (mu4e-main-mode all-the-icons-octicon "mail" :v-adjust 0.0) - (mu4e-view-mode all-the-icons-octicon "mail-read" :v-adjust 0.0) - (package-menu-mode all-the-icons-faicon "archive" :height 1.0 :v-adjust 0.0 :face all-the-icons-silver) - (paradox-menu-mode all-the-icons-faicon "archive" :height 1.0 :v-adjust 0.0 :face all-the-icons-silver) - (Custom-mode all-the-icons-octicon "settings" :v-adjust -0.1) - - ;; Special matcher for Web Mode based on the `web-mode-content-type' of the current buffer - (web-mode all-the-icons--web-mode-icon) - - (fundamental-mode all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.1 :face all-the-icons-dsilver) - (special-mode all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.1 :face all-the-icons-yellow) - (text-mode all-the-icons-octicon "file-text" :v-adjust 0.0 :face all-the-icons-cyan) - (enh-ruby-mode all-the-icons-alltheicon "ruby-alt" :face all-the-icons-lred) - (ruby-mode all-the-icons-alltheicon "ruby-alt" :face all-the-icons-lred) - (inf-ruby-mode all-the-icons-alltheicon "ruby-alt" :face all-the-icons-red) - (projectile-rails-compilation-mode all-the-icons-alltheicon "ruby-alt" :face all-the-icons-red) - (rspec-compilation-mode all-the-icons-alltheicon "ruby-alt" :face all-the-icons-red) - (rake-compilation-mode all-the-icons-alltheicon "ruby-alt" :face all-the-icons-red) - (sh-mode all-the-icons-alltheicon "terminal" :face all-the-icons-purple) - (shell-mode all-the-icons-alltheicon "terminal" :face all-the-icons-purple) - (fish-mode all-the-icons-alltheicon "terminal" :face all-the-icons-lpink) - (nginx-mode all-the-icons-fileicon "nginx" :height 0.9 :face all-the-icons-dgreen) - (apache-mode all-the-icons-alltheicon "apache" :height 0.9 :face all-the-icons-dgreen) - (makefile-mode all-the-icons-fileicon "gnu" :face all-the-icons-dorange) - (cmake-mode all-the-icons-fileicon "cmake" :face all-the-icons-red) - (dockerfile-mode all-the-icons-fileicon "dockerfile" :face all-the-icons-blue) - (docker-compose-mode all-the-icons-fileicon "dockerfile" :face all-the-icons-lblue) - (nxml-mode all-the-icons-faicon "file-code-o" :height 0.95 :face all-the-icons-lorange) - (json-mode all-the-icons-octicon "settings" :face all-the-icons-yellow) - (yaml-mode all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-dyellow) - (elisp-byte-code-mode all-the-icons-octicon "file-binary" :v-adjust 0.0 :face all-the-icons-dsilver) - (archive-mode all-the-icons-octicon "file-zip" :v-adjust 0.0 :face all-the-icons-lmaroon) - (elm-mode all-the-icons-fileicon "elm" :face all-the-icons-blue) - (erlang-mode all-the-icons-alltheicon "erlang" :face all-the-icons-red :v-adjust -0.1 :height 0.9) - (elixir-mode all-the-icons-alltheicon "elixir" :face all-the-icons-lorange :v-adjust -0.1 :height 0.9) - (java-mode all-the-icons-alltheicon "java" :height 1.0 :face all-the-icons-purple) - (go-mode all-the-icons-fileicon "go" :height 1.0 :face all-the-icons-blue) - (matlab-mode all-the-icons-fileicon "matlab" :face all-the-icons-orange) - (perl-mode all-the-icons-alltheicon "perl" :face all-the-icons-lorange) - (cperl-mode all-the-icons-alltheicon "perl" :face all-the-icons-lorange) - (php-mode all-the-icons-fileicon "php" :face all-the-icons-lsilver) - (prolog-mode all-the-icons-alltheicon "prolog" :height 1.1 :face all-the-icons-lmaroon) - (python-mode all-the-icons-alltheicon "python" :height 1.0 :face all-the-icons-dblue) - (inferior-python-mode all-the-icons-alltheicon "python" :height 1.0 :face all-the-icons-dblue) - (racket-mode all-the-icons-fileicon "racket" :height 1.2 :face all-the-icons-red) - (rust-mode all-the-icons-alltheicon "rust" :height 1.2 :face all-the-icons-maroon) - (scala-mode all-the-icons-alltheicon "scala" :face all-the-icons-red) - (scheme-mode all-the-icons-fileicon "scheme" :height 1.2 :face all-the-icons-red) - (swift-mode all-the-icons-alltheicon "swift" :height 1.0 :v-adjust -0.1 :face all-the-icons-green) - (c-mode all-the-icons-alltheicon "c-line" :face all-the-icons-blue) - (c++-mode all-the-icons-alltheicon "cplusplus-line" :v-adjust -0.2 :face all-the-icons-blue) - (csharp-mode all-the-icons-alltheicon "csharp-line" :face all-the-icons-dblue) - (clojure-mode all-the-icons-alltheicon "clojure" :height 1.0 :face all-the-icons-blue) - (cider-repl-mode all-the-icons-alltheicon "clojure" :height 1.0 :face all-the-icons-green) - (clojurescript-mode all-the-icons-fileicon "cljs" :height 1.0 :face all-the-icons-dblue) - (coffee-mode all-the-icons-alltheicon "coffeescript" :height 1.0 :face all-the-icons-maroon) - (lisp-mode all-the-icons-fileicon "lisp" :face all-the-icons-orange) - (css-mode all-the-icons-alltheicon "css3" :face all-the-icons-yellow) - (scss-mode all-the-icons-alltheicon "sass" :face all-the-icons-pink) - (sass-mode all-the-icons-alltheicon "sass" :face all-the-icons-dpink) - (less-css-mode all-the-icons-alltheicon "less" :height 0.8 :face all-the-icons-dyellow) - (stylus-mode all-the-icons-alltheicon "stylus" :face all-the-icons-lgreen) - (csv-mode all-the-icons-octicon "graph" :v-adjust 0.0 :face all-the-icons-dblue) - (haskell-mode all-the-icons-alltheicon "haskell" :height 1.0 :face all-the-icons-red) - (haskell-c2hs-mode all-the-icons-alltheicon "haskell" :height 1.0 :face all-the-icons-red) - (literate-haskell-mode all-the-icons-alltheicon "haskell" :height 1.0 :face all-the-icons-red) - (haml-mode all-the-icons-fileicon "haml" :face all-the-icons-lyellow) - (html-mode all-the-icons-alltheicon "html5" :face all-the-icons-orange) - (rhtml-mode all-the-icons-alltheicon "html5" :face all-the-icons-lred) - (mustache-mode all-the-icons-fileicon "moustache" :face all-the-icons-green) - (slim-mode all-the-icons-octicon "dashboard" :v-adjust 0.0 :face all-the-icons-yellow) - (jade-mode all-the-icons-fileicon "jade" :face all-the-icons-red) - (pug-mode all-the-icons-fileicon "pug" :face all-the-icons-red) - (react-mode all-the-icons-alltheicon "react" :height 1.1 :face all-the-icons-lblue) - (image-mode all-the-icons-octicon "file-media" :v-adjust 0.0 :face all-the-icons-blue) - (texinfo-mode all-the-icons-fileicon "tex" :face all-the-icons-lred) - (markdown-mode all-the-icons-octicon "markdown" :v-adjust 0.0 :face all-the-icons-lblue) - (bibtex-mode all-the-icons-fileicon "bib" :face all-the-icons-maroon) - (org-mode all-the-icons-fileicon "org" :face all-the-icons-lgreen) - (compilation-mode all-the-icons-faicon "cogs" :v-adjust 0.0 :height 1.0) - (objc-mode all-the-icons-faicon "apple" :v-adjust 0.0 :height 1.0) - (tuareg-mode all-the-icons-fileicon "ocaml" :v-adjust 0.0 :height 1.0) - (purescript-mode all-the-icons-fileicon "purescript" :v-adjust 0.0 :height 1.0) - (verilog-mode all-the-icons-fileicon "verilog" :height 1.0 :v-adjust -0.2 :face all-the-icons-red) - (vhdl-mode all-the-icons-fileicon "vhdl" :face all-the-icons-blue) - (haskell-cabal-mode all-the-icons-fileicon "cabal" :face all-the-icons-lblue) - (kotlin-mode all-the-icons-fileicon "kotlin" :face all-the-icons-orange) - (nim-mode all-the-icons-fileicon "nimrod" :face all-the-icons-yellow) - (sql-mode all-the-icons-octicon "database" :face all-the-icons-silver) - (lua-mode all-the-icons-fileicon "lua" :face all-the-icons-dblue) - (adoc-mode all-the-icons-fileicon "asciidoc" :face all-the-icons-lblue) - (puppet-mode all-the-icons-fileicon "puppet" :face all-the-icons-yellow) - (jinja2-mode all-the-icons-fileicon "jinja" :face all-the-icons-silver) - (powershell-mode all-the-icons-fileicon "powershell" :face all-the-icons-blue) - (tex-mode all-the-icons-fileicon "tex" :face all-the-icons-lred) - (latex-mode all-the-icons-fileicon "tex" :face all-the-icons-lred) - (dart-mode all-the-icons-fileicon "dart" :height 1.0 :face all-the-icons-blue) - (fsharp-mode all-the-icons-fileicon "fsharp" :height 1.0 :face all-the-icons-blue) - (asm-mode all-the-icons-fileicon "assembly" :height 1.0 :face all-the-icons-blue) - (nasm-mode all-the-icons-fileicon "assembly" :height 1.0 :face all-the-icons-blue) - (tcl-mode all-the-icons-fileicon "tcl" :height 1.0 :face all-the-icons-dred) - (cuda-mode all-the-icons-fileicon "nvidia" :face all-the-icons-green) - (glsl-mode all-the-icons-fileicon "vertex-shader" :face all-the-icons-green) - (zig-mode all-the-icons-fileicon "zig" :face all-the-icons-orange))) - -(defvar all-the-icons-url-alist - '( - ;; Social media and communities - ("^\\(https?://\\)?\\(www\\.\\)?del\\.icio\\.us" all-the-icons-faicon "delicious") - ("^\\(https?://\\)?\\(www\\.\\)?behance\\.net" all-the-icons-faicon "behance") - ("^\\(https?://\\)?\\(www\\.\\)?dribbble\\.com" all-the-icons-faicon "dribbble") - ("^\\(https?://\\)?\\(www\\.\\)?facebook\\.com" all-the-icons-faicon "facebook-official") - ("^\\(https?://\\)?\\(www\\.\\)?glide\\.me" all-the-icons-faicon "glide-g") - ("^\\(https?://\\)?\\(www\\.\\)?plus\\.google\\.com" all-the-icons-faicon "google-plus") - ("linkedin\\.com" all-the-icons-faicon "linkedin") - ("^\\(https?://\\)?\\(www\\.\\)?ok\\.ru" all-the-icons-faicon "odnoklassniki") - ("^\\(https?://\\)?\\(www\\.\\)?reddit\\.com" all-the-icons-faicon "reddit-alien") - ("^\\(https?://\\)?\\(www\\.\\)?slack\\.com" all-the-icons-faicon "slack") - ("^\\(https?://\\)?\\(www\\.\\)?snapchat\\.com" all-the-icons-faicon "snapchat-ghost") - ("^\\(https?://\\)?\\(www\\.\\)?weibo\\.com" all-the-icons-faicon "weibo") - ("^\\(https?://\\)?\\(www\\.\\)?twitter\\.com" all-the-icons-faicon "twitter") - ;; Blogging - ("joomla\\.org" all-the-icons-faicon "joomla") - ("^\\(https?://\\)?\\(www\\.\\)?medium\\.com" all-the-icons-faicon "medium") - ("tumblr\\.com" all-the-icons-faicon "tumblr") - ("^wordpress\\.com" all-the-icons-faicon "wordpress") - ;; Programming - ("^\\(https?://\\)?\\(www\\.\\)?bitbucket\\.org" all-the-icons-faicon "bitbucket") - ("^\\(https?://\\)?\\(www\\.\\)?codepen\\.io" all-the-icons-faicon "codepen") - ("^\\(https?://\\)?\\(www\\.\\)?codiepie\\.com" all-the-icons-faicon "codiepie") - ("^\\(https?://\\)?\\(www\\.\\)?gist\\.github\\.com" all-the-icons-octicon "gist") - ("^\\(https?://\\)?\\(www\\.\\)?github\\.com" all-the-icons-octicon "mark-github") - ("^\\(https?://\\)?\\(www\\.\\)?gitlab\\.com" all-the-icons-faicon "gitlab") - ("^\\(https?://\\)?\\(www\\.\\)?news\\.ycombinator\\.com" all-the-icons-faicon "hacker-news") - ("^\\(https?://\\)?\\(www\\.\\)?jsfiddle\\.net" all-the-icons-faicon "jsfiddle") - ("^\\(https?://\\)?\\(www\\.\\)?maxcdn\\.com" all-the-icons-faicon "maxcdn") - ("^\\(https?://\\)?\\(www\\.\\)?stackoverflow\\.com" all-the-icons-faicon "stack-overflow") - ;; Video - ("^\\(https?://\\)?\\(www\\.\\)?twitch\\.tv" all-the-icons-faicon "twitch") - ("^\\(https?://\\)?\\(www\\.\\)?vimeo\\.com" all-the-icons-faicon "vimeo") - ("^\\(https?://\\)?\\(www\\.\\)?youtube\\.com" all-the-icons-faicon "youtube") - ("^\\(https?://\\)?\\(www\\.\\)?youtu\\.be" all-the-icons-faicon "youtube") - ("^\\(https?://\\)?\\(www\\.\\)?vine\\.co" all-the-icons-faicon "vine") - ;; Sound - ("^\\(https?://\\)?\\(www\\.\\)?last\\.fm" all-the-icons-faicon "lastfm") - ("^\\(https?://\\)?\\(www\\.\\)?mixcloud\\.com" all-the-icons-faicon "mixcloud") - ("^\\(https?://\\)?\\(www\\.\\)?soundcloud\\.com" all-the-icons-faicon "soundcloud") - ("spotify\\.com" all-the-icons-faicon "spotify") - ;; Shopping - ("^\\(https?://\\)?\\(www\\.\\)?amazon\\." all-the-icons-faicon "amazon") - ("^\\(https?://\\)?\\(www\\.\\)?opencart\\.com" all-the-icons-faicon "opencart") - ("^\\(https?://\\)?\\(www\\.\\)?paypal\\.com" all-the-icons-faicon "paypal") - ("^\\(https?://\\)?\\(www\\.\\)?shirtsinbulk\\.com" all-the-icons-faicon "shitsinbulk") - ;; Images - ("^\\(https?://\\)?\\(www\\.\\)?500px\\.com" all-the-icons-faicon "500px") - ("^\\(https?://\\)?\\(www\\.\\)?deviantart\\.com" all-the-icons-faicon "deviantart") - ("^\\(https?://\\)?\\(www\\.\\)?flickr\\.com" all-the-icons-faicon "flickr") - ("^\\(https?://\\)?\\(www\\.\\)?instagram\\.com" all-the-icons-faicon "instagram") - ("^\\(https?://\\)?\\(www\\.\\)?pinterest\\." all-the-icons-faicon "pinterest") - ;; Information and books - ("^\\(https?://\\)?\\(www\\.\\)?digg\\.com" all-the-icons-faicon "digg") - ("^\\(https?://\\)?\\(www\\.\\)?foursquare\\.com" all-the-icons-faicon "foursquare") - ("^\\(https?://\\)?\\(www\\.\\)?getpocket\\.com" all-the-icons-faicon "get-pocket") - ("^\\(https?://\\)?\\(www\\.\\)?scribd\\.com" all-the-icons-faicon "scribd") - ("^\\(https?://\\)?\\(www\\.\\)?slideshare\\.net" all-the-icons-faicon "slideshare") - ("stackexchange\\.com" all-the-icons-faicon "stack-exchange") - ("^\\(https?://\\)?\\(www\\.\\)?stumbleupon\\.com" all-the-icons-faicon "stumbleupon") - ("^\\(https?://\\)?\\(www\\.\\)?tripadvisor\\." all-the-icons-faicon "tripadvisor") - ("^\\(https?://\\)?\\(www\\.\\)?yelp\\." all-the-icons-faicon "yelp") - - ("wikipedia\\.org" all-the-icons-faicon "wikipedia-w") - ;; Various companies and tools - ("^\\(https?://\\)?\\(www\\.\\)?angel\\.co" all-the-icons-faicon "angellist") - ("^\\(https?://\\)?\\(www\\.\\)?apple\\.com" all-the-icons-faicon "apple") - ("^\\(https?://\\)?\\(www\\.\\)?buysellads\\.com" all-the-icons-faicon "buysellads") - ("^\\(https?://\\)?\\(www\\.\\)?connectdevelop\\.com" all-the-icons-faicon "connectdevelop") - ("^\\(https?://\\)?\\(www\\.\\)?dashcube\\.com" all-the-icons-faicon "dashcube") - ("^\\(https?://\\)?\\(www\\.\\)?dropbox\\.com" all-the-icons-faicon "dropbox") - ("^\\(https?://\\)?\\(www\\.\\)?enviragallery\\.com" all-the-icons-faicon "envira") - ("^\\(https?://\\)?\\(www\\.\\)?fortawesome\\.com" all-the-icons-faicon "fort-awesome") - ("^\\(https?://\\)?\\(www\\.\\)?forumbee\\.com" all-the-icons-faicon "forumbee") - ("^\\(https?://\\)?\\(www\\.\\)?gratipay\\.com" all-the-icons-faicon "gratipay") - ("^\\(https?://\\)?\\(www\\.\\)?modx\\.com" all-the-icons-faicon "modx") - ("^\\(https?://\\)?\\(www\\.\\)?pagelines\\.com" all-the-icons-faicon "pagelines") - ("^\\(https?://\\)?\\(www\\.\\)?producthunt\\.com" all-the-icons-faicon "product-hunt") - ("sellsy\\.com" all-the-icons-faicon "sellsy") - ("^\\(https?://\\)?\\(www\\.\\)?simplybuilt\\.com" all-the-icons-faicon "simplybuilt") - ("^\\(https?://\\)?\\(www\\.\\)?skyatlas\\.com" all-the-icons-faicon "skyatlas") - ("^\\(https?://\\)?\\(www\\.\\)?skype\\.com" all-the-icons-faicon "skype") - ("steampowered\\.com" all-the-icons-faicon "steam") - ("^\\(https?://\\)?\\(www\\.\\)?themeisle\\.com" all-the-icons-faicon "themeisle") - ("^\\(https?://\\)?\\(www\\.\\)?trello\\.com" all-the-icons-faicon "trello") - ("^\\(https?://\\)?\\(www\\.\\)?whatsapp\\.com" all-the-icons-faicon "whatsapp") - ("^\\(https?://\\)?\\(www\\.\\)?ycombinator\\.com" all-the-icons-faicon "y-combinator") - ("yahoo\\.com" all-the-icons-faicon "yahoo") - ("^\\(https?://\\)?\\(www\\.\\)?yoast\\.com" all-the-icons-faicon "yoast") - ;; Catch all - ("android" all-the-icons-faicon "android") - ("creativecommons" all-the-icons-faicon "creative-commons") - ("forums?" all-the-icons-octicon "comment-discussion") - ("\\.pdf$" all-the-icons-octicon "file-pdf" :v-adjust 0.0 :face all-the-icons-dred) - ("google" all-the-icons-faicon "google") - ("\\.rss" all-the-icons-faicon "rss") - )) - -;; ==================== -;; Functions Start -;; ==================== - -(defun all-the-icons-auto-mode-match? (&optional file) - "Whether or not FILE's `major-mode' match against its `auto-mode-alist'." - (let* ((file (or file (buffer-file-name) (buffer-name))) - (auto-mode (all-the-icons-match-to-alist file auto-mode-alist))) - (eq major-mode auto-mode))) - -(defun all-the-icons-match-to-alist (file alist) - "Match FILE against an entry in ALIST using `string-match'." - (cdr (cl-find-if (lambda (it) (string-match (car it) file)) alist))) - -(defun all-the-icons-dir-is-submodule (dir) - "Checker whether or not DIR is a git submodule." - (let* ((gitmodule-dir (locate-dominating-file dir ".gitmodules")) - (modules-file (expand-file-name (format "%s.gitmodules" gitmodule-dir))) - (module-search (format "submodule \".*?%s\"" (file-name-base dir)))) - - (when (and gitmodule-dir (file-exists-p (format "%s/.git" dir))) - (with-temp-buffer - (insert-file-contents modules-file) - (search-forward-regexp module-search (point-max) t))))) - -;; Icon functions -(defun all-the-icons-icon-for-dir-with-chevron (dir &optional chevron padding) - "Format an icon for DIR with CHEVRON similar to tree based directories. - -If PADDING is provided, it will prepend and separate the chevron -and directory with PADDING. - -Produces different symbols by inspecting DIR to distinguish -symlinks and git repositories which do not depend on the -directory contents" - (let ((icon (all-the-icons-icon-for-dir dir)) - (chevron (if chevron (all-the-icons-octicon (format "chevron-%s" chevron) :height 0.8 :v-adjust -0.1) "")) - (padding (or padding "\t"))) - (format "%s%s%s%s%s" padding chevron padding icon padding))) - -(defun all-the-icons-icon-for-buffer () - "Get the formatted icon for the current buffer. - -This function prioritises the use of the buffers file extension to -discern the icon when its `major-mode' matches its auto mode, -otherwise it will use the buffers `major-mode' to decide its -icon." - (all-the-icons--icon-info-for-buffer)) - -(defun all-the-icons-icon-family-for-buffer () - "Get the icon font family for the current buffer." - (all-the-icons--icon-info-for-buffer "family")) - -(defun all-the-icons--web-mode-icon (&rest arg-overrides) "Get icon for a `web-mode' buffer with ARG-OVERRIDES." (all-the-icons--web-mode nil arg-overrides)) -(defun all-the-icons--web-mode-icon-family () "Get icon family for a `web-mode' buffer." (all-the-icons--web-mode t)) -(defun all-the-icons--web-mode (&optional family arg-overrides) - "Return icon or FAMILY for `web-mode' based on `web-mode-content-type'. -Providing ARG-OVERRIDES will modify the creation of the icon." - (let ((non-nil-args (cl-reduce (lambda (acc it) (if it (append acc (list it)) acc)) arg-overrides :initial-value '()))) - (cond - ((equal web-mode-content-type "jsx") - (if family (all-the-icons-fileicon-family) (apply 'all-the-icons-fileicon (append '("jsx-2") non-nil-args)))) - ((equal web-mode-content-type "javascript") - (if family (all-the-icons-alltheicon-family) (apply 'all-the-icons-alltheicon (append '("javascript") non-nil-args)))) - ((equal web-mode-content-type "json") - (if family (all-the-icons-alltheicon-family) (apply 'all-the-icons-alltheicon (append '("less") non-nil-args)))) - ((equal web-mode-content-type "xml") - (if family (all-the-icons-faicon-family) (apply 'all-the-icons-faicon (append '("file-code-o") non-nil-args)))) - ((equal web-mode-content-type "css") - (if family (all-the-icons-alltheicon-family) (apply 'all-the-icons-alltheicon (append '("css3") non-nil-args)))) - (t - (if family (all-the-icons-alltheicon-family) (apply 'all-the-icons-alltheicon (append '("html5") non-nil-args))))))) - -;; Icon Functions - -;;;###autoload -(defun all-the-icons-icon-for-dir (dir &rest arg-overrides) - "Get the formatted icon for DIR. -ARG-OVERRIDES should be a plist containining `:height', -`:v-adjust' or `:face' properties like in the normal icon -inserting functions. - -Note: You want chevron, please use `all-the-icons-icon-for-dir-with-chevron'." - (let* ((dirname (file-name-base (directory-file-name dir))) - (path (expand-file-name dir)) - (icon (all-the-icons-match-to-alist dirname all-the-icons-dir-icon-alist)) - (args (cdr icon))) - (when arg-overrides (setq args (append `(,(car args)) arg-overrides (cdr args)))) - (cond - ((file-symlink-p path) - (apply #'all-the-icons-octicon "file-symlink-directory" (cdr args))) - ((all-the-icons-dir-is-submodule path) - (apply #'all-the-icons-octicon "file-submodule" (cdr args))) - ((file-exists-p (format "%s/.git" path)) - (apply #'all-the-icons-octicon "repo" (cdr args))) - (t (apply (car icon) args))))) - -;;;###autoload -(defun all-the-icons-icon-for-file (file &rest arg-overrides) - "Get the formatted icon for FILE. -ARG-OVERRIDES should be a plist containining `:height', -`:v-adjust' or `:face' properties like in the normal icon -inserting functions." - (let* ((ext (file-name-extension file)) - (icon (or (and ext - (cdr (assoc (downcase ext) - all-the-icons-extension-icon-alist))) - (all-the-icons-match-to-alist file all-the-icons-regexp-icon-alist))) - (args (cdr icon))) - (when arg-overrides (setq args (append `(,(car args)) arg-overrides (cdr args)))) - (apply (car icon) args))) - -;;;###autoload -(defun all-the-icons-icon-for-mode (mode &rest arg-overrides) - "Get the formatted icon for MODE. -ARG-OVERRIDES should be a plist containining `:height', -`:v-adjust' or `:face' properties like in the normal icon -inserting functions." - (let* ((icon (cdr (or (assoc mode all-the-icons-mode-icon-alist) - (assoc (get mode 'derived-mode-parent) all-the-icons-mode-icon-alist)))) - (args (cdr icon))) - (when arg-overrides (setq args (append `(,(car args)) arg-overrides (cdr args)))) - (if icon (apply (car icon) args) mode))) - -;;;###autoload -(defun all-the-icons-icon-for-url (url &rest arg-overrides) - "Get the formatted icon for URL. -If an icon for URL isn't found in `all-the-icons-url-alist', a globe is used. -ARG-OVERRIDES should be a plist containining `:height', -`:v-adjust' or `:face' properties like in the normal icon -inserting functions." - (let* ((icon (all-the-icons-match-to-alist url all-the-icons-url-alist)) - (args (cdr icon))) - (unless icon - (setq icon '(all-the-icons-faicon "globe")) - (setq args (cdr icon))) - (when arg-overrides (setq args (append `(,(car args)) arg-overrides (cdr args)))) - (apply (car icon) args))) - -(defcustom all-the-icons--cache-limit 2048 - "Maximum cache size for functions cached by `all-the-icons-cache'." - :type 'integer) - -(defun all-the-icons-cache (func) - "Set a cache for FUNC. Does not work on interactive functions." - (unless (get func 'all-the-icons--cached) - (let ((cache (make-hash-table :test #'equal - :size all-the-icons--cache-limit)) - (orig-fn (symbol-function func))) - (fset func - (lambda (&rest args) - (or (gethash args cache) - (progn - (when (> (hash-table-count cache) - all-the-icons--cache-limit) - (clrhash cache)) - (puthash args (apply orig-fn args) cache))))))) - - (put func 'all-the-icons--cached t)) - -(all-the-icons-cache #'all-the-icons-icon-for-dir) -(all-the-icons-cache #'all-the-icons-icon-for-file) -(all-the-icons-cache #'all-the-icons-icon-for-mode) -(all-the-icons-cache #'all-the-icons-icon-for-url) - -;; Family Face Functions -(defun all-the-icons-icon-family-for-file (file) - "Get the icons font family for FILE." - (let ((icon (all-the-icons-match-to-alist file all-the-icons-regexp-icon-alist))) - (funcall (intern (format "%s-family" (car icon)))))) - -(defun all-the-icons-icon-family-for-mode (mode) - "Get the icons font family for MODE." - (let ((icon (cdr (assoc mode all-the-icons-mode-icon-alist)))) - (if icon (funcall (intern (format "%s-family" (car icon)))) nil))) - -(defun all-the-icons-icon-family (icon) - "Get a propertized ICON family programmatically." - (plist-get (get-text-property 0 'face icon) :family)) - -(all-the-icons-cache #'all-the-icons-icon-family-for-file) -(all-the-icons-cache #'all-the-icons-icon-family-for-mode) -(all-the-icons-cache #'all-the-icons-icon-family) - -(defun all-the-icons--icon-info-for-buffer (&optional f) - "Get icon info for the current buffer. - -When F is provided, the info function is calculated with the format -`all-the-icons-icon-%s-for-file' or `all-the-icons-icon-%s-for-mode'." - (let* ((base-f (concat "all-the-icons-icon" (when f (format "-%s" f)))) - (file-f (intern (concat base-f "-for-file"))) - (mode-f (intern (concat base-f "-for-mode")))) - (if (and (buffer-file-name) - (all-the-icons-auto-mode-match?)) - (funcall file-f (file-name-nondirectory (buffer-file-name))) - (funcall mode-f major-mode)))) - -;; Weather icons -(defun all-the-icons-icon-for-weather (weather) - "Get an icon for a WEATHER status." - (let ((icon (all-the-icons-match-to-alist weather all-the-icons-weather-icon-alist))) - (if icon (apply (car icon) (cdr icon)) weather))) - -;; Definitions - -(eval-and-compile - (defun all-the-icons--function-name (name) - "Get the symbol for an icon function name for icon set NAME." - (intern (concat "all-the-icons-" (downcase (symbol-name name))))) - - (defun all-the-icons--family-name (name) - "Get the symbol for an icon family function for icon set NAME." - (intern (concat "all-the-icons-" (downcase (symbol-name name)) "-family"))) - - (defun all-the-icons--data-name (name) - "Get the symbol for an icon family function for icon set NAME." - (intern (concat "all-the-icons-" (downcase (symbol-name name)) "-data"))) - - (defun all-the-icons--insert-function-name (name) - "Get the symbol for an icon insert function for icon set NAME." - (intern (concat "all-the-icons-insert-" (downcase (symbol-name name))))) - - (defun all-the-icons--family-scale-factor (family) - (intern (concat "all-the-icons-" (symbol-name family) "-scale-factor"))) - - (defun all-the-icons--family-adjust (family) - (intern (concat "all-the-icons-default-" (symbol-name family) "-adjust")))) - -;; Icon insertion functions - -(defun all-the-icons--read-candidates () - "Helper to build a list of candidates for all families." - (cl-reduce 'append (mapcar (lambda (it) (all-the-icons--read-candidates-for-family it t)) all-the-icons-font-families))) - -(defun all-the-icons--read-candidates-for-family (family &optional show-family) - "Helper to build read candidates for FAMILY. -If SHOW-FAMILY is non-nil, displays the icons family in the candidate string." - (let ((data (funcall (all-the-icons--data-name family))) - (icon-f (all-the-icons--function-name family))) - (mapcar - (lambda (it) - (let* ((icon-name (car it)) - (icon-name-head (substring icon-name 0 1)) - (icon-name-tail (substring icon-name 1)) - - (icon-display (propertize icon-name-head 'display (format "%s\t%s" (funcall icon-f icon-name) icon-name-head))) - (icon-family (if show-family (format "\t[%s]" family) "")) - - (candidate-name (format "%s%s%s" icon-display icon-name-tail icon-family)) - (candidate-icon (funcall (all-the-icons--function-name family) icon-name))) - - (cons candidate-name candidate-icon))) - data))) - -;;;###autoload -(defun all-the-icons-install-fonts (&optional pfx) - "Helper function to download and install the latests fonts based on OS. -When PFX is non-nil, ignore the prompt and just install" - (interactive "P") - (when (or pfx (yes-or-no-p "This will download and install fonts, are you sure you want to do this?")) - (let* ((url-format "https://raw.githubusercontent.com/domtronn/all-the-icons.el/master/fonts/%s") - (font-dest (cond - ;; Default Linux install directories - ((member system-type '(gnu gnu/linux gnu/kfreebsd)) - (concat (or (getenv "XDG_DATA_HOME") - (concat (getenv "HOME") "/.local/share")) - "/fonts/")) - ;; Default MacOS install directory - ((eq system-type 'darwin) - (concat (getenv "HOME") "/Library/Fonts/")))) - (known-dest? (stringp font-dest)) - (font-dest (or font-dest (read-directory-name "Font installation directory: " "~/")))) - - (unless (file-directory-p font-dest) (mkdir font-dest t)) - - (mapc (lambda (font) - (url-copy-file (format url-format font) (expand-file-name font font-dest) t)) - all-the-icons-font-names) - (when known-dest? - (message "Fonts downloaded, updating font cache... ") - (shell-command-to-string (format "fc-cache -f -v"))) - (message "%s Successfully %s `all-the-icons' fonts to `%s'!" - (all-the-icons-wicon "stars" :v-adjust 0.0) - (if known-dest? "installed" "downloaded") - font-dest)))) - -;;;###autoload -(defun all-the-icons-insert (&optional arg family) - "Interactive icon insertion function. -When Prefix ARG is non-nil, insert the propertized icon. -When FAMILY is non-nil, limit the candidates to the icon set matching it." - (interactive "P") - (let* ((standard-output (current-buffer)) - (candidates (if family - (all-the-icons--read-candidates-for-family family) - (all-the-icons--read-candidates))) - (prompt (if family - (format "%s Icon: " (funcall (all-the-icons--family-name family))) - "Icon : ")) - - (selection (completing-read prompt candidates nil t)) - (result (cdr (assoc selection candidates)))) - - (if arg (prin1 result) (insert result)))) - -;; Debug Helpers - -(defun all-the-icons-insert-icons-for (family &optional height duration) - "Insert all of the available icons associated with FAMILY. -If a HEIGHT is provided it will render the icons at this height. -This is useful both to see the icons more clearly and to test -different height rendering. If DURATION is provided, it will -pause for DURATION seconds between printing each character." - (let* ((data-f (all-the-icons--data-name family)) - (insert-f (all-the-icons--function-name family)) - - (height (or height 2.0)) - (data (funcall data-f))) - (mapc - (lambda (it) - (insert (format "%s - %s\n" (funcall insert-f (car it) :height height) (car it))) - (when duration (sit-for duration 0))) - data))) - -(defmacro all-the-icons-define-icon (name alist family &optional font-name) - "Macro to generate functions for inserting icons for icon set NAME. - -NAME defines is the name of the iconset and will produce a -function of the for `all-the-icons-NAME'. - -ALIST is the alist containing maps between icon names and the -UniCode for the character. All of these can be found in the data -directory of this package. - -FAMILY is the font family to use for the icons. -FONT-NAME is the name of the .ttf file providing the font, defaults to FAMILY." - `(progn - (add-to-list 'all-the-icons-font-families (quote ,name)) - (add-to-list 'all-the-icons-font-names (quote ,(downcase (format "%s.ttf" (or font-name family))))) - (defcustom ,(all-the-icons--family-scale-factor name) 1.0 - ,(format "The additional `height' face property Scale Factor for %s icons." - (symbol-name name)) - :group 'all-the-icons - :type 'number) - (defcustom ,(all-the-icons--family-adjust name) 0.0 - ,(format "The additional `raise' display property adjustment for %s icons." - (symbol-name name)) - :group 'all-the-icons - :type 'number) - (defun ,(all-the-icons--family-name name) () ,family) - (defun ,(all-the-icons--data-name name) () ,alist) - (defun ,(all-the-icons--function-name name) (icon-name &rest args) - (let ((icon (cdr (assoc icon-name ,alist))) - (other-face (when all-the-icons-color-icons (plist-get args :face))) - (height (* all-the-icons-scale-factor - ,(all-the-icons--family-scale-factor name) - (or (plist-get args :height) 1.0))) - (v-adjust (* all-the-icons-scale-factor ,(all-the-icons--family-scale-factor name) - (+ (or (plist-get args :v-adjust) all-the-icons-default-adjust) - ,(all-the-icons--family-adjust name)))) - (family ,family)) - (unless icon - (error (format "Unable to find icon with name `%s' in icon set `%s'" icon-name (quote ,name)))) - (let ((face (if other-face - `(:family ,family :height ,height :inherit ,other-face) - `(:family ,family :height ,height)))) - (propertize icon - 'face face ;so that this works without `font-lock-mode' enabled - 'font-lock-face face ;so that `font-lock-mode' leaves this alone - 'display `(raise ,v-adjust) - 'rear-nonsticky t)))) - (defun ,(all-the-icons--insert-function-name name) (&optional arg) - ,(format "Insert a %s icon at point." family) - (interactive "P") - (all-the-icons-insert arg (quote ,name))))) - -(define-obsolete-function-alias 'define-icon 'all-the-icons-define-icon "4.0.0") - -(all-the-icons-define-icon alltheicon all-the-icons-data/alltheicons-alist "all-the-icons") -(all-the-icons-define-icon fileicon all-the-icons-data/file-icon-alist "file-icons") -(all-the-icons-define-icon faicon all-the-icons-data/fa-icon-alist "FontAwesome") -(all-the-icons-define-icon octicon all-the-icons-data/octicons-alist "github-octicons" "octicons") -(all-the-icons-define-icon wicon all-the-icons-data/weather-icons-alist "Weather Icons" "weathericons") -(all-the-icons-define-icon material all-the-icons-data/material-icons-alist "Material Icons" "material-design-icons") - -(provide 'all-the-icons) - -;;; all-the-icons.el ends here blob - 3322500f19e96f449d2e5c0352714c5e7fae08b6 (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/data/data-alltheicons.el +++ /dev/null @@ -1,70 +0,0 @@ -(defvar all-the-icons-data/alltheicons-alist - '( - - ( "apache" . "\xe909" ) - ( "atom" . "\xe917" ) - ( "aws" . "\xe90c" ) - ( "bower" . "\xe918" ) - ( "c" . "\xe915" ) - ( "c-line" . "\xe90f" ) - ( "clojure" . "\xe919" ) - ( "clojure-line" . "\xe91a" ) - ( "coffeescript" . "\xe914" ) - ( "cplusplus" . "\xe913" ) - ( "cplusplus-line" . "\xe910" ) - ( "csharp" . "\xe911" ) - ( "csharp-line" . "\xe912" ) - ( "css3" . "\xe91b" ) - ( "css3-alt" . "\xe91c" ) - ( "d3" . "\xe90e" ) - ( "dlang" . "\xe935" ) - ( "elixir" . "\xe936" ) - ( "erlang" . "\xe934" ) - ( "git" . "\xe907" ) - ( "go" . "\xe91d" ) - ( "google-drive" . "\xe91e" ) - ( "grunt" . "\xe90d" ) - ( "grunt-line" . "\xe91f" ) - ( "gulp" . "\xe920" ) - ( "haskell" . "\xe921" ) - ( "html5" . "\xe932" ) - ( "jasmine" . "\xe904" ) - ( "java" . "\xe922" ) - ( "javascript" . "\xe906" ) - ( "javascript-badge" . "\xe923" ) - ( "javascript-shield" . "\xe924" ) - ( "less" . "\xe90b" ) - ( "nginx" . "\xe933" ) - ( "nodejs" . "\xe925" ) - ( "perl" . "\xe905" ) - ( "perldocs" . "\xe926" ) - ( "postgresql" . "\xe938" ) - ( "prolog" . "\xe927" ) - ( "python" . "\xe928" ) - ( "react" . "\xe929" ) - ( "ruby" . "\xe92a" ) - ( "ruby-alt" . "\xe92b" ) - ( "rust" . "\xe92c" ) - ( "sass" . "\xe92d" ) - ( "scala" . "\xe908" ) - ( "script" . "\xe90a" ) - ( "spring" . "\xe937" ) - ( "stylus" . "\xe92e" ) - ( "svg" . "\xe903" ) - ( "swift" . "\xe92f" ) - ( "terminal" . "\xe930" ) - ( "terminal-alt" . "\xe931" ) - ( "battery-charging" . "\xe939" ) - - ( "arrow-left" . "\xe93a" ) - ( "arrow-right" . "\xe93b" ) - ( "cup-left" . "\xe93c" ) - ( "cup-right" . "\xe93d" ) - ( "slant-left" . "\xe93e" ) - ( "slant-right" . "\xe93f" ) - ( "wave-left" . "\xe940" ) - ( "wave-right" . "\xe941" ) - - )) - -(provide 'data-alltheicons) blob - 6ab0480646c3e2251a035c254fa4fc24274a55fd (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/data/data-faicons.el +++ /dev/null @@ -1,641 +0,0 @@ -(defvar all-the-icons-data/fa-icon-alist - '( - - ("500px" . "\xf26e") - ("adjust" . "\xf042") - ("adn" . "\xf170") - ("align-center" . "\xf037") - ("align-justify" . "\xf039") - ("align-left" . "\xf036") - ("align-right" . "\xf038") - ("amazon" . "\xf270") - ("ambulance" . "\xf0f9") - ("american-sign-language-interpreting" . "\xf2a3") - ("anchor" . "\xf13d") - ("android" . "\xf17b") - ("angellist" . "\xf209") - ("angle-double-down" . "\xf103") - ("angle-double-left" . "\xf100") - ("angle-double-right" . "\xf101") - ("angle-double-up" . "\xf102") - ("angle-down" . "\xf107") - ("angle-left" . "\xf104") - ("angle-right" . "\xf105") - ("angle-up" . "\xf106") - ("apple" . "\xf179") - ("archive" . "\xf187") - ("area-chart" . "\xf1fe") - ("arrow-circle-down" . "\xf0ab") - ("arrow-circle-left" . "\xf0a8") - ("arrow-circle-o-down" . "\xf01a") - ("arrow-circle-o-left" . "\xf190") - ("arrow-circle-o-right" . "\xf18e") - ("arrow-circle-o-up" . "\xf01b") - ("arrow-circle-right" . "\xf0a9") - ("arrow-circle-up" . "\xf0aa") - ("arrow-down" . "\xf063") - ("arrow-left" . "\xf060") - ("arrow-right" . "\xf061") - ("arrow-up" . "\xf062") - ("arrows" . "\xf047") - ("arrows-alt" . "\xf0b2") - ("arrows-h" . "\xf07e") - ("arrows-v" . "\xf07d") - ("assistive-listening-systems" . "\xf2a2") - ("asterisk" . "\xf069") - ("at" . "\xf1fa") - ("audio-description" . "\xf29e") - ("backward" . "\xf04a") - ("balance-scale" . "\xf24e") - ("ban" . "\xf05e") - ("bar-chart" . "\xf080") - ("barcode" . "\xf02a") - ("bars" . "\xf0c9") - ("battery-empty" . "\xf244") - ("battery-full" . "\xf240") - ("battery-half" . "\xf242") - ("battery-quarter" . "\xf243") - ("battery-three-quarters" . "\xf241") - ("bed" . "\xf236") - ("beer" . "\xf0fc") - ("behance" . "\xf1b4") - ("behance-square" . "\xf1b5") - ("bell" . "\xf0f3") - ("bell-o" . "\xf0a2") - ("bell-slash" . "\xf1f6") - ("bell-slash-o" . "\xf1f7") - ("bicycle" . "\xf206") - ("binoculars" . "\xf1e5") - ("birthday-cake" . "\xf1fd") - ("bitbucket" . "\xf171") - ("bitbucket-square" . "\xf172") - ("black-tie" . "\xf27e") - ("blind" . "\xf29d") - ("bluetooth" . "\xf293") - ("bluetooth-b" . "\xf294") - ("bold" . "\xf032") - ("bolt" . "\xf0e7") - ("bomb" . "\xf1e2") - ("book" . "\xf02d") - ("bookmark" . "\xf02e") - ("bookmark-o" . "\xf097") - ("braille" . "\xf2a1") - ("briefcase" . "\xf0b1") - ("btc" . "\xf15a") - ("bug" . "\xf188") - ("building" . "\xf1ad") - ("building-o" . "\xf0f7") - ("bullhorn" . "\xf0a1") - ("bullseye" . "\xf140") - ("bus" . "\xf207") - ("buysellads" . "\xf20d") - ("calculator" . "\xf1ec") - ("calendar" . "\xf073") - ("calendar-check-o" . "\xf274") - ("calendar-minus-o" . "\xf272") - ("calendar-o" . "\xf133") - ("calendar-plus-o" . "\xf271") - ("calendar-times-o" . "\xf273") - ("camera" . "\xf030") - ("camera-retro" . "\xf083") - ("car" . "\xf1b9") - ("caret-down" . "\xf0d7") - ("caret-left" . "\xf0d9") - ("caret-right" . "\xf0da") - ("caret-square-o-down" . "\xf150") - ("caret-square-o-left" . "\xf191") - ("caret-square-o-right" . "\xf152") - ("caret-square-o-up" . "\xf151") - ("caret-up" . "\xf0d8") - ("cart-arrow-down" . "\xf218") - ("cart-plus" . "\xf217") - ("cc" . "\xf20a") - ("cc-amex" . "\xf1f3") - ("cc-diners-club" . "\xf24c") - ("cc-discover" . "\xf1f2") - ("cc-jcb" . "\xf24b") - ("cc-mastercard" . "\xf1f1") - ("cc-paypal" . "\xf1f4") - ("cc-stripe" . "\xf1f5") - ("cc-visa" . "\xf1f0") - ("certificate" . "\xf0a3") - ("chain-broken" . "\xf127") - ("check" . "\xf00c") - ("check-circle" . "\xf058") - ("check-circle-o" . "\xf05d") - ("check-square" . "\xf14a") - ("check-square-o" . "\xf046") - ("chevron-circle-down" . "\xf13a") - ("chevron-circle-left" . "\xf137") - ("chevron-circle-right" . "\xf138") - ("chevron-circle-up" . "\xf139") - ("chevron-down" . "\xf078") - ("chevron-left" . "\xf053") - ("chevron-right" . "\xf054") - ("chevron-up" . "\xf077") - ("child" . "\xf1ae") - ("chrome" . "\xf268") - ("circle" . "\xf111") - ("circle-o" . "\xf10c") - ("circle-o-notch" . "\xf1ce") - ("circle-thin" . "\xf1db") - ("clipboard" . "\xf0ea") - ("clock-o" . "\xf017") - ("clone" . "\xf24d") - ("cloud" . "\xf0c2") - ("cloud-download" . "\xf0ed") - ("cloud-upload" . "\xf0ee") - ("code" . "\xf121") - ("code-fork" . "\xf126") - ("codepen" . "\xf1cb") - ("codiepie" . "\xf284") - ("coffee" . "\xf0f4") - ("cog" . "\xf013") - ("cogs" . "\xf085") - ("columns" . "\xf0db") - ("comment" . "\xf075") - ("comment-o" . "\xf0e5") - ("commenting" . "\xf27a") - ("commenting-o" . "\xf27b") - ("comments" . "\xf086") - ("comments-o" . "\xf0e6") - ("compass" . "\xf14e") - ("compress" . "\xf066") - ("connectdevelop" . "\xf20e") - ("contao" . "\xf26d") - ("copyright" . "\xf1f9") - ("creative-commons" . "\xf25e") - ("credit-card" . "\xf09d") - ("credit-card-alt" . "\xf283") - ("crop" . "\xf125") - ("crosshairs" . "\xf05b") - ("css3" . "\xf13c") - ("cube" . "\xf1b2") - ("cubes" . "\xf1b3") - ("cutlery" . "\xf0f5") - ("dashcube" . "\xf210") - ("database" . "\xf1c0") - ("deaf" . "\xf2a4") - ("delicious" . "\xf1a5") - ("desktop" . "\xf108") - ("deviantart" . "\xf1bd") - ("diamond" . "\xf219") - ("digg" . "\xf1a6") - ("dot-circle-o" . "\xf192") - ("download" . "\xf019") - ("dribbble" . "\xf17d") - ("dropbox" . "\xf16b") - ("drupal" . "\xf1a9") - ("edge" . "\xf282") - ("eject" . "\xf052") - ("ellipsis-h" . "\xf141") - ("ellipsis-v" . "\xf142") - ("empire" . "\xf1d1") - ("envelope" . "\xf0e0") - ("envelope-o" . "\xf003") - ("envelope-square" . "\xf199") - ("envira" . "\xf299") - ("eraser" . "\xf12d") - ("eur" . "\xf153") - ("exchange" . "\xf0ec") - ("exclamation" . "\xf12a") - ("exclamation-circle" . "\xf06a") - ("exclamation-triangle" . "\xf071") - ("expand" . "\xf065") - ("expeditedssl" . "\xf23e") - ("external-link" . "\xf08e") - ("external-link-square" . "\xf14c") - ("eye" . "\xf06e") - ("eye-slash" . "\xf070") - ("eyedropper" . "\xf1fb") - ("facebook" . "\xf09a") - ("facebook-official" . "\xf230") - ("facebook-square" . "\xf082") - ("fast-backward" . "\xf049") - ("fast-forward" . "\xf050") - ("fax" . "\xf1ac") - ("female" . "\xf182") - ("fighter-jet" . "\xf0fb") - ("file" . "\xf15b") - ("file-archive-o" . "\xf1c6") - ("file-audio-o" . "\xf1c7") - ("file-code-o" . "\xf1c9") - ("file-excel-o" . "\xf1c3") - ("file-image-o" . "\xf1c5") - ("file-o" . "\xf016") - ("file-pdf-o" . "\xf1c1") - ("file-powerpoint-o" . "\xf1c4") - ("file-text" . "\xf15c") - ("file-text-o" . "\xf0f6") - ("file-video-o" . "\xf1c8") - ("file-word-o" . "\xf1c2") - ("files-o" . "\xf0c5") - ("film" . "\xf008") - ("filter" . "\xf0b0") - ("fire" . "\xf06d") - ("fire-extinguisher" . "\xf134") - ("firefox" . "\xf269") - ("first-order" . "\xf2b0") - ("flag" . "\xf024") - ("flag-checkered" . "\xf11e") - ("flag-o" . "\xf11d") - ("flask" . "\xf0c3") - ("flickr" . "\xf16e") - ("floppy-o" . "\xf0c7") - ("folder" . "\xf07b") - ("folder-o" . "\xf114") - ("folder-open" . "\xf07c") - ("folder-open-o" . "\xf115") - ("font" . "\xf031") - ("font-awesome" . "\xf2b4") - ("fonticons" . "\xf280") - ("fort-awesome" . "\xf286") - ("forumbee" . "\xf211") - ("forward" . "\xf04e") - ("foursquare" . "\xf180") - ("frown-o" . "\xf119") - ("futbol-o" . "\xf1e3") - ("gamepad" . "\xf11b") - ("gavel" . "\xf0e3") - ("gbp" . "\xf154") - ("genderless" . "\xf22d") - ("get-pocket" . "\xf265") - ("gg" . "\xf260") - ("gg-circle" . "\xf261") - ("gift" . "\xf06b") - ("git" . "\xf1d3") - ("git-square" . "\xf1d2") - ("github" . "\xf09b") - ("github-alt" . "\xf113") - ("github-square" . "\xf092") - ("gitlab" . "\xf296") - ("glass" . "\xf000") - ("glide" . "\xf2a5") - ("glide-g" . "\xf2a6") - ("globe" . "\xf0ac") - ("google" . "\xf1a0") - ("google-plus" . "\xf0d5") - ("google-plus-official" . "\xf2b3") - ("google-plus-square" . "\xf0d4") - ("google-wallet" . "\xf1ee") - ("graduation-cap" . "\xf19d") - ("gratipay" . "\xf184") - ("h-square" . "\xf0fd") - ("hacker-news" . "\xf1d4") - ("hand-lizard-o" . "\xf258") - ("hand-o-down" . "\xf0a7") - ("hand-o-left" . "\xf0a5") - ("hand-o-right" . "\xf0a4") - ("hand-o-up" . "\xf0a6") - ("hand-paper-o" . "\xf256") - ("hand-peace-o" . "\xf25b") - ("hand-pointer-o" . "\xf25a") - ("hand-rock-o" . "\xf255") - ("hand-scissors-o" . "\xf257") - ("hand-spock-o" . "\xf259") - ("hashtag" . "\xf292") - ("hdd-o" . "\xf0a0") - ("header" . "\xf1dc") - ("headphones" . "\xf025") - ("heart" . "\xf004") - ("heart-o" . "\xf08a") - ("heartbeat" . "\xf21e") - ("history" . "\xf1da") - ("home" . "\xf015") - ("hospital-o" . "\xf0f8") - ("hourglass" . "\xf254") - ("hourglass-end" . "\xf253") - ("hourglass-half" . "\xf252") - ("hourglass-o" . "\xf250") - ("hourglass-start" . "\xf251") - ("houzz" . "\xf27c") - ("html5" . "\xf13b") - ("i-cursor" . "\xf246") - ("ils" . "\xf20b") - ("inbox" . "\xf01c") - ("indent" . "\xf03c") - ("industry" . "\xf275") - ("info" . "\xf129") - ("info-circle" . "\xf05a") - ("inr" . "\xf156") - ("instagram" . "\xf16d") - ("internet-explorer" . "\xf26b") - ("ioxhost" . "\xf208") - ("italic" . "\xf033") - ("joomla" . "\xf1aa") - ("jpy" . "\xf157") - ("jsfiddle" . "\xf1cc") - ("key" . "\xf084") - ("keyboard-o" . "\xf11c") - ("krw" . "\xf159") - ("language" . "\xf1ab") - ("laptop" . "\xf109") - ("lastfm" . "\xf202") - ("lastfm-square" . "\xf203") - ("leaf" . "\xf06c") - ("leanpub" . "\xf212") - ("lemon-o" . "\xf094") - ("level-down" . "\xf149") - ("level-up" . "\xf148") - ("life-ring" . "\xf1cd") - ("lightbulb-o" . "\xf0eb") - ("line-chart" . "\xf201") - ("link" . "\xf0c1") - ("linkedin" . "\xf0e1") - ("linkedin-square" . "\xf08c") - ("linux" . "\xf17c") - ("list" . "\xf03a") - ("list-alt" . "\xf022") - ("list-ol" . "\xf0cb") - ("list-ul" . "\xf0ca") - ("location-arrow" . "\xf124") - ("lock" . "\xf023") - ("long-arrow-down" . "\xf175") - ("long-arrow-left" . "\xf177") - ("long-arrow-right" . "\xf178") - ("long-arrow-up" . "\xf176") - ("low-vision" . "\xf2a8") - ("magic" . "\xf0d0") - ("magnet" . "\xf076") - ("male" . "\xf183") - ("map" . "\xf279") - ("map-marker" . "\xf041") - ("map-o" . "\xf278") - ("map-pin" . "\xf276") - ("map-signs" . "\xf277") - ("mars" . "\xf222") - ("mars-double" . "\xf227") - ("mars-stroke" . "\xf229") - ("mars-stroke-h" . "\xf22b") - ("mars-stroke-v" . "\xf22a") - ("maxcdn" . "\xf136") - ("meanpath" . "\xf20c") - ("medium" . "\xf23a") - ("medkit" . "\xf0fa") - ("meh-o" . "\xf11a") - ("mercury" . "\xf223") - ("microphone" . "\xf130") - ("microphone-slash" . "\xf131") - ("minus" . "\xf068") - ("minus-circle" . "\xf056") - ("minus-square" . "\xf146") - ("minus-square-o" . "\xf147") - ("mixcloud" . "\xf289") - ("mobile" . "\xf10b") - ("modx" . "\xf285") - ("money" . "\xf0d6") - ("moon-o" . "\xf186") - ("motorcycle" . "\xf21c") - ("mouse-pointer" . "\xf245") - ("music" . "\xf001") - ("neuter" . "\xf22c") - ("newspaper-o" . "\xf1ea") - ("object-group" . "\xf247") - ("object-ungroup" . "\xf248") - ("odnoklassniki" . "\xf263") - ("odnoklassniki-square" . "\xf264") - ("opencart" . "\xf23d") - ("openid" . "\xf19b") - ("opera" . "\xf26a") - ("optin-monster" . "\xf23c") - ("outdent" . "\xf03b") - ("pagelines" . "\xf18c") - ("paint-brush" . "\xf1fc") - ("paper-plane" . "\xf1d8") - ("paper-plane-o" . "\xf1d9") - ("paperclip" . "\xf0c6") - ("paragraph" . "\xf1dd") - ("pause" . "\xf04c") - ("pause-circle" . "\xf28b") - ("pause-circle-o" . "\xf28c") - ("paw" . "\xf1b0") - ("paypal" . "\xf1ed") - ("pencil" . "\xf040") - ("pencil-square" . "\xf14b") - ("pencil-square-o" . "\xf044") - ("percent" . "\xf295") - ("phone" . "\xf095") - ("phone-square" . "\xf098") - ("picture-o" . "\xf03e") - ("pie-chart" . "\xf200") - ("pied-piper" . "\xf2ae") - ("pied-piper-alt" . "\xf1a8") - ("pied-piper-pp" . "\xf1a7") - ("pinterest" . "\xf0d2") - ("pinterest-p" . "\xf231") - ("pinterest-square" . "\xf0d3") - ("plane" . "\xf072") - ("play" . "\xf04b") - ("play-circle" . "\xf144") - ("play-circle-o" . "\xf01d") - ("plug" . "\xf1e6") - ("plus" . "\xf067") - ("plus-circle" . "\xf055") - ("plus-square" . "\xf0fe") - ("plus-square-o" . "\xf196") - ("power-off" . "\xf011") - ("print" . "\xf02f") - ("product-hunt" . "\xf288") - ("puzzle-piece" . "\xf12e") - ("qq" . "\xf1d6") - ("qrcode" . "\xf029") - ("question" . "\xf128") - ("question-circle" . "\xf059") - ("question-circle-o" . "\xf29c") - ("quote-left" . "\xf10d") - ("quote-right" . "\xf10e") - ("random" . "\xf074") - ("rebel" . "\xf1d0") - ("recycle" . "\xf1b8") - ("reddit" . "\xf1a1") - ("reddit-alien" . "\xf281") - ("reddit-square" . "\xf1a2") - ("refresh" . "\xf021") - ("registered" . "\xf25d") - ("renren" . "\xf18b") - ("repeat" . "\xf01e") - ("reply" . "\xf112") - ("reply-all" . "\xf122") - ("retweet" . "\xf079") - ("road" . "\xf018") - ("rocket" . "\xf135") - ("rss" . "\xf09e") - ("rss-square" . "\xf143") - ("rub" . "\xf158") - ("safari" . "\xf267") - ("scissors" . "\xf0c4") - ("scribd" . "\xf28a") - ("search" . "\xf002") - ("search-minus" . "\xf010") - ("search-plus" . "\xf00e") - ("sellsy" . "\xf213") - ("server" . "\xf233") - ("share" . "\xf064") - ("share-alt" . "\xf1e0") - ("share-alt-square" . "\xf1e1") - ("share-square" . "\xf14d") - ("share-square-o" . "\xf045") - ("shield" . "\xf132") - ("ship" . "\xf21a") - ("shirtsinbulk" . "\xf214") - ("shopping-bag" . "\xf290") - ("shopping-basket" . "\xf291") - ("shopping-cart" . "\xf07a") - ("sign-in" . "\xf090") - ("sign-language" . "\xf2a7") - ("sign-out" . "\xf08b") - ("signal" . "\xf012") - ("simplybuilt" . "\xf215") - ("sitemap" . "\xf0e8") - ("skyatlas" . "\xf216") - ("skype" . "\xf17e") - ("slack" . "\xf198") - ("sliders" . "\xf1de") - ("slideshare" . "\xf1e7") - ("smile-o" . "\xf118") - ("snapchat" . "\xf2ab") - ("snapchat-ghost" . "\xf2ac") - ("snapchat-square" . "\xf2ad") - ("sort" . "\xf0dc") - ("sort-alpha-asc" . "\xf15d") - ("sort-alpha-desc" . "\xf15e") - ("sort-amount-asc" . "\xf160") - ("sort-amount-desc" . "\xf161") - ("sort-asc" . "\xf0de") - ("sort-desc" . "\xf0dd") - ("sort-numeric-asc" . "\xf162") - ("sort-numeric-desc" . "\xf163") - ("soundcloud" . "\xf1be") - ("space-shuttle" . "\xf197") - ("spinner" . "\xf110") - ("spoon" . "\xf1b1") - ("spotify" . "\xf1bc") - ("square" . "\xf0c8") - ("square-o" . "\xf096") - ("stack-exchange" . "\xf18d") - ("stack-overflow" . "\xf16c") - ("star" . "\xf005") - ("star-half" . "\xf089") - ("star-half-o" . "\xf123") - ("star-o" . "\xf006") - ("steam" . "\xf1b6") - ("steam-square" . "\xf1b7") - ("step-backward" . "\xf048") - ("step-forward" . "\xf051") - ("stethoscope" . "\xf0f1") - ("sticky-note" . "\xf249") - ("sticky-note-o" . "\xf24a") - ("stop" . "\xf04d") - ("stop-circle" . "\xf28d") - ("stop-circle-o" . "\xf28e") - ("street-view" . "\xf21d") - ("strikethrough" . "\xf0cc") - ("stumbleupon" . "\xf1a4") - ("stumbleupon-circle" . "\xf1a3") - ("subscript" . "\xf12c") - ("subway" . "\xf239") - ("suitcase" . "\xf0f2") - ("sun-o" . "\xf185") - ("superscript" . "\xf12b") - ("table" . "\xf0ce") - ("tablet" . "\xf10a") - ("tachometer" . "\xf0e4") - ("tag" . "\xf02b") - ("tags" . "\xf02c") - ("tasks" . "\xf0ae") - ("taxi" . "\xf1ba") - ("television" . "\xf26c") - ("tencent-weibo" . "\xf1d5") - ("terminal" . "\xf120") - ("text-height" . "\xf034") - ("text-width" . "\xf035") - ("th" . "\xf00a") - ("th-large" . "\xf009") - ("th-list" . "\xf00b") - ("themeisle" . "\xf2b2") - ("thumb-tack" . "\xf08d") - ("thumbs-down" . "\xf165") - ("thumbs-o-down" . "\xf088") - ("thumbs-o-up" . "\xf087") - ("thumbs-up" . "\xf164") - ("ticket" . "\xf145") - ("times" . "\xf00d") - ("times-circle" . "\xf057") - ("times-circle-o" . "\xf05c") - ("tint" . "\xf043") - ("toggle-off" . "\xf204") - ("toggle-on" . "\xf205") - ("trademark" . "\xf25c") - ("train" . "\xf238") - ("transgender" . "\xf224") - ("transgender-alt" . "\xf225") - ("trash" . "\xf1f8") - ("trash-o" . "\xf014") - ("tree" . "\xf1bb") - ("trello" . "\xf181") - ("tripadvisor" . "\xf262") - ("trophy" . "\xf091") - ("truck" . "\xf0d1") - ("try" . "\xf195") - ("tty" . "\xf1e4") - ("tumblr" . "\xf173") - ("tumblr-square" . "\xf174") - ("twitch" . "\xf1e8") - ("twitter" . "\xf099") - ("twitter-square" . "\xf081") - ("umbrella" . "\xf0e9") - ("underline" . "\xf0cd") - ("undo" . "\xf0e2") - ("universal-access" . "\xf29a") - ("university" . "\xf19c") - ("unlock" . "\xf09c") - ("unlock-alt" . "\xf13e") - ("upload" . "\xf093") - ("usb" . "\xf287") - ("usd" . "\xf155") - ("user" . "\xf007") - ("user-md" . "\xf0f0") - ("user-plus" . "\xf234") - ("user-secret" . "\xf21b") - ("user-times" . "\xf235") - ("users" . "\xf0c0") - ("venus" . "\xf221") - ("venus-double" . "\xf226") - ("venus-mars" . "\xf228") - ("viacoin" . "\xf237") - ("viadeo" . "\xf2a9") - ("viadeo-square" . "\xf2aa") - ("video-camera" . "\xf03d") - ("vimeo" . "\xf27d") - ("vimeo-square" . "\xf194") - ("vine" . "\xf1ca") - ("vk" . "\xf189") - ("volume-control-phone" . "\xf2a0") - ("volume-down" . "\xf027") - ("volume-off" . "\xf026") - ("volume-up" . "\xf028") - ("weibo" . "\xf18a") - ("weixin" . "\xf1d7") - ("whatsapp" . "\xf232") - ("wheelchair" . "\xf193") - ("wheelchair-alt" . "\xf29b") - ("wifi" . "\xf1eb") - ("wikipedia-w" . "\xf266") - ("windows" . "\xf17a") - ("wordpress" . "\xf19a") - ("wpbeginner" . "\xf297") - ("wpforms" . "\xf298") - ("wrench" . "\xf0ad") - ("xing" . "\xf168") - ("xing-square" . "\xf169") - ("y-combinator" . "\xf23b") - ("yahoo" . "\xf19e") - ("yelp" . "\xf1e9") - ("yoast" . "\xf2b1") - ("youtube" . "\xf167") - ("youtube-play" . "\xf16a") - ("youtube-square" . "\xf166") - - )) - -(provide 'data-faicons) blob - 9baf46be11353c3888a6e0cbbb283a4b6ee86544 (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/data/data-fileicons.el +++ /dev/null @@ -1,491 +0,0 @@ -(defvar all-the-icons-data/file-icon-alist - '( - - ( "1c" . "\xa5ea" ) - ( "1c-alt" . "\xea28" ) - ( "MJML" . "\xea6f" ) - ( "R" . "\xe905" ) - ( "abap" . "\xe92b" ) - ( "abif" . "\xea4e" ) - ( "access" . "\xe9ea" ) - ( "actionscript" . "\xe92e" ) - ( "ada" . "\xe90b" ) - ( "ae" . "\xe9f3" ) - ( "ai" . "\xe6b4" ) - ( "akka" . "\xea0e" ) - ( "alex" . "\x29cb" ) - ( "alloy" . "\xe935" ) - ( "alpine-linux" . "\xe9ff" ) - ( "ampl" . "\xe94e" ) - ( "amx" . "\xe99b" ) - ( "angelscript" . "\xea5b" ) - ( "ansible" . "\x24b6" ) - ( "ansible-alt" . "\x61" ) - ( "ant" . "\xe93e" ) - ( "antlr" . "\xe92c" ) - ( "antwar" . "\x2591" ) - ( "api-blueprint" . "\xe92d" ) - ( "apl" . "\x234b" ) - ( "apl-old" . "\xe909" ) - ( "apple" . "\xe925" ) - ( "appveyor" . "\xe923" ) - ( "arc" . "\xe92f" ) - ( "arch-linux" . "\x41" ) - ( "arduino" . "\xe930" ) - ( "arttext" . "\x24d0" ) - ( "asciidoc" . "\xe918" ) - ( "assembly" . "\xEB4F" ) - ( "ats" . "\xe934" ) - ( "audacity" . "\xe9f9" ) - ( "augeas" . "\xe931" ) - ( "aurelia" . "\xea48" ) - ( "auto-hotkey" . "\xe932" ) - ( "autoit" . "\xe933" ) - ( "babel" . "\xe91f" ) - ( "bazel" . "\xea5a" ) - ( "bem" . "\xea59" ) - ( "bib" . "\xe601" ) - ( "bintray" . "\xea6e" ) - ( "bithound" . "\xea2a" ) - ( "blender" . "\xe9fa" ) - ( "bluespec" . "\xe93c" ) - ( "boo" . "\xe939" ) - ( "brain" . "\xe93a" ) - ( "brakeman" . "\xe9d6" ) - ( "bro" . "\xe93b" ) - ( "broccoli" . "\xe922" ) - ( "brotli" . "\xea6c" ) - ( "browserslist" . "\xea80" ) - ( "brunch" . "\xea47" ) - ( "buck" . "\xea46" ) - ( "build-boot" . "\xf103" ) - ( "bundler" . "\xea45" ) - ( "byond" . "\xe962" ) - ( "cabal" . "\xe9c2" ) - ( "caddy" . "\xea58" ) - ( "cake" . "\xe9e3" ) - ( "cakefile" . "\xe924" ) - ( "cakephp" . "\xea43" ) - ( "cakephp-old" . "\xe9d3" ) - ( "cc" . "\xe9d5" ) - ( "ceylon" . "\xe94f" ) - ( "chai" . "\x63" ) - ( "chapel" . "\xe950" ) - ( "chartjs" . "\xea0b" ) - ( "chef" . "\xea42" ) - ( "chuck" . "\xe943" ) - ( "circle-ci" . "\xea12" ) - ( "cirru" . "\xe951" ) - ( "ckeditor" . "\xea0c" ) - ( "clarion" . "\xe952" ) - ( "clean" . "\xe95b" ) - ( "click" . "\xe95c" ) - ( "clips" . "\xe940" ) - ( "clj" . "\xf105" ) - ( "cljs" . "\xf104" ) - ( "closure-template" . "\xea82" ) - ( "cmake" . "\xe93f" ) - ( "cobol" . "\xea44" ) - ( "codecov" . "\x2602" ) - ( "codekit" . "\xea41" ) - ( "codemirror" . "\xea0d" ) - ( "codeship" . "\xea6a" ) - ( "cold-fusion" . "\xe929" ) - ( "clisp" . "\xe972" ) - ( "composer" . "\xe683" ) - ( "config" . "\xf07c" ) - ( "coq" . "\xe95f" ) - ( "cordova" . "\xea11" ) - ( "cp" . "\xe942" ) - ( "cpan" . "\xea87" ) - ( "creole" . "\xe95e" ) - ( "crystal" . "\xe902" ) - ( "cs-script" . "\xe9e2" ) - ( "csound" . "\xe9f0" ) - ( "cucumber" . "\xf02b" ) - ( "cython" . "\xe963" ) - ( "d3" . "\xea10" ) - ( "darcs" . "\xe964" ) - ( "dart" . "\xe698" ) - ( "dashboard" . "\xf07d" ) - ( "dbase" . "\xe9f1" ) - ( "default" . "\x1f5cc" ) - ( "delphi" . "\xea40" ) - ( "devicetree" . "\xea57" ) - ( "diff" . "\xe960" ) - ( "dockerfile" . "\xf106" ) - ( "doclets" . "\xea3f" ) - ( "doge" . "\xe946" ) - ( "dom" . "\xea71" ) - ( "donejs" . "\x1f3c1" ) - ( "doxygen" . "\xe928" ) - ( "dragula" . "\x1f44c" ) - ( "drone" . "\xea3d" ) - ( "dyalog" . "\xe90c" ) - ( "dylib" . "\xea15" ) - ( "e" . "\x45" ) - ( "eagle" . "\xe965" ) - ( "easybuild" . "\xea85" ) - ( "ec" . "\xe9c9" ) - ( "ecere" . "\xe966" ) - ( "edge" . "\xea78" ) - ( "editorconfig" . "\xea1b" ) - ( "eiffel" . "\xe967" ) - ( "ejs" . "\xea4b" ) - ( "electron" . "\xea27" ) - ( "elm" . "\xf102" ) - ( "emacs" . "\xe926" ) - ( "elisp" . "\xe926" ) - ( "ember" . "\xe61b" ) - ( "emberscript" . "\xe968" ) - ( "eq" . "\xea0a" ) - ( "esdoc" . "\xea5c" ) - ( "eslint" . "\xea0f" ) - ( "eslint-old" . "\xe90e" ) - ( "excel" . "\xe9ee" ) - ( "fabfile" . "\xe94b" ) - ( "factor" . "\xe96a" ) - ( "fancy" . "\xe96b" ) - ( "fantom" . "\xe96f" ) - ( "fbx" . "\xe9fc" ) - ( "ffmpeg" . "\xea22" ) - ( "finder" . "\xe9e9" ) - ( "firebase" . "\xea7f" ) - ( "flow" . "\xe921" ) - ( "flux" . "\xe969" ) - ( "font" . "\xe90f" ) - ( "fontforge" . "\xfb00" ) - ( "fortran" . "\xe90a" ) - ( "franca" . "\xea56" ) - ( "freemarker" . "\xe970" ) - ( "frege" . "\xe96e" ) - ( "fsharp" . "\xe6a7" ) - ( "fuel-ux" . "\xea09" ) - ( "gams" . "\xe973" ) - ( "gap" . "\xe971" ) - ( "gdb" . "\xea08" ) - ( "genshi" . "\xe976" ) - ( "gentoo" . "\xe96d" ) - ( "gf" . "\xe978" ) - ( "gitlab" . "\xea3c" ) - ( "glade" . "\xe938" ) - ( "glyphs" . "\x47" ) - ( "gn" . "\xea25" ) - ( "gnu" . "\xe679" ) - ( "go" . "\xeaae" ) - ( "godot" . "\xe974" ) - ( "golo" . "\xe979" ) - ( "gosu" . "\xe97a" ) - ( "gradle" . "\xe903" ) - ( "graphql" . "\xe97c" ) - ( "graphviz" . "\xe97d" ) - ( "groovy" . "\xe904" ) - ( "grunt" . "\xe611" ) - ( "gulp" . "\xe610" ) - ( "hack" . "\xe9ce" ) - ( "haml" . "\xf15b" ) - ( "harbour" . "\xe97b" ) - ( "hashicorp" . "\xe97e" ) - ( "haxe" . "\xe907" ) - ( "haxedevelop" . "\xea3b" ) - ( "hg" . "\x263f" ) - ( "hoplon" . "\xea4d" ) - ( "hy" . "\xe97f" ) - ( "icu" . "\xea23" ) - ( "id" . "\xe9f4" ) - ( "idl" . "\xe947" ) - ( "idris" . "\xe983" ) - ( "igorpro" . "\xe980" ) - ( "image" . "\xf012" ) - ( "inform7" . "\xe984" ) - ( "inno" . "\xe985" ) - ( "io" . "\xe981" ) - ( "ioke" . "\xe982" ) - ( "ionic-project" . "\xf14b" ) - ( "isabelle" . "\xe945" ) - ( "j" . "\xe937" ) - ( "jade" . "\xe90d" ) - ( "jake" . "\xe948" ) - ( "jasmine" . "\xea3a" ) - ( "jenkins" . "\xe667" ) - ( "jest" . "\xea39" ) - ( "jinja" . "\xe944" ) - ( "jison" . "\xea55" ) - ( "jolie" . "\xea75" ) - ( "jsonld" . "\xe958" ) - ( "jsx" . "\xf100" ) - ( "jsx-2" . "\xf101" ) - ( "jsx2-alt" . "\xe9e6" ) - ( "julia" . "\x26ec" ) - ( "junos" . "\xea81" ) - ( "jupyter" . "\xe987" ) - ( "karma" . "\xe9cd" ) - ( "keynote" . "\xe9e5" ) - ( "khronos" . "\xe9f8" ) - ( "kicad" . "\xea4c" ) - ( "kitchenci" . "\xea38" ) - ( "kivy" . "\xe901" ) - ( "knockout" . "\x4b" ) - ( "kotlin" . "\xe989" ) - ( "krl" . "\xe988" ) - ( "labview" . "\xe98a" ) - ( "lasso" . "\xe98c" ) - ( "leaflet" . "\xea07" ) - ( "lean" . "\x4c" ) - ( "lerna" . "\xea37" ) - ( "lfe" . "\xe94c" ) - ( "libuv" . "\xea21" ) - ( "lightwave" . "\xe9fb" ) - ( "lime" . "\xea36" ) - ( "lisp" . "\xe908" ) - ( "livescript" . "\xe914" ) - ( "llvm" . "\xe91d" ) - ( "logtalk" . "\xe98d" ) - ( "lookml" . "\xe98e" ) - ( "lsl" . "\xe98b" ) - ( "lua" . "\xe91b" ) - ( "mako" . "\xe98f" ) - ( "man-page" . "\xe936" ) - ( "mapbox" . "\xe941" ) - ( "markdownlint" . "\xf0c9" ) - ( "marko" . "\xe920" ) - ( "mathematica" . "\xe990" ) - ( "mathjax" . "\xea06" ) - ( "matlab" . "\xe991" ) - ( "max" . "\xe993" ) - ( "maxscript" . "\xe900" ) - ( "maya" . "\xe9f6" ) - ( "mediawiki" . "\xe954" ) - ( "mercury" . "\xe994" ) - ( "meson" . "\xea54" ) - ( "metal" . "\x4d" ) - ( "meteor" . "\xe6a5" ) - ( "microsoft-infopath" . "\xea35" ) - ( "minecraft" . "\xe9dc" ) - ( "minizinc" . "\xea53" ) - ( "mirah" . "\xe995" ) - ( "miranda" . "\xea52" ) - ( "mocha" . "\x26fe" ) - ( "modula-2" . "\xe996" ) - ( "moment" . "\x1f558" ) - ( "moment-tz" . "\x1f30d" ) - ( "monkey" . "\xe997" ) - ( "moustache" . "\xe60f" ) - ( "mruby" . "\xea18" ) - ( "mupad" . "\xe9ca" ) - ( "nano" . "\xea76" ) - ( "nanoc" . "\xea51" ) - ( "nant" . "\xe9e1" ) - ( "nasm" . "\xea72" ) - ( "neko" . "\xea05" ) - ( "netlogo" . "\xe99c" ) - ( "new-relic" . "\xe9d7" ) - ( "nginx" . "\xf146b" ) - ( "nib" . "\x2712" ) - ( "nimrod" . "\xe998" ) - ( "nit" . "\xe999" ) - ( "nix" . "\xe99a" ) - ( "nmap" . "\xe94d" ) - ( "nodemon" . "\xea26" ) - ( "normalize" . "\xea04" ) - ( "npm" . "\xe91c" ) - ( "npm-old" . "\xf17b" ) - ( "nsis" . "\xea1e" ) - ( "nsis-old" . "\xe992" ) - ( "nuclide" . "\xea34" ) - ( "nuget" . "\xe9d9" ) - ( "numpy" . "\xe99d" ) - ( "nunjucks" . "\xe953" ) - ( "nvidia" . "\xe95d" ) - ( "nxc" . "\xea6b" ) - ( "obj" . "\xe9e8" ) - ( "objective-j" . "\xe99e" ) - ( "ocaml" . "\xe91a" ) - ( "octave" . "\xea33" ) - ( "onenote" . "\xe9eb" ) - ( "ooc" . "\xe9cb" ) - ( "opa" . "\x2601" ) - ( "opencl" . "\xe99f" ) - ( "opengl" . "\xea7a" ) - ( "openoffice" . "\xe9e4" ) - ( "openscad" . "\xe911" ) - ( "org" . "\xe917" ) - ( "owl" . "\xe957" ) - ( "ox" . "\xe9a1" ) - ( "oxygene" . "\xe9bf" ) - ( "oz" . "\xe9be" ) - ( "p4" . "\xea50" ) - ( "pan" . "\xe9bd" ) - ( "papyrus" . "\xe9bc" ) - ( "parrot" . "\xe9bb" ) - ( "pascal" . "\xe92a" ) - ( "patch" . "\xe961" ) - ( "pawn" . "\x265f" ) - ( "pb" . "\xea14" ) - ( "pegjs" . "\xea74" ) - ( "perl6" . "\xe96c" ) - ( "phalcon" . "\xe94a" ) - ( "phoenix" . "\xea5f" ) - ( "php" . "\xf147" ) - ( "phpunit" . "\xea32" ) - ( "pickle" . "\xe9c4" ) - ( "pike" . "\xe9b9" ) - ( "platformio" . "\xea2c" ) - ( "pm2" . "\x2630" ) - ( "pod" . "\xea84" ) - ( "pogo" . "\xe9b8" ) - ( "pointwise" . "\xe977" ) - ( "polymer" . "\xea2b" ) - ( "pony" . "\xe9b7" ) - ( "postcss" . "\xe910" ) - ( "postscript" . "\xe955" ) - ( "povray" . "\x50" ) - ( "powerpoint" . "\xe9ec" ) - ( "powershell" . "\xe9da" ) - ( "precision" . "\x2295" ) - ( "premiere" . "\xe9f5" ) - ( "processing" . "\xe9a0" ) - ( "progress" . "\xe9c0" ) - ( "propeller" . "\xe9b5" ) - ( "proselint" . "\xea6d" ) - ( "protractor" . "\xe9de" ) - ( "ps" . "\xe6b8" ) - ( "pug" . "\xea13" ) - ( "pug-alt" . "\xe9d0" ) - ( "puppet" . "\xf0c3" ) - ( "purebasic" . "\x1b5" ) - ( "purescript" . "\xe9b2" ) - ( "racket" . "\xe9b1" ) - ( "raml" . "\xe913" ) - ( "rascal" . "\xea24" ) - ( "rdoc" . "\xe9b0" ) - ( "realbasic" . "\xe9af" ) - ( "reason" . "\xea1d" ) - ( "rebol" . "\xe9ae" ) - ( "red" . "\xe9ad" ) - ( "redux" . "\xea30" ) - ( "regex" . "\x2a" ) - ( "rexx" . "\xea16" ) - ( "rhino" . "\xea4a" ) - ( "ring" . "\x1f48d" ) - ( "riot" . "\xe919" ) - ( "robot" . "\xe9ac" ) - ( "rollup" . "\xea20" ) - ( "rollup-old" . "\xe9fd" ) - ( "rot" . "\x1f764" ) - ( "rspec" . "\xea31" ) - ( "rst" . "\xe9cc" ) - ( "sage" . "\xe9ab" ) - ( "saltstack" . "\xe915" ) - ( "sas" . "\xe95a" ) - ( "sbt" . "\xe9d2" ) - ( "sc" . "\xe9a2" ) - ( "scheme" . "\x3bb" ) - ( "scilab" . "\xe9a9" ) - ( "scrutinizer" . "\xe9d4" ) - ( "self" . "\xe9a8" ) - ( "sequelize" . "\xea2f" ) - ( "sf" . "\xe9db" ) - ( "shen" . "\xe9a7" ) - ( "shipit" . "\x26f5" ) - ( "shippable" . "\xea2d" ) - ( "shopify" . "\xe9cf" ) - ( "shuriken" . "\x272b" ) - ( "silverstripe" . "\xe800" ) - ( "sinatra" . "\xea03" ) - ( "sketch" . "\xe927" ) - ( "sketchup-layout" . "\xea7c" ) - ( "sketchup-make" . "\xea7e" ) - ( "sketchup-stylebuilder" . "\xea7d" ) - ( "slash" . "\xe9a6" ) - ( "snyk" . "\xea1c" ) - ( "solidity" . "\xea86" ) - ( "sparql" . "\xe959" ) - ( "spray" . "\xea02" ) - ( "sqf" . "\xe9a5" ) - ( "sqlite" . "\xe9dd" ) - ( "squarespace" . "\xea5e" ) - ( "stan" . "\xe9a4" ) - ( "stata" . "\xe9a3" ) - ( "storyist" . "\xe9ef" ) - ( "strings" . "\xe9e0" ) - ( "stylelint" . "\xe93d" ) - ( "stylus" . "\x73" ) - ( "stylus-full" . "\xe9f7" ) - ( "stylus-orb" . "\x53" ) - ( "sublime" . "\xe986" ) - ( "sv" . "\xe9c3" ) - ( "svn" . "\xea17" ) - ( "swagger" . "\xea29" ) - ( "tag" . "\xf015" ) - ( "tcl" . "\xe956" ) - ( "telegram" . "\x2708" ) - ( "terminal" . "\xf0c8" ) - ( "tern" . "\x1f54a" ) - ( "terraform" . "\xe916" ) - ( "test-coffeescript" . "\xea62" ) - ( "test-dir" . "\xea60" ) - ( "test-generic" . "\xea63" ) - ( "test-js" . "\xea64" ) - ( "test-perl" . "\xea65" ) - ( "test-python" . "\xea66" ) - ( "test-react" . "\xea67" ) - ( "test-ruby" . "\xea68" ) - ( "test-typescript" . "\xea69" ) - ( "tex" . "\xe600" ) - ( "textile" . "\x74" ) - ( "textmate" . "\x2122" ) - ( "thor" . "\xe9d8" ) - ( "tinymce" . "\xea01" ) - ( "tsx" . "\xe9d1" ) - ( "tsx-alt" . "\xe9e7" ) - ( "tt" . "\x54" ) - ( "turing" . "\xe9b6" ) - ( "twig" . "\x2e19" ) - ( "twine" . "\xea5d" ) - ( "txl" . "\xe9c1" ) - ( "typedoc" . "\xe9fe" ) - ( "typescript" . "\xe912" ) - ( "typescript-alt" . "\x2a6" ) - ( "typings" . "\xe9df" ) - ( "uno" . "\xe9b3" ) - ( "unreal" . "\x75" ) - ( "urweb" . "\xe9ba" ) - ( "v8" . "\xea1f" ) - ( "vagrant" . "\x56" ) - ( "vcl" . "\xe9b4" ) - ( "verilog" . "\xe949" ) - ( "vertex-shader" . "\xea79" ) - ( "vhdl" . "\xe9aa" ) - ( "video" . "\xf057" ) - ( "virtualbox" . "\xea3e" ) - ( "virtualbox-alt" . "\xea2e" ) - ( "visio" . "\xea83" ) - ( "vmware" . "\xea49" ) - ( "vue" . "\xe906" ) - ( "wasm" . "\xea70" ) - ( "watchman" . "\xea4f" ) - ( "webgl" . "\xea7b" ) - ( "webpack" . "\xea61" ) - ( "webpack-old" . "\xe91e" ) - ( "wercker" . "\xea19" ) - ( "word" . "\xe9ed" ) - ( "x10" . "\x2169" ) - ( "xamarin" . "\xea77" ) - ( "xmos" . "\x58" ) - ( "xpages" . "\xe9c5" ) - ( "xtend" . "\xe9c6" ) - ( "yarn" . "\xea1a" ) - ( "yasm" . "\xea73" ) - ( "yin-yang" . "\x262f" ) - ( "yoyo" . "\xe975" ) - ( "yui" . "\xea00" ) - ( "zbrush" . "\xe9f2" ) - ( "zephir" . "\xe9c7" ) - ("zig" . "\x7A") - ( "zimpl" . "\xe9c8" ) - - ) - ) - -(provide 'data-fileicons) blob - bafcfe7814af764da1665fdac97769cc41d9ffa2 (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/data/data-material.el +++ /dev/null @@ -1,935 +0,0 @@ -(defvar all-the-icons-data/material-icons-alist - '(("3d_rotation" . "\xe84d") - ("ac_unit" . "\xeb3b") - ("access_alarm" . "\xe190") - ("access_alarms" . "\xe191") - ("access_time" . "\xe192") - ("accessibility" . "\xe84e") - ("accessible" . "\xe914") - ("account_balance" . "\xe84f") - ("account_balance_wallet" . "\xe850") - ("account_box" . "\xe851") - ("account_circle" . "\xe853") - ("adb" . "\xe60e") - ("add" . "\xe145") - ("add_a_photo" . "\xe439") - ("add_alarm" . "\xe193") - ("add_alert" . "\xe003") - ("add_box" . "\xe146") - ("add_circle" . "\xe147") - ("add_circle_outline" . "\xe148") - ("add_location" . "\xe567") - ("add_shopping_cart" . "\xe854") - ("add_to_photos" . "\xe39d") - ("add_to_queue" . "\xe05c") - ("adjust" . "\xe39e") - ("airline_seat_flat" . "\xe630") - ("airline_seat_flat_angled" . "\xe631") - ("airline_seat_individual_suite" . "\xe632") - ("airline_seat_legroom_extra" . "\xe633") - ("airline_seat_legroom_normal" . "\xe634") - ("airline_seat_legroom_reduced" . "\xe635") - ("airline_seat_recline_extra" . "\xe636") - ("airline_seat_recline_normal" . "\xe637") - ("airplanemode_active" . "\xe195") - ("airplanemode_inactive" . "\xe194") - ("airplay" . "\xe055") - ("airport_shuttle" . "\xeb3c") - ("alarm" . "\xe855") - ("alarm_add" . "\xe856") - ("alarm_off" . "\xe857") - ("alarm_on" . "\xe858") - ("album" . "\xe019") - ("all_inclusive" . "\xeb3d") - ("all_out" . "\xe90b") - ("android" . "\xe859") - ("announcement" . "\xe85a") - ("apps" . "\xe5c3") - ("archive" . "\xe149") - ("arrow_back" . "\xe5c4") - ("arrow_downward" . "\xe5db") - ("arrow_drop_down" . "\xe5c5") - ("arrow_drop_down_circle" . "\xe5c6") - ("arrow_drop_up" . "\xe5c7") - ("arrow_forward" . "\xe5c8") - ("arrow_upward" . "\xe5d8") - ("art_track" . "\xe060") - ("aspect_ratio" . "\xe85b") - ("assessment" . "\xe85c") - ("assignment" . "\xe85d") - ("assignment_ind" . "\xe85e") - ("assignment_late" . "\xe85f") - ("assignment_return" . "\xe860") - ("assignment_returned" . "\xe861") - ("assignment_turned_in" . "\xe862") - ("assistant" . "\xe39f") - ("assistant_photo" . "\xe3a0") - ("attach_file" . "\xe226") - ("attach_money" . "\xe227") - ("attachment" . "\xe2bc") - ("audiotrack" . "\xe3a1") - ("autorenew" . "\xe863") - ("av_timer" . "\xe01b") - ("backspace" . "\xe14a") - ("backup" . "\xe864") - ("battery_alert" . "\xe19c") - ("battery_charging_full" . "\xe1a3") - ("battery_full" . "\xe1a4") - ("battery_std" . "\xe1a5") - ("battery_unknown" . "\xe1a6") - ("beach_access" . "\xeb3e") - ("beenhere" . "\xe52d") - ("block" . "\xe14b") - ("bluetooth" . "\xe1a7") - ("bluetooth_audio" . "\xe60f") - ("bluetooth_connected" . "\xe1a8") - ("bluetooth_disabled" . "\xe1a9") - ("bluetooth_searching" . "\xe1aa") - ("blur_circular" . "\xe3a2") - ("blur_linear" . "\xe3a3") - ("blur_off" . "\xe3a4") - ("blur_on" . "\xe3a5") - ("book" . "\xe865") - ("bookmark" . "\xe866") - ("bookmark_border" . "\xe867") - ("border_all" . "\xe228") - ("border_bottom" . "\xe229") - ("border_clear" . "\xe22a") - ("border_color" . "\xe22b") - ("border_horizontal" . "\xe22c") - ("border_inner" . "\xe22d") - ("border_left" . "\xe22e") - ("border_outer" . "\xe22f") - ("border_right" . "\xe230") - ("border_style" . "\xe231") - ("border_top" . "\xe232") - ("border_vertical" . "\xe233") - ("branding_watermark" . "\xe06b") - ("brightness_1" . "\xe3a6") - ("brightness_2" . "\xe3a7") - ("brightness_3" . "\xe3a8") - ("brightness_4" . "\xe3a9") - ("brightness_5" . "\xe3aa") - ("brightness_6" . "\xe3ab") - ("brightness_7" . "\xe3ac") - ("brightness_auto" . "\xe1ab") - ("brightness_high" . "\xe1ac") - ("brightness_low" . "\xe1ad") - ("brightness_medium" . "\xe1ae") - ("broken_image" . "\xe3ad") - ("brush" . "\xe3ae") - ("bubble_chart" . "\xe6dd") - ("bug_report" . "\xe868") - ("build" . "\xe869") - ("burst_mode" . "\xe43c") - ("business" . "\xe0af") - ("business_center" . "\xeb3f") - ("cached" . "\xe86a") - ("cake" . "\xe7e9") - ("call" . "\xe0b0") - ("call_end" . "\xe0b1") - ("call_made" . "\xe0b2") - ("call_merge" . "\xe0b3") - ("call_missed" . "\xe0b4") - ("call_missed_outgoing" . "\xe0e4") - ("call_received" . "\xe0b5") - ("call_split" . "\xe0b6") - ("call_to_action" . "\xe06c") - ("camera" . "\xe3af") - ("camera_alt" . "\xe3b0") - ("camera_enhance" . "\xe8fc") - ("camera_front" . "\xe3b1") - ("camera_rear" . "\xe3b2") - ("camera_roll" . "\xe3b3") - ("cancel" . "\xe5c9") - ("card_giftcard" . "\xe8f6") - ("card_membership" . "\xe8f7") - ("card_travel" . "\xe8f8") - ("casino" . "\xeb40") - ("cast" . "\xe307") - ("cast_connected" . "\xe308") - ("center_focus_strong" . "\xe3b4") - ("center_focus_weak" . "\xe3b5") - ("change_history" . "\xe86b") - ("chat" . "\xe0b7") - ("chat_bubble" . "\xe0ca") - ("chat_bubble_outline" . "\xe0cb") - ("check" . "\xe5ca") - ("check_box" . "\xe834") - ("check_box_outline_blank" . "\xe835") - ("check_circle" . "\xe86c") - ("chevron_left" . "\xe5cb") - ("chevron_right" . "\xe5cc") - ("child_care" . "\xeb41") - ("child_friendly" . "\xeb42") - ("chrome_reader_mode" . "\xe86d") - ("class" . "\xe86e") - ("clear" . "\xe14c") - ("clear_all" . "\xe0b8") - ("close" . "\xe5cd") - ("closed_caption" . "\xe01c") - ("cloud" . "\xe2bd") - ("cloud_circle" . "\xe2be") - ("cloud_done" . "\xe2bf") - ("cloud_download" . "\xe2c0") - ("cloud_off" . "\xe2c1") - ("cloud_queue" . "\xe2c2") - ("cloud_upload" . "\xe2c3") - ("code" . "\xe86f") - ("collections" . "\xe3b6") - ("collections_bookmark" . "\xe431") - ("color_lens" . "\xe3b7") - ("colorize" . "\xe3b8") - ("comment" . "\xe0b9") - ("compare" . "\xe3b9") - ("compare_arrows" . "\xe915") - ("computer" . "\xe30a") - ("confirmation_number" . "\xe638") - ("contact_mail" . "\xe0d0") - ("contact_phone" . "\xe0cf") - ("contacts" . "\xe0ba") - ("content_copy" . "\xe14d") - ("content_cut" . "\xe14e") - ("content_paste" . "\xe14f") - ("control_point" . "\xe3ba") - ("control_point_duplicate" . "\xe3bb") - ("copyright" . "\xe90c") - ("create" . "\xe150") - ("create_new_folder" . "\xe2cc") - ("credit_card" . "\xe870") - ("crop" . "\xe3be") - ("crop_16_9" . "\xe3bc") - ("crop_3_2" . "\xe3bd") - ("crop_5_4" . "\xe3bf") - ("crop_7_5" . "\xe3c0") - ("crop_din" . "\xe3c1") - ("crop_free" . "\xe3c2") - ("crop_landscape" . "\xe3c3") - ("crop_original" . "\xe3c4") - ("crop_portrait" . "\xe3c5") - ("crop_rotate" . "\xe437") - ("crop_square" . "\xe3c6") - ("dashboard" . "\xe871") - ("data_usage" . "\xe1af") - ("date_range" . "\xe916") - ("dehaze" . "\xe3c7") - ("delete" . "\xe872") - ("delete_forever" . "\xe92b") - ("delete_sweep" . "\xe16c") - ("description" . "\xe873") - ("desktop_mac" . "\xe30b") - ("desktop_windows" . "\xe30c") - ("details" . "\xe3c8") - ("developer_board" . "\xe30d") - ("developer_mode" . "\xe1b0") - ("device_hub" . "\xe335") - ("devices" . "\xe1b1") - ("devices_other" . "\xe337") - ("dialer_sip" . "\xe0bb") - ("dialpad" . "\xe0bc") - ("directions" . "\xe52e") - ("directions_bike" . "\xe52f") - ("directions_boat" . "\xe532") - ("directions_bus" . "\xe530") - ("directions_car" . "\xe531") - ("directions_railway" . "\xe534") - ("directions_run" . "\xe566") - ("directions_subway" . "\xe533") - ("directions_transit" . "\xe535") - ("directions_walk" . "\xe536") - ("disc_full" . "\xe610") - ("dns" . "\xe875") - ("do_not_disturb" . "\xe612") - ("do_not_disturb_alt" . "\xe611") - ("do_not_disturb_off" . "\xe643") - ("do_not_disturb_on" . "\xe644") - ("dock" . "\xe30e") - ("domain" . "\xe7ee") - ("done" . "\xe876") - ("done_all" . "\xe877") - ("donut_large" . "\xe917") - ("donut_small" . "\xe918") - ("drafts" . "\xe151") - ("drag_handle" . "\xe25d") - ("drive_eta" . "\xe613") - ("dvr" . "\xe1b2") - ("edit" . "\xe3c9") - ("edit_location" . "\xe568") - ("eject" . "\xe8fb") - ("email" . "\xe0be") - ("enhanced_encryption" . "\xe63f") - ("equalizer" . "\xe01d") - ("error" . "\xe000") - ("error_outline" . "\xe001") - ("euro_symbol" . "\xe926") - ("ev_station" . "\xe56d") - ("event" . "\xe878") - ("event_available" . "\xe614") - ("event_busy" . "\xe615") - ("event_note" . "\xe616") - ("event_seat" . "\xe903") - ("exit_to_app" . "\xe879") - ("expand_less" . "\xe5ce") - ("expand_more" . "\xe5cf") - ("explicit" . "\xe01e") - ("explore" . "\xe87a") - ("exposure" . "\xe3ca") - ("exposure_neg_1" . "\xe3cb") - ("exposure_neg_2" . "\xe3cc") - ("exposure_plus_1" . "\xe3cd") - ("exposure_plus_2" . "\xe3ce") - ("exposure_zero" . "\xe3cf") - ("extension" . "\xe87b") - ("face" . "\xe87c") - ("fast_forward" . "\xe01f") - ("fast_rewind" . "\xe020") - ("favorite" . "\xe87d") - ("favorite_border" . "\xe87e") - ("featured_play_list" . "\xe06d") - ("featured_video" . "\xe06e") - ("feedback" . "\xe87f") - ("fiber_dvr" . "\xe05d") - ("fiber_manual_record" . "\xe061") - ("fiber_new" . "\xe05e") - ("fiber_pin" . "\xe06a") - ("fiber_smart_record" . "\xe062") - ("file_download" . "\xe2c4") - ("file_upload" . "\xe2c6") - ("filter" . "\xe3d3") - ("filter_1" . "\xe3d0") - ("filter_2" . "\xe3d1") - ("filter_3" . "\xe3d2") - ("filter_4" . "\xe3d4") - ("filter_5" . "\xe3d5") - ("filter_6" . "\xe3d6") - ("filter_7" . "\xe3d7") - ("filter_8" . "\xe3d8") - ("filter_9" . "\xe3d9") - ("filter_9_plus" . "\xe3da") - ("filter_b_and_w" . "\xe3db") - ("filter_center_focus" . "\xe3dc") - ("filter_drama" . "\xe3dd") - ("filter_frames" . "\xe3de") - ("filter_hdr" . "\xe3df") - ("filter_list" . "\xe152") - ("filter_none" . "\xe3e0") - ("filter_tilt_shift" . "\xe3e2") - ("filter_vintage" . "\xe3e3") - ("find_in_page" . "\xe880") - ("find_replace" . "\xe881") - ("fingerprint" . "\xe90d") - ("first_page" . "\xe5dc") - ("fitness_center" . "\xeb43") - ("flag" . "\xe153") - ("flare" . "\xe3e4") - ("flash_auto" . "\xe3e5") - ("flash_off" . "\xe3e6") - ("flash_on" . "\xe3e7") - ("flight" . "\xe539") - ("flight_land" . "\xe904") - ("flight_takeoff" . "\xe905") - ("flip" . "\xe3e8") - ("flip_to_back" . "\xe882") - ("flip_to_front" . "\xe883") - ("folder" . "\xe2c7") - ("folder_open" . "\xe2c8") - ("folder_shared" . "\xe2c9") - ("folder_special" . "\xe617") - ("font_download" . "\xe167") - ("format_align_center" . "\xe234") - ("format_align_justify" . "\xe235") - ("format_align_left" . "\xe236") - ("format_align_right" . "\xe237") - ("format_bold" . "\xe238") - ("format_clear" . "\xe239") - ("format_color_fill" . "\xe23a") - ("format_color_reset" . "\xe23b") - ("format_color_text" . "\xe23c") - ("format_indent_decrease" . "\xe23d") - ("format_indent_increase" . "\xe23e") - ("format_italic" . "\xe23f") - ("format_line_spacing" . "\xe240") - ("format_list_bulleted" . "\xe241") - ("format_list_numbered" . "\xe242") - ("format_paint" . "\xe243") - ("format_quote" . "\xe244") - ("format_shapes" . "\xe25e") - ("format_size" . "\xe245") - ("format_strikethrough" . "\xe246") - ("format_textdirection_l_to_r" . "\xe247") - ("format_textdirection_r_to_l" . "\xe248") - ("format_underlined" . "\xe249") - ("forum" . "\xe0bf") - ("forward" . "\xe154") - ("forward_10" . "\xe056") - ("forward_30" . "\xe057") - ("forward_5" . "\xe058") - ("free_breakfast" . "\xeb44") - ("fullscreen" . "\xe5d0") - ("fullscreen_exit" . "\xe5d1") - ("functions" . "\xe24a") - ("g_translate" . "\xe927") - ("gamepad" . "\xe30f") - ("games" . "\xe021") - ("gavel" . "\xe90e") - ("gesture" . "\xe155") - ("get_app" . "\xe884") - ("gif" . "\xe908") - ("golf_course" . "\xeb45") - ("gps_fixed" . "\xe1b3") - ("gps_not_fixed" . "\xe1b4") - ("gps_off" . "\xe1b5") - ("grade" . "\xe885") - ("gradient" . "\xe3e9") - ("grain" . "\xe3ea") - ("graphic_eq" . "\xe1b8") - ("grid_off" . "\xe3eb") - ("grid_on" . "\xe3ec") - ("group" . "\xe7ef") - ("group_add" . "\xe7f0") - ("group_work" . "\xe886") - ("hd" . "\xe052") - ("hdr_off" . "\xe3ed") - ("hdr_on" . "\xe3ee") - ("hdr_strong" . "\xe3f1") - ("hdr_weak" . "\xe3f2") - ("headset" . "\xe310") - ("headset_mic" . "\xe311") - ("healing" . "\xe3f3") - ("hearing" . "\xe023") - ("help" . "\xe887") - ("help_outline" . "\xe8fd") - ("high_quality" . "\xe024") - ("highlight" . "\xe25f") - ("highlight_off" . "\xe888") - ("history" . "\xe889") - ("home" . "\xe88a") - ("hot_tub" . "\xeb46") - ("hotel" . "\xe53a") - ("hourglass_empty" . "\xe88b") - ("hourglass_full" . "\xe88c") - ("http" . "\xe902") - ("https" . "\xe88d") - ("image" . "\xe3f4") - ("image_aspect_ratio" . "\xe3f5") - ("import_contacts" . "\xe0e0") - ("import_export" . "\xe0c3") - ("important_devices" . "\xe912") - ("inbox" . "\xe156") - ("indeterminate_check_box" . "\xe909") - ("info" . "\xe88e") - ("info_outline" . "\xe88f") - ("input" . "\xe890") - ("insert_chart" . "\xe24b") - ("insert_comment" . "\xe24c") - ("insert_drive_file" . "\xe24d") - ("insert_emoticon" . "\xe24e") - ("insert_invitation" . "\xe24f") - ("insert_link" . "\xe250") - ("insert_photo" . "\xe251") - ("invert_colors" . "\xe891") - ("invert_colors_off" . "\xe0c4") - ("iso" . "\xe3f6") - ("keyboard" . "\xe312") - ("keyboard_arrow_down" . "\xe313") - ("keyboard_arrow_left" . "\xe314") - ("keyboard_arrow_right" . "\xe315") - ("keyboard_arrow_up" . "\xe316") - ("keyboard_backspace" . "\xe317") - ("keyboard_capslock" . "\xe318") - ("keyboard_hide" . "\xe31a") - ("keyboard_return" . "\xe31b") - ("keyboard_tab" . "\xe31c") - ("keyboard_voice" . "\xe31d") - ("kitchen" . "\xeb47") - ("label" . "\xe892") - ("label_outline" . "\xe893") - ("landscape" . "\xe3f7") - ("language" . "\xe894") - ("laptop" . "\xe31e") - ("laptop_chromebook" . "\xe31f") - ("laptop_mac" . "\xe320") - ("laptop_windows" . "\xe321") - ("last_page" . "\xe5dd") - ("launch" . "\xe895") - ("layers" . "\xe53b") - ("layers_clear" . "\xe53c") - ("leak_add" . "\xe3f8") - ("leak_remove" . "\xe3f9") - ("lens" . "\xe3fa") - ("library_add" . "\xe02e") - ("library_books" . "\xe02f") - ("library_music" . "\xe030") - ("lightbulb_outline" . "\xe90f") - ("line_style" . "\xe919") - ("line_weight" . "\xe91a") - ("linear_scale" . "\xe260") - ("link" . "\xe157") - ("linked_camera" . "\xe438") - ("list" . "\xe896") - ("live_help" . "\xe0c6") - ("live_tv" . "\xe639") - ("local_activity" . "\xe53f") - ("local_airport" . "\xe53d") - ("local_atm" . "\xe53e") - ("local_bar" . "\xe540") - ("local_cafe" . "\xe541") - ("local_car_wash" . "\xe542") - ("local_convenience_store" . "\xe543") - ("local_dining" . "\xe556") - ("local_drink" . "\xe544") - ("local_florist" . "\xe545") - ("local_gas_station" . "\xe546") - ("local_grocery_store" . "\xe547") - ("local_hospital" . "\xe548") - ("local_hotel" . "\xe549") - ("local_laundry_service" . "\xe54a") - ("local_library" . "\xe54b") - ("local_mall" . "\xe54c") - ("local_movies" . "\xe54d") - ("local_offer" . "\xe54e") - ("local_parking" . "\xe54f") - ("local_pharmacy" . "\xe550") - ("local_phone" . "\xe551") - ("local_pizza" . "\xe552") - ("local_play" . "\xe553") - ("local_post_office" . "\xe554") - ("local_printshop" . "\xe555") - ("local_see" . "\xe557") - ("local_shipping" . "\xe558") - ("local_taxi" . "\xe559") - ("location_city" . "\xe7f1") - ("location_disabled" . "\xe1b6") - ("location_off" . "\xe0c7") - ("location_on" . "\xe0c8") - ("location_searching" . "\xe1b7") - ("lock" . "\xe897") - ("lock_open" . "\xe898") - ("lock_outline" . "\xe899") - ("looks" . "\xe3fc") - ("looks_3" . "\xe3fb") - ("looks_4" . "\xe3fd") - ("looks_5" . "\xe3fe") - ("looks_6" . "\xe3ff") - ("looks_one" . "\xe400") - ("looks_two" . "\xe401") - ("loop" . "\xe028") - ("loupe" . "\xe402") - ("low_priority" . "\xe16d") - ("loyalty" . "\xe89a") - ("mail" . "\xe158") - ("mail_outline" . "\xe0e1") - ("map" . "\xe55b") - ("markunread" . "\xe159") - ("markunread_mailbox" . "\xe89b") - ("memory" . "\xe322") - ("menu" . "\xe5d2") - ("merge_type" . "\xe252") - ("message" . "\xe0c9") - ("mic" . "\xe029") - ("mic_none" . "\xe02a") - ("mic_off" . "\xe02b") - ("mms" . "\xe618") - ("mode_comment" . "\xe253") - ("mode_edit" . "\xe254") - ("monetization_on" . "\xe263") - ("money_off" . "\xe25c") - ("monochrome_photos" . "\xe403") - ("mood" . "\xe7f2") - ("mood_bad" . "\xe7f3") - ("more" . "\xe619") - ("more_horiz" . "\xe5d3") - ("more_vert" . "\xe5d4") - ("motorcycle" . "\xe91b") - ("mouse" . "\xe323") - ("move_to_inbox" . "\xe168") - ("movie" . "\xe02c") - ("movie_creation" . "\xe404") - ("movie_filter" . "\xe43a") - ("multiline_chart" . "\xe6df") - ("music_note" . "\xe405") - ("music_video" . "\xe063") - ("my_location" . "\xe55c") - ("nature" . "\xe406") - ("nature_people" . "\xe407") - ("navigate_before" . "\xe408") - ("navigate_next" . "\xe409") - ("navigation" . "\xe55d") - ("near_me" . "\xe569") - ("network_cell" . "\xe1b9") - ("network_check" . "\xe640") - ("network_locked" . "\xe61a") - ("network_wifi" . "\xe1ba") - ("new_releases" . "\xe031") - ("next_week" . "\xe16a") - ("nfc" . "\xe1bb") - ("no_encryption" . "\xe641") - ("no_sim" . "\xe0cc") - ("not_interested" . "\xe033") - ("note" . "\xe06f") - ("note_add" . "\xe89c") - ("notifications" . "\xe7f4") - ("notifications_active" . "\xe7f7") - ("notifications_none" . "\xe7f5") - ("notifications_off" . "\xe7f6") - ("notifications_paused" . "\xe7f8") - ("offline_pin" . "\xe90a") - ("ondemand_video" . "\xe63a") - ("opacity" . "\xe91c") - ("open_in_browser" . "\xe89d") - ("open_in_new" . "\xe89e") - ("open_with" . "\xe89f") - ("pages" . "\xe7f9") - ("pageview" . "\xe8a0") - ("palette" . "\xe40a") - ("pan_tool" . "\xe925") - ("panorama" . "\xe40b") - ("panorama_fish_eye" . "\xe40c") - ("panorama_horizontal" . "\xe40d") - ("panorama_vertical" . "\xe40e") - ("panorama_wide_angle" . "\xe40f") - ("party_mode" . "\xe7fa") - ("pause" . "\xe034") - ("pause_circle_filled" . "\xe035") - ("pause_circle_outline" . "\xe036") - ("payment" . "\xe8a1") - ("people" . "\xe7fb") - ("people_outline" . "\xe7fc") - ("perm_camera_mic" . "\xe8a2") - ("perm_contact_calendar" . "\xe8a3") - ("perm_data_setting" . "\xe8a4") - ("perm_device_information" . "\xe8a5") - ("perm_identity" . "\xe8a6") - ("perm_media" . "\xe8a7") - ("perm_phone_msg" . "\xe8a8") - ("perm_scan_wifi" . "\xe8a9") - ("person" . "\xe7fd") - ("person_add" . "\xe7fe") - ("person_outline" . "\xe7ff") - ("person_pin" . "\xe55a") - ("person_pin_circle" . "\xe56a") - ("personal_video" . "\xe63b") - ("pets" . "\xe91d") - ("phone" . "\xe0cd") - ("phone_android" . "\xe324") - ("phone_bluetooth_speaker" . "\xe61b") - ("phone_forwarded" . "\xe61c") - ("phone_in_talk" . "\xe61d") - ("phone_iphone" . "\xe325") - ("phone_locked" . "\xe61e") - ("phone_missed" . "\xe61f") - ("phone_paused" . "\xe620") - ("phonelink" . "\xe326") - ("phonelink_erase" . "\xe0db") - ("phonelink_lock" . "\xe0dc") - ("phonelink_off" . "\xe327") - ("phonelink_ring" . "\xe0dd") - ("phonelink_setup" . "\xe0de") - ("photo" . "\xe410") - ("photo_album" . "\xe411") - ("photo_camera" . "\xe412") - ("photo_filter" . "\xe43b") - ("photo_library" . "\xe413") - ("photo_size_select_actual" . "\xe432") - ("photo_size_select_large" . "\xe433") - ("photo_size_select_small" . "\xe434") - ("picture_as_pdf" . "\xe415") - ("picture_in_picture" . "\xe8aa") - ("picture_in_picture_alt" . "\xe911") - ("pie_chart" . "\xe6c4") - ("pie_chart_outlined" . "\xe6c5") - ("pin_drop" . "\xe55e") - ("place" . "\xe55f") - ("play_arrow" . "\xe037") - ("play_circle_filled" . "\xe038") - ("play_circle_outline" . "\xe039") - ("play_for_work" . "\xe906") - ("playlist_add" . "\xe03b") - ("playlist_add_check" . "\xe065") - ("playlist_play" . "\xe05f") - ("plus_one" . "\xe800") - ("poll" . "\xe801") - ("polymer" . "\xe8ab") - ("pool" . "\xeb48") - ("portable_wifi_off" . "\xe0ce") - ("portrait" . "\xe416") - ("power" . "\xe63c") - ("power_input" . "\xe336") - ("power_settings_new" . "\xe8ac") - ("pregnant_woman" . "\xe91e") - ("present_to_all" . "\xe0df") - ("print" . "\xe8ad") - ("priority_high" . "\xe645") - ("public" . "\xe80b") - ("publish" . "\xe255") - ("query_builder" . "\xe8ae") - ("question_answer" . "\xe8af") - ("queue" . "\xe03c") - ("queue_music" . "\xe03d") - ("queue_play_next" . "\xe066") - ("radio" . "\xe03e") - ("radio_button_checked" . "\xe837") - ("radio_button_unchecked" . "\xe836") - ("rate_review" . "\xe560") - ("receipt" . "\xe8b0") - ("recent_actors" . "\xe03f") - ("record_voice_over" . "\xe91f") - ("redeem" . "\xe8b1") - ("redo" . "\xe15a") - ("refresh" . "\xe5d5") - ("remove" . "\xe15b") - ("remove_circle" . "\xe15c") - ("remove_circle_outline" . "\xe15d") - ("remove_from_queue" . "\xe067") - ("remove_red_eye" . "\xe417") - ("remove_shopping_cart" . "\xe928") - ("reorder" . "\xe8fe") - ("repeat" . "\xe040") - ("repeat_one" . "\xe041") - ("replay" . "\xe042") - ("replay_10" . "\xe059") - ("replay_30" . "\xe05a") - ("replay_5" . "\xe05b") - ("reply" . "\xe15e") - ("reply_all" . "\xe15f") - ("report" . "\xe160") - ("report_problem" . "\xe8b2") - ("restaurant" . "\xe56c") - ("restaurant_menu" . "\xe561") - ("restore" . "\xe8b3") - ("restore_page" . "\xe929") - ("ring_volume" . "\xe0d1") - ("room" . "\xe8b4") - ("room_service" . "\xeb49") - ("rotate_90_degrees_ccw" . "\xe418") - ("rotate_left" . "\xe419") - ("rotate_right" . "\xe41a") - ("rounded_corner" . "\xe920") - ("router" . "\xe328") - ("rowing" . "\xe921") - ("rss_feed" . "\xe0e5") - ("rv_hookup" . "\xe642") - ("satellite" . "\xe562") - ("save" . "\xe161") - ("scanner" . "\xe329") - ("schedule" . "\xe8b5") - ("school" . "\xe80c") - ("screen_lock_landscape" . "\xe1be") - ("screen_lock_portrait" . "\xe1bf") - ("screen_lock_rotation" . "\xe1c0") - ("screen_rotation" . "\xe1c1") - ("screen_share" . "\xe0e2") - ("sd_card" . "\xe623") - ("sd_storage" . "\xe1c2") - ("search" . "\xe8b6") - ("security" . "\xe32a") - ("select_all" . "\xe162") - ("send" . "\xe163") - ("sentiment_dissatisfied" . "\xe811") - ("sentiment_neutral" . "\xe812") - ("sentiment_satisfied" . "\xe813") - ("sentiment_very_dissatisfied" . "\xe814") - ("sentiment_very_satisfied" . "\xe815") - ("settings" . "\xe8b8") - ("settings_applications" . "\xe8b9") - ("settings_backup_restore" . "\xe8ba") - ("settings_bluetooth" . "\xe8bb") - ("settings_brightness" . "\xe8bd") - ("settings_cell" . "\xe8bc") - ("settings_ethernet" . "\xe8be") - ("settings_input_antenna" . "\xe8bf") - ("settings_input_component" . "\xe8c0") - ("settings_input_composite" . "\xe8c1") - ("settings_input_hdmi" . "\xe8c2") - ("settings_input_svideo" . "\xe8c3") - ("settings_overscan" . "\xe8c4") - ("settings_phone" . "\xe8c5") - ("settings_power" . "\xe8c6") - ("settings_remote" . "\xe8c7") - ("settings_system_daydream" . "\xe1c3") - ("settings_voice" . "\xe8c8") - ("share" . "\xe80d") - ("shop" . "\xe8c9") - ("shop_two" . "\xe8ca") - ("shopping_basket" . "\xe8cb") - ("shopping_cart" . "\xe8cc") - ("short_text" . "\xe261") - ("show_chart" . "\xe6e1") - ("shuffle" . "\xe043") - ("signal_cellular_4_bar" . "\xe1c8") - ("signal_cellular_connected_no_internet_4_bar" . "\xe1cd") - ("signal_cellular_no_sim" . "\xe1ce") - ("signal_cellular_null" . "\xe1cf") - ("signal_cellular_off" . "\xe1d0") - ("signal_wifi_4_bar" . "\xe1d8") - ("signal_wifi_4_bar_lock" . "\xe1d9") - ("signal_wifi_off" . "\xe1da") - ("sim_card" . "\xe32b") - ("sim_card_alert" . "\xe624") - ("skip_next" . "\xe044") - ("skip_previous" . "\xe045") - ("slideshow" . "\xe41b") - ("slow_motion_video" . "\xe068") - ("smartphone" . "\xe32c") - ("smoke_free" . "\xeb4a") - ("smoking_rooms" . "\xeb4b") - ("sms" . "\xe625") - ("sms_failed" . "\xe626") - ("snooze" . "\xe046") - ("sort" . "\xe164") - ("sort_by_alpha" . "\xe053") - ("spa" . "\xeb4c") - ("space_bar" . "\xe256") - ("speaker" . "\xe32d") - ("speaker_group" . "\xe32e") - ("speaker_notes" . "\xe8cd") - ("speaker_notes_off" . "\xe92a") - ("speaker_phone" . "\xe0d2") - ("spellcheck" . "\xe8ce") - ("star" . "\xe838") - ("star_border" . "\xe83a") - ("star_half" . "\xe839") - ("stars" . "\xe8d0") - ("stay_current_landscape" . "\xe0d3") - ("stay_current_portrait" . "\xe0d4") - ("stay_primary_landscape" . "\xe0d5") - ("stay_primary_portrait" . "\xe0d6") - ("stop" . "\xe047") - ("stop_screen_share" . "\xe0e3") - ("storage" . "\xe1db") - ("store" . "\xe8d1") - ("store_mall_directory" . "\xe563") - ("straighten" . "\xe41c") - ("streetview" . "\xe56e") - ("strikethrough_s" . "\xe257") - ("style" . "\xe41d") - ("subdirectory_arrow_left" . "\xe5d9") - ("subdirectory_arrow_right" . "\xe5da") - ("subject" . "\xe8d2") - ("subscriptions" . "\xe064") - ("subtitles" . "\xe048") - ("subway" . "\xe56f") - ("supervisor_account" . "\xe8d3") - ("surround_sound" . "\xe049") - ("swap_calls" . "\xe0d7") - ("swap_horiz" . "\xe8d4") - ("swap_vert" . "\xe8d5") - ("swap_vertical_circle" . "\xe8d6") - ("switch_camera" . "\xe41e") - ("switch_video" . "\xe41f") - ("sync" . "\xe627") - ("sync_disabled" . "\xe628") - ("sync_problem" . "\xe629") - ("system_update" . "\xe62a") - ("system_update_alt" . "\xe8d7") - ("tab" . "\xe8d8") - ("tab_unselected" . "\xe8d9") - ("tablet" . "\xe32f") - ("tablet_android" . "\xe330") - ("tablet_mac" . "\xe331") - ("tag_faces" . "\xe420") - ("tap_and_play" . "\xe62b") - ("terrain" . "\xe564") - ("text_fields" . "\xe262") - ("text_format" . "\xe165") - ("textsms" . "\xe0d8") - ("texture" . "\xe421") - ("theaters" . "\xe8da") - ("thumb_down" . "\xe8db") - ("thumb_up" . "\xe8dc") - ("thumbs_up_down" . "\xe8dd") - ("time_to_leave" . "\xe62c") - ("timelapse" . "\xe422") - ("timeline" . "\xe922") - ("timer" . "\xe425") - ("timer_10" . "\xe423") - ("timer_3" . "\xe424") - ("timer_off" . "\xe426") - ("title" . "\xe264") - ("toc" . "\xe8de") - ("today" . "\xe8df") - ("toll" . "\xe8e0") - ("tonality" . "\xe427") - ("touch_app" . "\xe913") - ("toys" . "\xe332") - ("track_changes" . "\xe8e1") - ("traffic" . "\xe565") - ("train" . "\xe570") - ("tram" . "\xe571") - ("transfer_within_a_station" . "\xe572") - ("transform" . "\xe428") - ("translate" . "\xe8e2") - ("trending_down" . "\xe8e3") - ("trending_flat" . "\xe8e4") - ("trending_up" . "\xe8e5") - ("tune" . "\xe429") - ("turned_in" . "\xe8e6") - ("turned_in_not" . "\xe8e7") - ("tv" . "\xe333") - ("unarchive" . "\xe169") - ("undo" . "\xe166") - ("unfold_less" . "\xe5d6") - ("unfold_more" . "\xe5d7") - ("update" . "\xe923") - ("usb" . "\xe1e0") - ("verified_user" . "\xe8e8") - ("vertical_align_bottom" . "\xe258") - ("vertical_align_center" . "\xe259") - ("vertical_align_top" . "\xe25a") - ("vibration" . "\xe62d") - ("video_call" . "\xe070") - ("video_label" . "\xe071") - ("video_library" . "\xe04a") - ("videocam" . "\xe04b") - ("videocam_off" . "\xe04c") - ("videogame_asset" . "\xe338") - ("view_agenda" . "\xe8e9") - ("view_array" . "\xe8ea") - ("view_carousel" . "\xe8eb") - ("view_column" . "\xe8ec") - ("view_comfy" . "\xe42a") - ("view_compact" . "\xe42b") - ("view_day" . "\xe8ed") - ("view_headline" . "\xe8ee") - ("view_list" . "\xe8ef") - ("view_module" . "\xe8f0") - ("view_quilt" . "\xe8f1") - ("view_stream" . "\xe8f2") - ("view_week" . "\xe8f3") - ("vignette" . "\xe435") - ("visibility" . "\xe8f4") - ("visibility_off" . "\xe8f5") - ("voice_chat" . "\xe62e") - ("voicemail" . "\xe0d9") - ("volume_down" . "\xe04d") - ("volume_mute" . "\xe04e") - ("volume_off" . "\xe04f") - ("volume_up" . "\xe050") - ("vpn_key" . "\xe0da") - ("vpn_lock" . "\xe62f") - ("wallpaper" . "\xe1bc") - ("warning" . "\xe002") - ("watch" . "\xe334") - ("watch_later" . "\xe924") - ("wb_auto" . "\xe42c") - ("wb_cloudy" . "\xe42d") - ("wb_incandescent" . "\xe42e") - ("wb_iridescent" . "\xe436") - ("wb_sunny" . "\xe430") - ("wc" . "\xe63d") - ("web" . "\xe051") - ("web_asset" . "\xe069") - ("weekend" . "\xe16b") - ("whatshot" . "\xe80e") - ("widgets" . "\xe1bd") - ("wifi" . "\xe63e") - ("wifi_lock" . "\xe1e1") - ("wifi_tethering" . "\xe1e2") - ("work" . "\xe8f9") - ("wrap_text" . "\xe25b") - ("youtube_searched_for" . "\xe8fa") - ("zoom_in" . "\xe8ff") - ("zoom_out" . "\xe900") - ("zoom_out_map" . "\xe56b"))) - - (provide 'data-material) blob - 432251ed18f260b539ea757bfc653b35a79abb93 (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/data/data-octicons.el +++ /dev/null @@ -1,165 +0,0 @@ -(defvar all-the-icons-data/octicons-alist - '( - - ("alert" . "\xf02d") - ("arrow-down" . "\xf03f") - ("arrow-left" . "\xf040") - ("arrow-right" . "\xf03e") - ("arrow-small-down" . "\xf0a0") - ("arrow-small-left" . "\xf0a1") - ("arrow-small-right" . "\xf071") - ("arrow-small-up" . "\xf09f") - ("arrow-up" . "\xf03d") - ("book" . "\xf007") - ("bookmark" . "\xf07b") - ("briefcase" . "\xf0d3") - ("broadcast" . "\xf048") - ("browser" . "\xf0c5") - ("bug" . "\xf091") - ("calendar" . "\xf068") - ("check" . "\xf03a") - ("checklist" . "\xf076") - ("chevron-down" . "\xf0a3") - ("chevron-left" . "\xf0a4") - ("chevron-right" . "\xf078") - ("chevron-up" . "\xf0a2") - ("circle-slash" . "\xf084") - ("circuit-board" . "\xf0d6") - ("clippy" . "\xf035") - ("clock" . "\xf046") - ("cloud-download" . "\xf00b") - ("cloud-upload" . "\xf00c") - ("code" . "\xf05f") - ("comment" . "\xf02b") - ("comment-discussion" . "\xf04f") - ("credit-card" . "\xf045") - ("dash" . "\xf0ca") - ("dashboard" . "\xf07d") - ("database" . "\xf096") - ("device-camera" . "\xf056") - ("device-camera-video" . "\xf057") - ("device-desktop" . "\xf27c") - ("device-mobile" . "\xf038") - ("diff" . "\xf04d") - ("diff-added" . "\xf06b") - ("diff-ignored" . "\xf099") - ("diff-modified" . "\xf06d") - ("diff-removed" . "\xf06c") - ("diff-renamed" . "\xf06e") - ("ellipsis" . "\xf09a") - ("eye" . "\xf04e") - ("file-binary" . "\xf094") - ("file-code" . "\xf010") - ("file-directory" . "\xf016") - ("file-media" . "\xf012") - ("file-pdf" . "\xf014") - ("file-submodule" . "\xf017") - ("file-symlink-directory" . "\xf0b1") - ("file-symlink-file" . "\xf0b0") - ("file-text" . "\xf011") - ("file-zip" . "\xf013") - ("flame" . "\xf0d2") - ("fold" . "\xf0cc") - ("gear" . "\xf02f") - ("gift" . "\xf042") - ("gist" . "\xf00e") - ("gist-secret" . "\xf08c") - ("git-branch" . "\xf020") - ("git-commit" . "\xf01f") - ("git-compare" . "\xf0ac") - ("git-merge" . "\xf023") - ("git-pull-request" . "\xf009") - ("globe" . "\xf0b6") - ("graph" . "\xf043") - ("beaker" . "\xf0dd") - ("heart" . "\x2665") - ("history" . "\xf07e") - ("home" . "\xf08d") - ("horizontal-rule" . "\xf070") - ("hourglass" . "\xf09e") - ("hubot" . "\xf09d") - ("inbox" . "\xf0cf") - ("info" . "\xf059") - ("issue-closed" . "\xf028") - ("issue-opened" . "\xf026") - ("issue-reopened" . "\xf027") - ("jersey" . "\xf019") - ("key" . "\xf049") - ("keyboard" . "\xf00d") - ("law" . "\xf0d8") - ("light-bulb" . "\xf000") - ("link" . "\xf05c") - ("link-external" . "\xf07f") - ("list-ordered" . "\xf062") - ("list-unordered" . "\xf061") - ("location" . "\xf060") - ("lock" . "\xf06a") - ("logo-github" . "\xf092") - ("mail" . "\xf03b") - ("mail-read" . "\xf03c") - ("mail-reply" . "\xf051") - ("mark-github" . "\xf00a") - ("markdown" . "\xf0c9") - ("megaphone" . "\xf077") - ("mention" . "\xf0be") - ("milestone" . "\xf075") - ("mirror" . "\xf024") - ("mortar-board" . "\xf0d7") - ("mute" . "\xf080") - ("no-newline" . "\xf09c") - ("octoface" . "\xf008") - ("organization" . "\xf037") - ("package" . "\xf0c4") - ("paintcan" . "\xf0d1") - ("pencil" . "\xf058") - ("person" . "\xf018") - ("pin" . "\xf041") - ("plug" . "\xf0d4") - ("plus" . "\xf05d") - ("primitive-dot" . "\xf052") - ("primitive-square" . "\xf053") - ("pulse" . "\xf085") - ("puzzle" . "\xf0c0") - ("question" . "\xf02c") - ("quote" . "\xf063") - ("radio-tower" . "\xf030") - ("repo" . "\xf001") - ("repo-clone" . "\xf04c") - ("repo-force-push" . "\xf04a") - ("repo-forked" . "\xf002") - ("repo-pull" . "\xf006") - ("repo-push" . "\xf005") - ("rocket" . "\xf033") - ("rss" . "\xf034") - ("ruby" . "\xf047") - ("search" . "\xf02e") - ("server" . "\xf097") - ("settings" . "\xf07c") - ("sign-in" . "\xf036") - ("sign-out" . "\xf032") - ("squirrel" . "\xf0b2") - ("star" . "\xf02a") - ("steps" . "\xf0c7") - ("stop" . "\xf08f") - ("sync" . "\xf087") - ("tag" . "\xf015") - ("telescope" . "\xf088") - ("terminal" . "\xf0c8") - ("three-bars" . "\xf05e") - ("thumbsdown" . "\xf0db") - ("thumbsup" . "\xf0da") - ("tools" . "\xf031") - ("trashcan" . "\xf0d0") - ("triangle-down" . "\xf05b") - ("triangle-left" . "\xf044") - ("triangle-right" . "\xf05a") - ("triangle-up" . "\xf0aa") - ("unfold" . "\xf039") - ("unmute" . "\xf0ba") - ("versions" . "\xf064") - ("x" . "\xf081") - ("zap" . "\x26A1") - - )) - -(provide 'data-octicons) blob - 676581c3c85c478e0553f5ad2e8ae1e9737af7b3 (mode 644) blob + /dev/null --- elpa/all-the-icons-5.0.0/data/data-weathericons.el +++ /dev/null @@ -1,594 +0,0 @@ -(defvar all-the-icons-data/weather-icons-alist - '( - - ("alien" . "\xf075") - ("barometer" . "\xf079") - ("celsius" . "\xf03c") - ("cloud" . "\xf041") - ("cloud-down" . "\xf03d") - ("cloud-refresh" . "\xf03e") - ("cloud-up" . "\xf040") - ("cloudy" . "\xf013") - ("cloudy-gusts" . "\xf011") - ("cloudy-windy" . "\xf012") - ("day-cloudy" . "\xf002") - ("day-cloudy-gusts" . "\xf000") - ("day-cloudy-high" . "\xf07d") - ("day-cloudy-windy" . "\xf001") - ("day-fog" . "\xf003") - ("day-hail" . "\xf004") - ("day-haze" . "\xf0b6") - ("day-light-wind" . "\xf0c4") - ("day-lightning" . "\xf005") - ("day-rain" . "\xf008") - ("day-rain-mix" . "\xf006") - ("day-rain-wind" . "\xf007") - ("day-showers" . "\xf009") - ("day-sleet" . "\xf0b2") - ("day-sleet-storm" . "\xf068") - ("day-snow" . "\xf00a") - ("day-snow-thunderstorm" . "\xf06b") - ("day-snow-wind" . "\xf065") - ("day-sprinkle" . "\xf00b") - ("day-storm-showers" . "\xf00e") - ("day-sunny" . "\xf00d") - ("day-sunny-overcast" . "\xf00c") - ("day-thunderstorm" . "\xf010") - ("day-windy" . "\xf085") - ("degrees" . "\xf042") - ("direction-down" . "\xf044") - ("direction-down-left" . "\xf043") - ("direction-down-right" . "\xf088") - ("direction-left" . "\xf048") - ("direction-right" . "\xf04d") - ("direction-up" . "\xf058") - ("direction-up-left" . "\xf087") - ("direction-up-right" . "\xf057") - ("dust" . "\xf063") - ("earthquake" . "\xf0c6") - ("fahrenheit" . "\xf045") - ("fire" . "\xf0c7") - ("flood" . "\xf07c") - ("fog" . "\xf014") - ("forecast-io-clear-day" . "\xf00d") - ("forecast-io-clear-night" . "\xf02e") - ("forecast-io-cloudy" . "\xf013") - ("forecast-io-fog" . "\xf014") - ("forecast-io-hail" . "\xf015") - ("forecast-io-partly-cloudy-day" . "\xf002") - ("forecast-io-partly-cloudy-night" . "\xf031") - ("forecast-io-rain" . "\xf019") - ("forecast-io-sleet" . "\xf0b5") - ("forecast-io-snow" . "\xf01b") - ("forecast-io-thunderstorm" . "\xf01e") - ("forecast-io-tornado" . "\xf056") - ("forecast-io-wind" . "\xf050") - ("gale-warning" . "\xf0cd") - ("hail" . "\xf015") - ("horizon" . "\xf047") - ("horizon-alt" . "\xf046") - ("hot" . "\xf072") - ("humidity" . "\xf07a") - ("hurricane" . "\xf073") - ("hurricane-warning" . "\xf0cf") - ("lightning" . "\xf016") - ("lunar-eclipse" . "\xf070") - ("meteor" . "\xf071") - ("moon-0" . "\xf095") - ("moon-1" . "\xf096") - ("moon-10" . "\xf09f") - ("moon-11" . "\xf0a0") - ("moon-12" . "\xf0a1") - ("moon-13" . "\xf0a2") - ("moon-14" . "\xf0a3") - ("moon-15" . "\xf0a4") - ("moon-16" . "\xf0a5") - ("moon-17" . "\xf0a6") - ("moon-18" . "\xf0a7") - ("moon-19" . "\xf0a8") - ("moon-2" . "\xf097") - ("moon-20" . "\xf0a9") - ("moon-21" . "\xf0aa") - ("moon-22" . "\xf0ab") - ("moon-23" . "\xf0ac") - ("moon-24" . "\xf0ad") - ("moon-25" . "\xf0ae") - ("moon-26" . "\xf0af") - ("moon-27" . "\xf0b0") - ("moon-3" . "\xf098") - ("moon-4" . "\xf099") - ("moon-5" . "\xf09a") - ("moon-6" . "\xf09b") - ("moon-7" . "\xf09c") - ("moon-8" . "\xf09d") - ("moon-9" . "\xf09e") - ("moon-alt-first-quarter" . "\xf0d6") - ("moon-alt-full" . "\xf0dd") - ("moon-alt-new" . "\xf0eb") - ("moon-alt-third-quarter" . "\xf0e4") - ("moon-alt-waning-crescent-1" . "\xf0e5") - ("moon-alt-waning-crescent-2" . "\xf0e6") - ("moon-alt-waning-crescent-3" . "\xf0e7") - ("moon-alt-waning-crescent-4" . "\xf0e8") - ("moon-alt-waning-crescent-5" . "\xf0e9") - ("moon-alt-waning-crescent-6" . "\xf0ea") - ("moon-alt-waning-gibbous-1" . "\xf0de") - ("moon-alt-waning-gibbous-2" . "\xf0df") - ("moon-alt-waning-gibbous-3" . "\xf0e0") - ("moon-alt-waning-gibbous-4" . "\xf0e1") - ("moon-alt-waning-gibbous-5" . "\xf0e2") - ("moon-alt-waning-gibbous-6" . "\xf0e3") - ("moon-alt-waxing-crescent-1" . "\xf0d0") - ("moon-alt-waxing-crescent-2" . "\xf0d1") - ("moon-alt-waxing-crescent-3" . "\xf0d2") - ("moon-alt-waxing-crescent-4" . "\xf0d3") - ("moon-alt-waxing-crescent-5" . "\xf0d4") - ("moon-alt-waxing-crescent-6" . "\xf0d5") - ("moon-alt-waxing-gibbous-1" . "\xf0d7") - ("moon-alt-waxing-gibbous-2" . "\xf0d8") - ("moon-alt-waxing-gibbous-3" . "\xf0d9") - ("moon-alt-waxing-gibbous-4" . "\xf0da") - ("moon-alt-waxing-gibbous-5" . "\xf0db") - ("moon-alt-waxing-gibbous-6" . "\xf0dc") - ("moon-first-quarter" . "\xf09c") - ("moon-full" . "\xf0a3") - ("moon-new" . "\xf095") - ("moon-third-quarter" . "\xf0aa") - ("moon-waning-crescent-1" . "\xf0ab") - ("moon-waning-crescent-2" . "\xf0ac") - ("moon-waning-crescent-3" . "\xf0ad") - ("moon-waning-crescent-4" . "\xf0ae") - ("moon-waning-crescent-5" . "\xf0af") - ("moon-waning-crescent-6" . "\xf0b0") - ("moon-waning-gibbous-1" . "\xf0a4") - ("moon-waning-gibbous-2" . "\xf0a5") - ("moon-waning-gibbous-3" . "\xf0a6") - ("moon-waning-gibbous-4" . "\xf0a7") - ("moon-waning-gibbous-5" . "\xf0a8") - ("moon-waning-gibbous-6" . "\xf0a9") - ("moon-waxing-crescent-1" . "\xf096") - ("moon-waxing-crescent-2" . "\xf097") - ("moon-waxing-crescent-3" . "\xf098") - ("moon-waxing-crescent-4" . "\xf099") - ("moon-waxing-crescent-5" . "\xf09a") - ("moon-waxing-crescent-6" . "\xf09b") - ("moon-waxing-gibbous-1" . "\xf09d") - ("moon-waxing-gibbous-2" . "\xf09e") - ("moon-waxing-gibbous-3" . "\xf09f") - ("moon-waxing-gibbous-4" . "\xf0a0") - ("moon-waxing-gibbous-5" . "\xf0a1") - ("moon-waxing-gibbous-6" . "\xf0a2") - ("moonrise" . "\xf0c9") - ("moonset" . "\xf0ca") - ("na" . "\xf07b") - ("night-alt-cloudy" . "\xf086") - ("night-alt-cloudy-gusts" . "\xf022") - ("night-alt-cloudy-high" . "\xf07e") - ("night-alt-cloudy-windy" . "\xf023") - ("night-alt-hail" . "\xf024") - ("night-alt-lightning" . "\xf025") - ("night-alt-partly-cloudy" . "\xf081") - ("night-alt-rain" . "\xf028") - ("night-alt-rain-mix" . "\xf026") - ("night-alt-rain-wind" . "\xf027") - ("night-alt-showers" . "\xf029") - ("night-alt-sleet" . "\xf0b4") - ("night-alt-sleet-storm" . "\xf06a") - ("night-alt-snow" . "\xf02a") - ("night-alt-snow-thunderstorm" . "\xf06d") - ("night-alt-snow-wind" . "\xf067") - ("night-alt-sprinkle" . "\xf02b") - ("night-alt-storm-showers" . "\xf02c") - ("night-alt-thunderstorm" . "\xf02d") - ("night-clear" . "\xf02e") - ("night-cloudy" . "\xf031") - ("night-cloudy-gusts" . "\xf02f") - ("night-cloudy-high" . "\xf080") - ("night-cloudy-windy" . "\xf030") - ("night-fog" . "\xf04a") - ("night-hail" . "\xf032") - ("night-lightning" . "\xf033") - ("night-partly-cloudy" . "\xf083") - ("night-rain" . "\xf036") - ("night-rain-mix" . "\xf034") - ("night-rain-wind" . "\xf035") - ("night-showers" . "\xf037") - ("night-sleet" . "\xf0b3") - ("night-sleet-storm" . "\xf069") - ("night-snow" . "\xf038") - ("night-snow-thunderstorm" . "\xf06c") - ("night-snow-wind" . "\xf066") - ("night-sprinkle" . "\xf039") - ("night-storm-showers" . "\xf03a") - ("night-thunderstorm" . "\xf03b") - ("owm-200" . "\xf01e") - ("owm-201" . "\xf01e") - ("owm-202" . "\xf01e") - ("owm-210" . "\xf016") - ("owm-211" . "\xf016") - ("owm-212" . "\xf016") - ("owm-221" . "\xf016") - ("owm-230" . "\xf01e") - ("owm-231" . "\xf01e") - ("owm-232" . "\xf01e") - ("owm-300" . "\xf01c") - ("owm-301" . "\xf01c") - ("owm-302" . "\xf019") - ("owm-310" . "\xf017") - ("owm-311" . "\xf019") - ("owm-312" . "\xf019") - ("owm-313" . "\xf01a") - ("owm-314" . "\xf019") - ("owm-321" . "\xf01c") - ("owm-500" . "\xf01c") - ("owm-501" . "\xf019") - ("owm-502" . "\xf019") - ("owm-503" . "\xf019") - ("owm-504" . "\xf019") - ("owm-511" . "\xf017") - ("owm-520" . "\xf01a") - ("owm-521" . "\xf01a") - ("owm-522" . "\xf01a") - ("owm-531" . "\xf01d") - ("owm-600" . "\xf01b") - ("owm-601" . "\xf01b") - ("owm-602" . "\xf0b5") - ("owm-611" . "\xf017") - ("owm-612" . "\xf017") - ("owm-615" . "\xf017") - ("owm-616" . "\xf017") - ("owm-620" . "\xf017") - ("owm-621" . "\xf01b") - ("owm-622" . "\xf01b") - ("owm-701" . "\xf01a") - ("owm-711" . "\xf062") - ("owm-721" . "\xf0b6") - ("owm-731" . "\xf063") - ("owm-741" . "\xf014") - ("owm-761" . "\xf063") - ("owm-762" . "\xf063") - ("owm-771" . "\xf011") - ("owm-781" . "\xf056") - ("owm-800" . "\xf00d") - ("owm-801" . "\xf011") - ("owm-802" . "\xf011") - ("owm-803" . "\xf012") - ("owm-804" . "\xf013") - ("owm-900" . "\xf056") - ("owm-901" . "\xf01d") - ("owm-902" . "\xf073") - ("owm-903" . "\xf076") - ("owm-904" . "\xf072") - ("owm-905" . "\xf021") - ("owm-906" . "\xf015") - ("owm-957" . "\xf050") - ("owm-day-200" . "\xf010") - ("owm-day-201" . "\xf010") - ("owm-day-202" . "\xf010") - ("owm-day-210" . "\xf005") - ("owm-day-211" . "\xf005") - ("owm-day-212" . "\xf005") - ("owm-day-221" . "\xf005") - ("owm-day-230" . "\xf010") - ("owm-day-231" . "\xf010") - ("owm-day-232" . "\xf010") - ("owm-day-300" . "\xf00b") - ("owm-day-301" . "\xf00b") - ("owm-day-302" . "\xf008") - ("owm-day-310" . "\xf008") - ("owm-day-311" . "\xf008") - ("owm-day-312" . "\xf008") - ("owm-day-313" . "\xf008") - ("owm-day-314" . "\xf008") - ("owm-day-321" . "\xf00b") - ("owm-day-500" . "\xf00b") - ("owm-day-501" . "\xf008") - ("owm-day-502" . "\xf008") - ("owm-day-503" . "\xf008") - ("owm-day-504" . "\xf008") - ("owm-day-511" . "\xf006") - ("owm-day-520" . "\xf009") - ("owm-day-521" . "\xf009") - ("owm-day-522" . "\xf009") - ("owm-day-531" . "\xf00e") - ("owm-day-600" . "\xf00a") - ("owm-day-601" . "\xf0b2") - ("owm-day-602" . "\xf00a") - ("owm-day-611" . "\xf006") - ("owm-day-612" . "\xf006") - ("owm-day-615" . "\xf006") - ("owm-day-616" . "\xf006") - ("owm-day-620" . "\xf006") - ("owm-day-621" . "\xf00a") - ("owm-day-622" . "\xf00a") - ("owm-day-701" . "\xf009") - ("owm-day-711" . "\xf062") - ("owm-day-721" . "\xf0b6") - ("owm-day-731" . "\xf063") - ("owm-day-741" . "\xf003") - ("owm-day-761" . "\xf063") - ("owm-day-762" . "\xf063") - ("owm-day-781" . "\xf056") - ("owm-day-800" . "\xf00d") - ("owm-day-801" . "\xf000") - ("owm-day-802" . "\xf000") - ("owm-day-803" . "\xf000") - ("owm-day-804" . "\xf00c") - ("owm-day-900" . "\xf056") - ("owm-day-902" . "\xf073") - ("owm-day-903" . "\xf076") - ("owm-day-904" . "\xf072") - ("owm-day-906" . "\xf004") - ("owm-day-957" . "\xf050") - ("owm-night-200" . "\xf02d") - ("owm-night-201" . "\xf02d") - ("owm-night-202" . "\xf02d") - ("owm-night-210" . "\xf025") - ("owm-night-211" . "\xf025") - ("owm-night-212" . "\xf025") - ("owm-night-221" . "\xf025") - ("owm-night-230" . "\xf02d") - ("owm-night-231" . "\xf02d") - ("owm-night-232" . "\xf02d") - ("owm-night-300" . "\xf02b") - ("owm-night-301" . "\xf02b") - ("owm-night-302" . "\xf028") - ("owm-night-310" . "\xf028") - ("owm-night-311" . "\xf028") - ("owm-night-312" . "\xf028") - ("owm-night-313" . "\xf028") - ("owm-night-314" . "\xf028") - ("owm-night-321" . "\xf02b") - ("owm-night-500" . "\xf02b") - ("owm-night-501" . "\xf028") - ("owm-night-502" . "\xf028") - ("owm-night-503" . "\xf028") - ("owm-night-504" . "\xf028") - ("owm-night-511" . "\xf026") - ("owm-night-520" . "\xf029") - ("owm-night-521" . "\xf029") - ("owm-night-522" . "\xf029") - ("owm-night-531" . "\xf02c") - ("owm-night-600" . "\xf02a") - ("owm-night-601" . "\xf0b4") - ("owm-night-602" . "\xf02a") - ("owm-night-611" . "\xf026") - ("owm-night-612" . "\xf026") - ("owm-night-615" . "\xf026") - ("owm-night-616" . "\xf026") - ("owm-night-620" . "\xf026") - ("owm-night-621" . "\xf02a") - ("owm-night-622" . "\xf02a") - ("owm-night-701" . "\xf029") - ("owm-night-711" . "\xf062") - ("owm-night-721" . "\xf0b6") - ("owm-night-731" . "\xf063") - ("owm-night-741" . "\xf04a") - ("owm-night-761" . "\xf063") - ("owm-night-762" . "\xf063") - ("owm-night-781" . "\xf056") - ("owm-night-800" . "\xf02e") - ("owm-night-801" . "\xf022") - ("owm-night-802" . "\xf022") - ("owm-night-803" . "\xf022") - ("owm-night-804" . "\xf086") - ("owm-night-900" . "\xf056") - ("owm-night-902" . "\xf073") - ("owm-night-903" . "\xf076") - ("owm-night-904" . "\xf072") - ("owm-night-906" . "\xf024") - ("owm-night-957" . "\xf050") - ("rain" . "\xf019") - ("rain-mix" . "\xf017") - ("rain-wind" . "\xf018") - ("raindrop" . "\xf078") - ("raindrops" . "\xf04e") - ("refresh" . "\xf04c") - ("refresh-alt" . "\xf04b") - ("sandstorm" . "\xf082") - ("showers" . "\xf01a") - ("sleet" . "\xf0b5") - ("small-craft-advisory" . "\xf0cc") - ("smog" . "\xf074") - ("smoke" . "\xf062") - ("snow" . "\xf01b") - ("snow" . "\xf01b") - ("snow-wind" . "\xf064") - ("snowflake-cold" . "\xf076") - ("solar-eclipse" . "\xf06e") - ("sprinkle" . "\xf01c") - ("stars" . "\xf077") - ("storm-showers" . "\xf01d") - ("storm-showers" . "\xf01d") - ("storm-warning" . "\xf0ce") - ("strong-wind" . "\xf050") - ("sunrise" . "\xf051") - ("sunset" . "\xf052") - ("thermometer" . "\xf055") - ("thermometer-exterior" . "\xf053") - ("thermometer-internal" . "\xf054") - ("thunderstorm" . "\xf01e") - ("thunderstorm" . "\xf01e") - ("time-1" . "\xf08a") - ("time-10" . "\xf093") - ("time-11" . "\xf094") - ("time-12" . "\xf089") - ("time-2" . "\xf08b") - ("time-3" . "\xf08c") - ("time-4" . "\xf08d") - ("time-5" . "\xf08e") - ("time-6" . "\xf08f") - ("time-7" . "\xf090") - ("time-8" . "\xf091") - ("time-9" . "\xf092") - ("tornado" . "\xf056") - ("train" . "\xf0cb") - ("tsunami" . "\xf0c5") - ("umbrella" . "\xf084") - ("volcano" . "\xf0c8") - ("wind-beaufort-0" . "\xf0b7") - ("wind-beaufort-1" . "\xf0b8") - ("wind-beaufort-10" . "\xf0c1") - ("wind-beaufort-11" . "\xf0c2") - ("wind-beaufort-12" . "\xf0c3") - ("wind-beaufort-2" . "\xf0b9") - ("wind-beaufort-3" . "\xf0ba") - ("wind-beaufort-4" . "\xf0bb") - ("wind-beaufort-5" . "\xf0bc") - ("wind-beaufort-6" . "\xf0bd") - ("wind-beaufort-7" . "\xf0be") - ("wind-beaufort-8" . "\xf0bf") - ("wind-beaufort-9" . "\xf0c0") - ("wind-direction" . "\xf0b1") - ("windy" . "\xf021") - ("wmo4680-00" . "\xf055") - ("wmo4680-01" . "\xf013") - ("wmo4680-02" . "\xf055") - ("wmo4680-03" . "\xf013") - ("wmo4680-04" . "\xf014") - ("wmo4680-05" . "\xf014") - ("wmo4680-10" . "\xf014") - ("wmo4680-11" . "\xf014") - ("wmo4680-12" . "\xf016") - ("wmo4680-18" . "\xf050") - ("wmo4680-20" . "\xf014") - ("wmo4680-21" . "\xf017") - ("wmo4680-22" . "\xf017") - ("wmo4680-23" . "\xf019") - ("wmo4680-24" . "\xf01b") - ("wmo4680-25" . "\xf015") - ("wmo4680-26" . "\xf01e") - ("wmo4680-27" . "\xf063") - ("wmo4680-28" . "\xf063") - ("wmo4680-29" . "\xf063") - ("wmo4680-30" . "\xf014") - ("wmo4680-31" . "\xf014") - ("wmo4680-32" . "\xf014") - ("wmo4680-33" . "\xf014") - ("wmo4680-34" . "\xf014") - ("wmo4680-35" . "\xf014") - ("wmo4680-40" . "\xf017") - ("wmo4680-41" . "\xf01c") - ("wmo4680-42" . "\xf019") - ("wmo4680-43" . "\xf01c") - ("wmo4680-44" . "\xf019") - ("wmo4680-45" . "\xf015") - ("wmo4680-46" . "\xf015") - ("wmo4680-47" . "\xf01b") - ("wmo4680-48" . "\xf01b") - ("wmo4680-50" . "\xf01c") - ("wmo4680-51" . "\xf01c") - ("wmo4680-52" . "\xf019") - ("wmo4680-53" . "\xf019") - ("wmo4680-54" . "\xf076") - ("wmo4680-55" . "\xf076") - ("wmo4680-56" . "\xf076") - ("wmo4680-57" . "\xf01c") - ("wmo4680-58" . "\xf019") - ("wmo4680-60" . "\xf01c") - ("wmo4680-61" . "\xf01c") - ("wmo4680-62" . "\xf019") - ("wmo4680-63" . "\xf019") - ("wmo4680-64" . "\xf015") - ("wmo4680-65" . "\xf015") - ("wmo4680-66" . "\xf015") - ("wmo4680-67" . "\xf017") - ("wmo4680-68" . "\xf017") - ("wmo4680-70" . "\xf01b") - ("wmo4680-71" . "\xf01b") - ("wmo4680-72" . "\xf01b") - ("wmo4680-73" . "\xf01b") - ("wmo4680-74" . "\xf076") - ("wmo4680-75" . "\xf076") - ("wmo4680-76" . "\xf076") - ("wmo4680-77" . "\xf01b") - ("wmo4680-78" . "\xf076") - ("wmo4680-80" . "\xf019") - ("wmo4680-81" . "\xf01c") - ("wmo4680-82" . "\xf019") - ("wmo4680-83" . "\xf019") - ("wmo4680-84" . "\xf01d") - ("wmo4680-85" . "\xf017") - ("wmo4680-86" . "\xf017") - ("wmo4680-87" . "\xf017") - ("wmo4680-89" . "\xf015") - ("wmo4680-90" . "\xf016") - ("wmo4680-91" . "\xf01d") - ("wmo4680-92" . "\xf01e") - ("wmo4680-93" . "\xf01e") - ("wmo4680-94" . "\xf016") - ("wmo4680-95" . "\xf01e") - ("wmo4680-96" . "\xf01e") - ("wmo4680-99" . "\xf056") - ("wu-chanceflurries" . "\xf064") - ("wu-chancerain" . "\xf019") - ("wu-chancesleat" . "\xf0b5") - ("wu-chancesnow" . "\xf01b") - ("wu-chancetstorms" . "\xf01e") - ("wu-clear" . "\xf00d") - ("wu-cloudy" . "\xf002") - ("wu-flurries" . "\xf064") - ("wu-hazy" . "\xf0b6") - ("wu-mostlycloudy" . "\xf002") - ("wu-mostlysunny" . "\xf00d") - ("wu-partlycloudy" . "\xf002") - ("wu-partlysunny" . "\xf00d") - ("wu-rain" . "\xf01a") - ("wu-sleat" . "\xf0b5") - ("wu-snow" . "\xf01b") - ("wu-sunny" . "\xf00d") - ("wu-tstorms" . "\xf01e") - ("wu-unknown" . "\xf00d") - ("yahoo-0" . "\xf056") - ("yahoo-1" . "\xf00e") - ("yahoo-10" . "\xf015") - ("yahoo-11" . "\xf01a") - ("yahoo-12" . "\xf01a") - ("yahoo-13" . "\xf01b") - ("yahoo-14" . "\xf00a") - ("yahoo-15" . "\xf064") - ("yahoo-16" . "\xf01b") - ("yahoo-17" . "\xf015") - ("yahoo-18" . "\xf017") - ("yahoo-19" . "\xf063") - ("yahoo-2" . "\xf073") - ("yahoo-20" . "\xf014") - ("yahoo-21" . "\xf021") - ("yahoo-22" . "\xf062") - ("yahoo-23" . "\xf050") - ("yahoo-24" . "\xf050") - ("yahoo-25" . "\xf076") - ("yahoo-26" . "\xf013") - ("yahoo-27" . "\xf031") - ("yahoo-28" . "\xf002") - ("yahoo-29" . "\xf031") - ("yahoo-3" . "\xf01e") - ("yahoo-30" . "\xf002") - ("yahoo-31" . "\xf02e") - ("yahoo-32" . "\xf00d") - ("yahoo-3200" . "\xf077") - ("yahoo-33" . "\xf083") - ("yahoo-34" . "\xf00c") - ("yahoo-35" . "\xf017") - ("yahoo-36" . "\xf072") - ("yahoo-37" . "\xf00e") - ("yahoo-38" . "\xf00e") - ("yahoo-39" . "\xf00e") - ("yahoo-4" . "\xf01e") - ("yahoo-40" . "\xf01a") - ("yahoo-41" . "\xf064") - ("yahoo-42" . "\xf01b") - ("yahoo-43" . "\xf064") - ("yahoo-44" . "\xf00c") - ("yahoo-45" . "\xf00e") - ("yahoo-46" . "\xf01b") - ("yahoo-47" . "\xf00e") - ("yahoo-5" . "\xf017") - ("yahoo-6" . "\xf017") - ("yahoo-7" . "\xf017") - ("yahoo-8" . "\xf015") - ("yahoo-9" . "\xf01a") - - )) - -(provide 'data-weathericons) blob - 09ebb099140de677ecd22d1a8ab32c726f7cb5f2 (mode 644) blob + /dev/null --- elpa/all-the-icons-completion-1.0/all-the-icons-completion-autoloads.el +++ /dev/null @@ -1,57 +0,0 @@ -;;; all-the-icons-completion-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from all-the-icons-completion.el - -(autoload 'all-the-icons-completion-marginalia-setup "all-the-icons-completion" "\ -Hook to `marginalia-mode-hook' to bind `all-the-icons-completion-mode' to it.") -(defvar all-the-icons-completion-mode nil "\ -Non-nil if All-The-Icons-Completion mode is enabled. -See the `all-the-icons-completion-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `all-the-icons-completion-mode'.") -(custom-autoload 'all-the-icons-completion-mode "all-the-icons-completion" nil) -(autoload 'all-the-icons-completion-mode "all-the-icons-completion" "\ -Add icons to completion candidates. - -This is a global minor mode. If called interactively, toggle the -`All-The-Icons-Completion mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='all-the-icons-completion-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "all-the-icons-completion" '("all-the-icons-completion-completion-metadata-get")) - -;;; End of scraped data - -(provide 'all-the-icons-completion-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; all-the-icons-completion-autoloads.el ends here blob - 94016a5c65918c9623bd70a2be06111fa2d8b1c9 (mode 644) blob + /dev/null --- elpa/all-the-icons-completion-1.0/all-the-icons-completion-pkg.el +++ /dev/null @@ -1,15 +0,0 @@ -(define-package "all-the-icons-completion" "1.0" "Add icons to completion candidates" - '((emacs "26.1") - (all-the-icons "5.0")) - :commit "8eb3e410d63f5d0657b41829e7898793e81f31c0" :authors - '(("Itai Y. Efrat ")) - :maintainers - '(("Itai Y. Efrat" . "itai3397@gmail.com")) - :maintainer - '("Itai Y. Efrat" . "itai3397@gmail.com") - :keywords - '("convenient" "lisp") - :url "https://github.com/iyefrat/all-the-icons-completion") -;; Local Variables: -;; no-byte-compile: t -;; End: blob - 4f1c910326b7401a052ab7de0ecc48be152fcb6a (mode 644) blob + /dev/null --- elpa/all-the-icons-completion-1.0/all-the-icons-completion.el +++ /dev/null @@ -1,151 +0,0 @@ -;;; all-the-icons-completion.el --- Add icons to completion candidates -*- lexical-binding: t; -*- -;; -;; Copyright (C) 2021 Itai Y. Efrat -;; -;; Author: Itai Y. Efrat -;; Maintainer: Itai Y. Efrat -;; Created: June 06, 2021 -;; Modified: June 06, 2021 -;; Version: 1.0 -;; Keywords: convenient, lisp -;; Homepage: https://github.com/iyefrat/all-the-icons-completion -;; Package-Requires: ((emacs "26.1") (all-the-icons "5.0")) -;; -;; This file is not part of GNU Emacs. -;; -;; Licence: -;; -;; This program 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 of the License, or -;; (at your option) any later version. -;; -;; This program 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 this program. If not, see . -;; -;;; Commentary: -;; -;; Add icons to completion candidates. -;; -;;; Code: - -(require 'all-the-icons) - -(defgroup all-the-icons-completion nil - "Add icons to completion candidates." - :version "26.1" - :group 'appearance - :group 'convenience - :prefix "all-the-icons-completion") - -(defface all-the-icons-completion-dir-face - '((t nil)) - "Face for the directory icon." - :group 'all-the-icons-faces) - -(cl-defgeneric all-the-icons-completion-get-icon (_cand _cat) - "Return the icon for the candidate CAND of completion category CAT." - "") - -(cl-defmethod all-the-icons-completion-get-icon (cand (_cat (eql file))) - "Return the icon for the candidate CAND of completion category file." - (cond ((string-match-p "\\/$" cand) - (concat - (all-the-icons-icon-for-dir cand :face 'all-the-icons-completion-dir-face) - " ")) - (t (concat (all-the-icons-icon-for-file cand) " ")))) - -(cl-defmethod all-the-icons-completion-get-icon (cand (_cat (eql project-file))) - "Return the icon for the candidate CAND of completion category project-file." - (all-the-icons-completion-get-icon cand 'file)) - -(cl-defmethod all-the-icons-completion-get-icon (cand (_cat (eql buffer))) - "Return the icon for the candidate CAND of completion category buffer." - (let* ((mode (buffer-local-value 'major-mode (get-buffer cand))) - (icon (all-the-icons-icon-for-mode mode)) - (parent-icon (all-the-icons-icon-for-mode - (get mode 'derived-mode-parent)))) - (concat - (if (symbolp icon) - (if (symbolp parent-icon) - (all-the-icons-faicon "sticky-note-o") - parent-icon) - icon) - " "))) - -(autoload 'bookmark-get-filename "bookmark") -(cl-defmethod all-the-icons-completion-get-icon (cand (_cat (eql bookmark))) - "Return the icon for the candidate CAND of completion category bookmark." - (if-let (fname (bookmark-get-filename cand)) - (all-the-icons-completion-get-icon fname 'file) - (concat (all-the-icons-octicon "bookmark" :face 'all-the-icons-completion-dir-face) " "))) - -(defun all-the-icons-completion-completion-metadata-get (orig metadata prop) - "Meant as :around advice for `completion-metadata-get', Add icons as prefix. -ORIG should be `completion-metadata-get' -METADATA is the metadata. -PROP is the property which is looked up." - (if (eq prop 'affixation-function) - (let ((cat (funcall orig metadata 'category)) - (aff (or (funcall orig metadata 'affixation-function) - (when-let ((ann (funcall orig metadata 'annotation-function))) - (lambda (cands) - (mapcar (lambda (x) (list x "" (funcall ann x))) cands)))))) - (cond - ((and (eq cat 'multi-category) aff) - (lambda (cands) - (mapcar (lambda (x) - (pcase-exhaustive x - (`(,cand ,prefix ,suffix) - (let ((orig (get-text-property 0 'multi-category cand))) - (list cand - (concat (all-the-icons-completion-get-icon (cdr orig) (car orig)) - prefix) - suffix))))) - (funcall aff cands)))) - ((and cat aff) - (lambda (cands) - (mapcar (lambda (x) - (pcase-exhaustive x - (`(,cand ,prefix ,suffix) - (list cand - (concat (all-the-icons-completion-get-icon cand cat) - prefix) - suffix)))) - (funcall aff cands)))) - ((eq cat 'multi-category) - (lambda (cands) - (mapcar (lambda (x) - (let ((orig (get-text-property 0 'multi-category x))) - (list x (all-the-icons-completion-get-icon (cdr orig) (car orig)) ""))) - cands))) - (cat - (lambda (cands) - (mapcar (lambda (x) - (list x (all-the-icons-completion-get-icon x cat) "")) - cands))) - (aff))) - (funcall orig metadata prop))) - -;; For the byte compiler -(defvar marginalia-mode) -;;;###autoload -(defun all-the-icons-completion-marginalia-setup () - "Hook to `marginalia-mode-hook' to bind `all-the-icons-completion-mode' to it." - (all-the-icons-completion-mode (if marginalia-mode 1 -1))) - -;;;###autoload -(define-minor-mode all-the-icons-completion-mode - "Add icons to completion candidates." - :global t - (if all-the-icons-completion-mode - (advice-add #'completion-metadata-get :around #'all-the-icons-completion-completion-metadata-get) - (advice-remove #'completion-metadata-get #'all-the-icons-completion-completion-metadata-get))) - -(provide 'all-the-icons-completion) -;;; all-the-icons-completion.el ends here blob - dbd25abd3f54dc51f038365e1ba537da36be6201 (mode 644) blob + /dev/null --- elpa/all-the-icons-dired-2.0/all-the-icons-dired-autoloads.el +++ /dev/null @@ -1,47 +0,0 @@ -;;; all-the-icons-dired-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from all-the-icons-dired.el - -(autoload 'all-the-icons-dired-mode "all-the-icons-dired" "\ -Display all-the-icons icon for each file in a dired buffer. - -This is a minor mode. If called interactively, toggle the -`All-The-Icons-Dired mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `all-the-icons-dired-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "all-the-icons-dired" '("all-the-icons-dired-")) - -;;; End of scraped data - -(provide 'all-the-icons-dired-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; all-the-icons-dired-autoloads.el ends here blob - c42aceee75af7c62e29f3011d148a3ab29db2e8d (mode 644) blob + /dev/null --- elpa/all-the-icons-dired-2.0/all-the-icons-dired-pkg.el +++ /dev/null @@ -1,15 +0,0 @@ -(define-package "all-the-icons-dired" "2.0" "Shows icons for each file in dired mode" - '((emacs "24.4") - (all-the-icons "2.2.0")) - :commit "a758766878b6e8b9eaaf41d68599a2df99e37f48" :authors - '(("jtbm37")) - :maintainers - '(("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com")) - :maintainer - '("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com") - :keywords - '("files" "icons" "dired") - :url "https://github.com/wyuenho/all-the-icons-dired") -;; Local Variables: -;; no-byte-compile: t -;; End: blob - 625b027256ddf8f29b9c3c5b48933db463d3457b (mode 644) blob + /dev/null --- elpa/all-the-icons-dired-2.0/all-the-icons-dired.el +++ /dev/null @@ -1,153 +0,0 @@ -;;; all-the-icons-dired.el --- Shows icons for each file in dired mode -*- lexical-binding: t; -*- - -;; Copyright (C) 2016-2020 jtbm37 -;; Copyright (C) 2021 Jimmy Yuen Ho Wong - -;; Author: jtbm37 -;; Maintainer: Jimmy Yuen Ho Wong -;; Version: 2.0 -;; Keywords: files icons dired -;; Package-Requires: ((emacs "24.4") (all-the-icons "2.2.0")) -;; URL: https://github.com/wyuenho/all-the-icons-dired - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: -;; To use this package, simply add this to your init.el: -;; (add-hook 'dired-mode-hook 'all-the-icons-dired-mode) - -;; To manually install, add this to your init.el before the hook mentioned above. -;; (add-to-load-path (expand-file-name "~/path/to/all-the-icons-dired")) -;; (load "all-the-icons-dired.el") - - -;;; Code: - -(require 'cl-lib) -(require 'dired) -(require 'all-the-icons) -(require 'subr-x) - -(defface all-the-icons-dired-dir-face - '((((background dark)) :foreground "white") - (((background light)) :foreground "black")) - "Face for the directory icon" - :group 'all-the-icons-faces) - -(defcustom all-the-icons-dired-v-adjust 0.01 - "The default vertical adjustment of the icon in the dired buffer." - :group 'all-the-icons - :type 'number) - -(defcustom all-the-icons-dired-monochrome t - "Whether to show the icons as the same color as the text on the same line." - :group 'all-the-icons - :type 'boolean) - -(defvar all-the-icons-dired-mode) - -(defun all-the-icons-dired--add-overlay (pos string) - "Add overlay to display STRING at POS." - (let ((ov (make-overlay (1- pos) pos))) - (overlay-put ov 'all-the-icons-dired-overlay t) - (overlay-put ov 'after-string string))) - -(defun all-the-icons-dired--overlays-in (beg end) - "Get all all-the-icons-dired overlays between BEG to END." - (cl-remove-if-not - (lambda (ov) - (overlay-get ov 'all-the-icons-dired-overlay)) - (overlays-in beg end))) - -(defun all-the-icons-dired--overlays-at (pos) - "Get all-the-icons-dired overlays at POS." - (apply #'all-the-icons-dired--overlays-in `(,pos ,pos))) - -(defun all-the-icons-dired--remove-all-overlays () - "Remove all `all-the-icons-dired' overlays." - (save-restriction - (widen) - (mapc #'delete-overlay - (all-the-icons-dired--overlays-in (point-min) (point-max))))) - -(defun all-the-icons-dired--refresh () - "Display the icons of files in a dired buffer." - (all-the-icons-dired--remove-all-overlays) - (save-excursion - (goto-char (point-min)) - (while (not (eobp)) - (when (dired-move-to-filename nil) - (let ((case-fold-search t)) - (when-let* ((file (dired-get-filename 'relative 'noerror)) - (icon (if (file-directory-p file) - (all-the-icons-icon-for-dir file - :face 'all-the-icons-dired-dir-face - :v-adjust all-the-icons-dired-v-adjust) - (apply 'all-the-icons-icon-for-file file - (append - `(:v-adjust ,all-the-icons-dired-v-adjust) - (when all-the-icons-dired-monochrome - `(:face ,(face-at-point)))))))) - (if (member file '("." "..")) - (all-the-icons-dired--add-overlay (point) " \t") - (all-the-icons-dired--add-overlay (point) (concat icon "\t")))))) - (forward-line 1)))) - -(defun all-the-icons-dired--refresh-advice (fn &rest args) - "Advice function for FN with ARGS." - (apply fn args) - (when all-the-icons-dired-mode - (all-the-icons-dired--refresh))) - -(defvar all-the-icons-dired-advice-alist - '((dired-aux dired-create-directory all-the-icons-dired--refresh-advice) - (dired-aux dired-do-create-files all-the-icons-dired--refresh-advice) - (dired-aux dired-do-kill-lines all-the-icons-dired--refresh-advice) - (dired-aux dired-do-rename all-the-icons-dired--refresh-advice) - (dired-aux dired-insert-subdir all-the-icons-dired--refresh-advice) - (dired wdired-abort-changes all-the-icons-dired--refresh-advice) - (dired dired-internal-do-deletions all-the-icons-dired--refresh-advice) - (dired-narrow dired-narrow--internal all-the-icons-dired--refresh-advice) - (dired dired-readin all-the-icons-dired--refresh-advice) - (dired dired-revert all-the-icons-dired--refresh-advice) - (find-dired find-dired-sentinel all-the-icons-dired--refresh-advice)) - "A list of file, adviced function, and advice function.") - -(defun all-the-icons-dired--setup () - "Setup `all-the-icons-dired'." - (setq-local tab-width 1) - (pcase-dolist (`(,file ,sym ,fn) all-the-icons-dired-advice-alist) - (with-eval-after-load file - (advice-add sym :around fn))) - (all-the-icons-dired--refresh)) - -(defun all-the-icons-dired--teardown () - "Functions used as advice when redisplaying buffer." - (kill-local-variable 'tab-width) - (pcase-dolist (`(,file ,sym ,fn) all-the-icons-dired-advice-alist) - (with-eval-after-load file - (advice-remove sym fn))) - (all-the-icons-dired--remove-all-overlays)) - -;;;###autoload -(define-minor-mode all-the-icons-dired-mode - "Display all-the-icons icon for each file in a dired buffer." - :lighter " all-the-icons-dired-mode" - (when (derived-mode-p 'dired-mode) - (if all-the-icons-dired-mode - (all-the-icons-dired--setup) - (all-the-icons-dired--teardown)))) - -(provide 'all-the-icons-dired) -;;; all-the-icons-dired.el ends here blob - c35c71285aa187d07877b351f2874bfb93f24502 (mode 644) blob + /dev/null --- elpa/all-the-icons-ibuffer-1.3.0/all-the-icons-ibuffer-autoloads.el +++ /dev/null @@ -1,59 +0,0 @@ -;;; all-the-icons-ibuffer-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from all-the-icons-ibuffer.el - -(autoload 'ibuffer-make-column-icon "all-the-icons-ibuffer") -(autoload 'ibuffer-make-column-size-h "all-the-icons-ibuffer") -(autoload 'ibuffer-make-column-mode+ "all-the-icons-ibuffer") -(autoload 'ibuffer-make-column-filename-and-process+ "all-the-icons-ibuffer") -(defvar all-the-icons-ibuffer-mode nil "\ -Non-nil if All-The-Icons-Ibuffer mode is enabled. -See the `all-the-icons-ibuffer-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `all-the-icons-ibuffer-mode'.") -(custom-autoload 'all-the-icons-ibuffer-mode "all-the-icons-ibuffer" nil) -(autoload 'all-the-icons-ibuffer-mode "all-the-icons-ibuffer" "\ -Display icons for all buffers in ibuffer. - -This is a global minor mode. If called interactively, toggle the -`All-The-Icons-Ibuffer mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='all-the-icons-ibuffer-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "all-the-icons-ibuffer" '("all-the-icons-ibuffer-")) - -;;; End of scraped data - -(provide 'all-the-icons-ibuffer-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; all-the-icons-ibuffer-autoloads.el ends here blob - a79de10a051cf096f0029a7b048b5ecad80efe47 (mode 644) blob + /dev/null --- elpa/all-the-icons-ibuffer-1.3.0/all-the-icons-ibuffer-pkg.el +++ /dev/null @@ -1,15 +0,0 @@ -(define-package "all-the-icons-ibuffer" "1.3.0" "Display icons for all buffers in ibuffer" - '((emacs "24.4") - (all-the-icons "2.2.0")) - :commit "767b52186c1d9ef52f087f34a48af39c31e45b73" :authors - '(("Vincent Zhang" . "seagle0128@gmail.com")) - :maintainers - '(("Vincent Zhang" . "seagle0128@gmail.com")) - :maintainer - '("Vincent Zhang" . "seagle0128@gmail.com") - :keywords - '("convenience" "icons" "ibuffer") - :url "https://github.com/seagle0128/all-the-icons-ibuffer") -;; Local Variables: -;; no-byte-compile: t -;; End: blob - e1ce644d094ecc2846826e00d93d74e2402d5f5e (mode 644) blob + /dev/null --- elpa/all-the-icons-ibuffer-1.3.0/all-the-icons-ibuffer.el +++ /dev/null @@ -1,205 +0,0 @@ -;;; all-the-icons-ibuffer.el --- Display icons for all buffers in ibuffer -*- lexical-binding: t; -*- - -;; Copyright (C) 2020 Vincent Zhang - -;; Author: Vincent Zhang -;; Homepage: https://github.com/seagle0128/all-the-icons-ibuffer -;; Version: 1.3.0 -;; Package-Requires: ((emacs "24.4") (all-the-icons "2.2.0")) -;; Keywords: convenience, icons, ibuffer - -;; This file is not part of GNU Emacs. - -;; -;; This program 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 2, or -;; (at your option) any later version. -;; -;; This program 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 this program; see the file COPYING. If not, write to -;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth -;; Floor, Boston, MA 02110-1301, USA. -;; - -;;; Commentary: - -;; Display icons for all buffers in ibuffer. -;; -;; Install: -;; From melpa, `M-x package-install RET all-the-icons-ibuffer RET`. -;; (all-the-icons-ibuffer-mode 1) -;; or -;; (use-package all-the-icons-ibuffer-mode -;; :ensure t -;; :init (all-the-icons-ibuffer-mode 1)) - - -;;; Code: - -(require 'ibuffer) -(require 'all-the-icons) - -(defgroup all-the-icons-ibuffer nil - "Display icons for all buffers in ibuffer." - :group 'all-the-icons - :group 'ibuffer - :link '(url-link :tag "Homepage" "https://github.com/seagle0128/all-the-icons-ibuffer")) - -(defface all-the-icons-ibuffer-icon-face - '((t (:inherit default))) - "Face used for the icons while `all-the-icons-ibuffer-color-icon' is nil." - :group 'all-the-icons-ibuffer) - -(defcustom all-the-icons-ibuffer-color-icon t - "Whether display the colorful icons. - -It respects `all-the-icons-color-icons'." - :group 'all-the-icons-ibuffer - :type 'boolean) - -(defcustom all-the-icons-ibuffer-icon-size 1.0 - "The default icon size in ibuffer." - :group 'all-the-icons-ibuffer - :type 'number) - -(defcustom all-the-icons-ibuffer-icon-v-adjust 0.0 - "The default vertical adjustment of the icon in ibuffer." - :group 'all-the-icons-ibuffer - :type 'number) - -(defcustom all-the-icons-ibuffer-human-readable-size t - "Use human readable file size in ibuffer." - :group 'all-the-icons-ibuffer - :type 'boolean) - -(defcustom all-the-icons-ibuffer-formats - `((mark modified read-only ,(if (>= emacs-major-version 26) 'locked "") - ;; Here you may adjust by replacing :right with :center or :left - ;; According to taste, if you want the icon further from the name - " " (icon 2 2 :left :elide) - ,(propertize " " 'display `(space :align-to 8)) - (name 18 18 :left :elide) - " " (size-h 9 -1 :right) - " " (mode+ 16 16 :left :elide) - " " filename-and-process+) - (mark " " (name 16 -1) " " filename)) - "A list of ways to display buffer lines with `all-the-icons'. - -See `ibuffer-formats' for details." - :group 'all-the-icons-ibuffer - :type '(repeat sexp)) - - - -;; For alignment, the size of the name field should be the width of an icon -;;;###autoload(autoload 'ibuffer-make-column-icon "all-the-icons-ibuffer") -(define-ibuffer-column icon - (:name " " :inline t) - (let ((icon (if (and (buffer-file-name) (all-the-icons-auto-mode-match?)) - (all-the-icons-icon-for-file (file-name-nondirectory (buffer-file-name)) - :height all-the-icons-ibuffer-icon-size - :v-adjust all-the-icons-ibuffer-icon-v-adjust) - (all-the-icons-icon-for-mode major-mode - :height all-the-icons-ibuffer-icon-size - :v-adjust all-the-icons-ibuffer-icon-v-adjust)))) - (if (or (null icon) (symbolp icon)) - (setq icon (all-the-icons-faicon "file-o" - :face (if all-the-icons-ibuffer-color-icon - 'all-the-icons-dsilver - 'all-the-icons-ibuffer-icon-face) - :height (* 0.9 all-the-icons-ibuffer-icon-size) - :v-adjust all-the-icons-ibuffer-icon-v-adjust)) - (let* ((props (get-text-property 0 'face icon)) - (family (plist-get props :family)) - (face (if all-the-icons-ibuffer-color-icon - (or (plist-get props :inherit) props) - 'all-the-icons-ibuffer-icon-face)) - (new-face `(:inherit ,face - :family ,family - :height ,all-the-icons-ibuffer-icon-size))) - (propertize icon 'face new-face))))) - -;; Human readable file size for ibuffer -;;;###autoload(autoload 'ibuffer-make-column-size-h "all-the-icons-ibuffer") -(define-ibuffer-column size-h - (:name "Size" - :inline t - :props ('font-lock-face 'font-lock-comment-face) - :header-mouse-map ibuffer-size-header-map - :summarizer - (lambda (column-strings) - (let ((total 0)) - (dolist (string column-strings) - (setq total - ;; like, ewww ... - (+ (float (string-to-number string)) - total))) - (format "%.0f" total)))) - (let ((size (buffer-size))) - (if all-the-icons-ibuffer-human-readable-size - (file-size-human-readable size) - (format "%s" (buffer-size))))) - -;;;###autoload(autoload 'ibuffer-make-column-mode+ "all-the-icons-ibuffer") -(define-ibuffer-column mode+ - (:name "Mode" - :inline t - :header-mouse-map ibuffer-mode-header-map - :props ('font-lock-face 'font-lock-keyword-face - 'mouse-face 'highlight - 'keymap ibuffer-mode-name-map - 'help-echo "mouse-2: filter by this mode")) - (format-mode-line mode-name nil nil (current-buffer))) - -;;;###autoload(autoload 'ibuffer-make-column-filename-and-process+ "all-the-icons-ibuffer") -(define-ibuffer-column filename-and-process+ - (:name "Filename/Process" - :props ('font-lock-face 'font-lock-string-face) - :header-mouse-map ibuffer-filename/process-header-map - :summarizer - (lambda (strings) - (setq strings (delete "" strings)) - (let ((procs 0) - (files 0)) - (dolist (string strings) - (when (get-text-property 1 'ibuffer-process string) - (setq procs (1+ procs))) - (setq files (1+ files))) - (concat (cond ((zerop files) "No files") - ((= 1 files) "1 file") - (t (format "%d files" files))) - ", " - (cond ((zerop procs) "no processes") - ((= 1 procs) "1 process") - (t (format "%d processes" procs))))))) - (let ((proc (get-buffer-process buffer)) - (filename (ibuffer-make-column-filename buffer mark))) - (if proc - (concat (propertize (format "(%s %s)" proc (process-status proc)) - 'font-lock-face 'italic - 'ibuffer-process proc) - (if (> (length filename) 0) - (format " %s" filename) - "")) - filename))) - -(defvar all-the-icons-ibuffer-old-formats ibuffer-formats) - -;;;###autoload -(define-minor-mode all-the-icons-ibuffer-mode - "Display icons for all buffers in ibuffer." - :lighter nil - :global t - (if all-the-icons-ibuffer-mode - (setq ibuffer-formats all-the-icons-ibuffer-formats) - (setq ibuffer-formats all-the-icons-ibuffer-old-formats))) - -(provide 'all-the-icons-ibuffer) - -;;; all-the-icons-ibuffer.el ends here blob - e576e8d46294d00efd11ba6b795ec33ef0b326eb (mode 644) blob + /dev/null --- elpa/atomic-chrome-2.0.0/atomic-chrome-autoloads.el +++ /dev/null @@ -1,32 +0,0 @@ -;;; atomic-chrome-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from atomic-chrome.el - -(autoload 'atomic-chrome-start-server "atomic-chrome" "\ -Start websocket server for atomic-chrome." t) -(autoload 'atomic-chrome-stop-server "atomic-chrome" "\ -Stop websocket server for atomic-chrome." t) -(register-definition-prefixes "atomic-chrome" '("atomic-chrome-" "global-atomic-chrome-edit-mode")) - -;;; End of scraped data - -(provide 'atomic-chrome-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; atomic-chrome-autoloads.el ends here blob - fb29385788688dc57a74ffe5e33eec1259310149 (mode 644) blob + /dev/null --- elpa/atomic-chrome-2.0.0/atomic-chrome-pkg.el +++ /dev/null @@ -1,12 +0,0 @@ -;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "atomic-chrome" "2.0.0" - "Edit Chrome text area with Emacs using Atomic Chrome." - '((emacs "24.3") - (let-alist "1.0.4") - (websocket "1.4")) - :url "https://github.com/alpha22jp/atomic-chrome" - :commit "38ce9127285e1ff45f0f39b9da36a682103bdb96" - :revdesc "38ce9127285e" - :keywords '("chrome" "edit" "textarea") - :authors '(("alpha22jp" . "alpha22jp@gmail.com")) - :maintainers '(("alpha22jp" . "alpha22jp@gmail.com"))) blob - 2540e8b7654aadd2099a6f57df4aa3a8b146e31e (mode 644) blob + /dev/null --- elpa/atomic-chrome-2.0.0/atomic-chrome.el +++ /dev/null @@ -1,374 +0,0 @@ -;;; atomic-chrome.el --- Edit Chrome text area with Emacs using Atomic Chrome - -;; Copyright (C) 2016 alpha22jp - -;; Author: alpha22jp -;; Package-Requires: ((emacs "24.3") (let-alist "1.0.4") (websocket "1.4")) -;; Keywords: chrome edit textarea -;; URL: https://github.com/alpha22jp/atomic-chrome -;; Package-Version: 2.0.0 -;; Package-Revision: 38ce9127285e - -;; This program 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 2 of the License, or (at your option) any later -;; version. - -;; This program 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 -;; this program; if not, write to the Free Software Foundation, Inc., 51 -;; Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -;;; Commentary: - -;; This is the Emacs version of Atomic Chrome which is an extension for Google -;; Chrome browser that allows you to edit text areas of the browser in Emacs. -;; -;; It's similar to Edit with Emacs, but has some advantages as below with the -;; help of websocket. -;; -;; * Live update -;; The input on Emacs is reflected to the browser instantly and continuously. -;; * Bidirectional communication -;; You can edit both on the browser and Emacs, they are synced to the same. - -;;; Code: - -(eval-when-compile (require 'cl)) -(require 'json) -(require 'let-alist) -(require 'websocket) - -(defgroup atomic-chrome nil - "Edit Chrome text area with Emacs using Atomic Chrome." - :prefix "atomic-chrome-" - :group 'applications) - -(defcustom atomic-chrome-extension-type-list '(atomic-chrome ghost-text) - "List of chrome extension type available." - :type '(repeat (choice (const :tag "Atomic Chrome" atomic-chrome) - (const :tag "Ghost Text" ghost-text))) - :group 'atomic-chrome) - -(defcustom atomic-chrome-buffer-open-style 'split - "Specify the style to open new buffer for editing." - :type '(choice (const :tag "Open buffer with full window" full) - (const :tag "Open buffer with splitted window" split) - (const :tag "Open buffer with new frame" frame)) - :group 'atomic-chrome) - -(defcustom atomic-chrome-buffer-frame-width 80 - "Width of editing buffer frame." - :type 'integer - :group 'atomic-chrome) - -(defcustom atomic-chrome-buffer-frame-height 25 - "Height of editing buffer frame." - :type 'integer - :group 'atomic-chrome) - -(defcustom atomic-chrome-enable-auto-update t - "If non-nil, edit on Emacs is reflected to Chrome instantly, \ -otherwise you need to type \"C-xC-s\" manually." - :type 'boolean - :group 'atomic-chrome) - -(defcustom atomic-chrome-enable-bidirectional-edit t - "If non-nil, you can edit both on Chrome text area and Emacs, \ -otherwise edit on Chrome is ignored while editing on Emacs." - :type 'boolean - :group 'atomic-chrome) - -(defcustom atomic-chrome-default-major-mode 'text-mode - "Default major mode for editing buffer." - :type 'function - :group 'atomic-chrome) - -(defcustom atomic-chrome-url-major-mode-alist nil - "Association list of URL regexp and corresponding major mode \ -which is used to select major mode for specified website." - :type '(alist :key-type (string :tag "regexp") - :value-type (function :tag "major mode")) - :group 'atomic-chrome) - -(defcustom atomic-chrome-edit-mode-hook nil - "Customizable hook which run when the editing buffer is created." - :type 'hook - :group 'atomic-chrome) - -(defcustom atomic-chrome-edit-done-hook nil - "Customizable hook which run when the editing buffer is closed." - :type 'hook - :group 'atomic-chrome) - -(defvar atomic-chrome-server-atomic-chrome nil - "Websocket server connection handle for Atomic Chrome.") - -(defvar atomic-chrome-server-ghost-text nil - "Websocket server connection handle for Ghost Text.") - -(defvar atomic-chrome-buffer-table (make-hash-table :test 'equal) - "Hash table of editing buffer and its assciated data. -Each element has a list consisting of (websocket, frame).") - -(defun atomic-chrome-get-websocket (buffer) - "Lookup websocket associated with buffer BUFFER \ -from `atomic-chrome-buffer-table'." - (nth 0 (gethash buffer atomic-chrome-buffer-table))) - -(defun atomic-chrome-get-frame (buffer) - "Lookup frame associated with buffer BUFFER \ -from `atomic-chrome-buffer-table'." - (nth 1 (gethash buffer atomic-chrome-buffer-table))) - -(defun atomic-chrome-get-buffer-by-socket (socket) - "Lookup buffer which is associated to the websocket SOCKET \ -from `atomic-chrome-buffer-table'." - (let (buffer) - (cl-loop for key being the hash-keys of atomic-chrome-buffer-table - using (hash-values val) - do (when (equal (nth 0 val) socket) (setq buffer key))) - buffer)) - -(defun atomic-chrome-close-connection () - "Close client connection associated with current buffer." - (let ((socket (atomic-chrome-get-websocket (current-buffer)))) - (when socket - (remhash (current-buffer) atomic-chrome-buffer-table) - (websocket-close socket)))) - -(defun atomic-chrome-send-buffer-text () - "Send request to update text with current buffer content." - (interactive) - (let ((socket (atomic-chrome-get-websocket (current-buffer))) - (text (buffer-substring-no-properties (point-min) (point-max)))) - (when (and socket text) - (websocket-send-text - socket - (json-encode - (if (eq (websocket-server-conn socket) atomic-chrome-server-ghost-text) - (list (cons "text" text)) - (list '("type" . "updateText") - (cons "payload" (list (cons "text" text)))))))))) - -(defun atomic-chrome-set-major-mode (url) - "Set major mode for editing buffer depending on URL. -`atomic-chrome-url-major-mode-alist' can be used to select major mode. -The specified major mode is used if URL matches to one of the alist, -otherwise fallback to `atomic-chrome-default-major-mode'" - (funcall (or (and url (assoc-default url - atomic-chrome-url-major-mode-alist - 'string-match)) - atomic-chrome-default-major-mode))) - -(defun atomic-chrome-show-edit-buffer (buffer title) - "Show editing buffer BUFFER by creating a frame with title TITLE, \ -or raising the selected frame depending on `atomic-chrome-buffer-open-style'." - (let ((edit-frame nil) - (frame-params (list (cons 'name (format "Atomic Chrome: %s" title)) - (cons 'width atomic-chrome-buffer-frame-width) - (cons 'height atomic-chrome-buffer-frame-height)))) - (when (eq atomic-chrome-buffer-open-style 'frame) - (setq edit-frame - (if (memq window-system '(ns mac)) - ;; Avoid using make-frame-on-display for Mac OS. - (make-frame frame-params) - (make-frame-on-display (getenv "DISPLAY") frame-params))) - (select-frame edit-frame)) - (if (eq atomic-chrome-buffer-open-style 'split) - (pop-to-buffer buffer) - (switch-to-buffer buffer)) - (raise-frame edit-frame) - (select-frame-set-input-focus (window-frame (selected-window))) - edit-frame)) - -(defun atomic-chrome-create-buffer (socket url title text) - "Create buffer associated with websocket specified by SOCKET. -URL is used to determine the major mode of the buffer created, -TITLE is used for the buffer name and TEXT is inserted to the buffer." - (let ((buffer (generate-new-buffer title))) - (with-current-buffer buffer - (puthash buffer - (list socket (atomic-chrome-show-edit-buffer buffer title)) - atomic-chrome-buffer-table) - (atomic-chrome-set-major-mode url) - (insert text)))) - -(defun atomic-chrome-close-edit-buffer (buffer) - "Close buffer BUFFER if it's one of Atomic Chrome edit buffers." - (let ((frame (atomic-chrome-get-frame buffer))) - (with-current-buffer buffer - (save-restriction - (run-hooks 'atomic-chrome-edit-done-hook) - (when frame (delete-frame frame)) - (kill-buffer buffer))))) - -(defun atomic-chrome-close-current-buffer () - "Close current buffer and connection from client." - (interactive) - (atomic-chrome-close-edit-buffer (current-buffer))) - -(defun atomic-chrome-update-buffer (socket text) - "Update text on buffer associated with SOCKET to TEXT." - (let ((buffer (atomic-chrome-get-buffer-by-socket socket))) - (when buffer - (with-current-buffer buffer - (erase-buffer) - (insert text))))) - -(defun atomic-chrome-on-message (socket frame) - "Function to handle data received from websocket client specified by SOCKET, \ -where FRAME show raw data received." - (let ((msg (json-read-from-string - (decode-coding-string - (string-make-unibyte (websocket-frame-payload frame)) 'utf-8)))) - (let-alist msg - (if (eq (websocket-server-conn socket) atomic-chrome-server-ghost-text) - (if (atomic-chrome-get-buffer-by-socket socket) - (atomic-chrome-update-buffer socket .text) - (atomic-chrome-create-buffer socket .url .title .text)) - (cond ((string= .type "register") - (atomic-chrome-create-buffer socket .payload.url .payload.title .payload.text)) - ((string= .type "updateText") - (when atomic-chrome-enable-bidirectional-edit - (atomic-chrome-update-buffer socket .payload.text)))))))) - -(defun atomic-chrome-on-close (socket) - "Function to handle request from client to close websocket SOCKET." - (let ((buffer (atomic-chrome-get-buffer-by-socket socket))) - (when buffer (atomic-chrome-close-edit-buffer buffer)))) - -(defvar atomic-chrome-edit-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-x C-s") 'atomic-chrome-send-buffer-text) - (define-key map (kbd "C-c C-c") 'atomic-chrome-close-current-buffer) - map) - "Keymap for minor mode `atomic-chrome-edit-mode'.") - -(define-minor-mode atomic-chrome-edit-mode - "Minor mode enabled on buffers opened by Emacs Chrome server." - :group 'atomic-chrome - :lighter " AtomicChrome" - :init-value nil - :keymap atomic-chrome-edit-mode-map - (when atomic-chrome-edit-mode - (add-hook 'kill-buffer-hook 'atomic-chrome-close-connection nil t) - (when atomic-chrome-enable-auto-update - (add-hook 'post-command-hook 'atomic-chrome-send-buffer-text nil t)))) - -(defun atomic-chrome-turn-on-edit-mode () - "Turn on `atomic-chrome-edit-mode' if the buffer is an editing buffer." - (when (gethash (current-buffer) atomic-chrome-buffer-table) - (atomic-chrome-edit-mode t))) - -(define-global-minor-mode global-atomic-chrome-edit-mode - atomic-chrome-edit-mode atomic-chrome-turn-on-edit-mode) - -(defadvice save-buffers-kill-emacs - (before atomic-chrome-server-stop-before-kill-emacs) - "Call `atomic-chrome-close-server' before closing Emacs to avoid users \ -being prompted to kill the websocket server process." - (atomic-chrome-stop-server)) - -(defun atomic-chrome-start-websocket-server (port) - "Create websocket server on port PORT." - (websocket-server - port - :host 'local - :on-message #'atomic-chrome-on-message - :on-open nil - :on-close #'atomic-chrome-on-close)) - -(defun atomic-chrome-start-httpd () - "Start the HTTP server for Ghost Text query." - (interactive) - (make-network-process - :name "atomic-chrome-httpd" - :family 'ipv4 - :host 'local - :service 4001 - :filter 'atomic-chrome-httpd-process-filter - :filter-multibyte nil - :server t)) - -(defun atomic-chrome-normalize-header (header) - "Destructively capitalize the components of HEADER." - (mapconcat #'capitalize (split-string header "-") "-")) - -(defun atomic-chrome-httpd-parse-string (string) - "Parse client http header STRING into alist." - (let* ((lines (split-string string "[\n\r]+")) - (req (list (split-string (car lines)))) - (post (cadr (split-string string "\r\n\r\n")))) - (dolist (line (butlast (cdr lines))) - (push (list (atomic-chrome-normalize-header (car (split-string line ": "))) - (mapconcat #'identity - (cdr (split-string line ": ")) ": ")) - req)) - (push (list "Content" post) req) - (reverse req))) - -(defun atomic-chrome-httpd-process-filter (proc string) - "Process filter of PROC which run each time client make a request. -STRING is the string process received." - (setf string (concat (process-get proc :previous-string) string)) - (let* ((request (atomic-chrome-httpd-parse-string string)) - (content-length (cadr (assoc "Content-Length" request))) - (uri (cl-cadar request)) - (content (cadr (assoc "Content" request)))) - (if (and content-length - (< (string-bytes content) (string-to-number content-length))) - (process-put proc :previous-string string) - (atomic-chrome-httpd-send-response proc)))) - -(defun atomic-chrome-httpd-send-response (proc) - "Send an HTTP 200 OK response back to process PROC." - (when (processp proc) - (unless atomic-chrome-server-ghost-text - (setq atomic-chrome-server-ghost-text - (atomic-chrome-start-websocket-server 64293))) - (let ((header "HTTP/1.0 200 OK\nContent-Type: application/json\n") - (body (json-encode '(:ProtocolVersion 1 :WebSocketPort 64293)))) - (process-send-string proc (concat header "\n" body)) - (process-send-eof proc)))) - -;;;###autoload -(defun atomic-chrome-start-server () - "Start websocket server for atomic-chrome." - (interactive) - (and (not atomic-chrome-server-atomic-chrome) - (memq 'atomic-chrome atomic-chrome-extension-type-list) - (setq atomic-chrome-server-atomic-chrome - (atomic-chrome-start-websocket-server 64292))) - (and (not (process-status "atomic-chrome-httpd")) - (memq 'ghost-text atomic-chrome-extension-type-list) - (atomic-chrome-start-httpd)) - (global-atomic-chrome-edit-mode 1) - (ad-activate 'save-buffers-kill-emacs)) - -;;;###autoload -(defun atomic-chrome-stop-server nil - "Stop websocket server for atomic-chrome." - (interactive) - (when atomic-chrome-server-atomic-chrome - (websocket-server-close atomic-chrome-server-atomic-chrome) - (setq atomic-chrome-server-atomic-chrome nil)) - (when atomic-chrome-server-ghost-text - (websocket-server-close atomic-chrome-server-ghost-text) - (setq atomic-chrome-server-ghost-text nil)) - (when (process-status "atomic-chrome-httpd") - (delete-process "atomic-chrome-httpd")) - (ad-disable-advice 'save-buffers-kill-emacs - 'before 'atomic-chrome-server-stop-before-kill-emacs) - ;; Disabling advice doesn't take effect until you (re-)activate - ;; all advice for the function. - (ad-activate 'save-buffers-kill-emacs) - (global-atomic-chrome-edit-mode 0)) - -(provide 'atomic-chrome) - -;;; atomic-chrome.el ends here blob - 8b24b645b98e75c4420677b8eaefc23645e8108a (mode 644) blob + /dev/null --- elpa/avy-0.5.0/.dir-locals.el +++ /dev/null @@ -1,9 +0,0 @@ -;;; Directory Local Variables -;;; For more information see (info "(emacs) Directory Variables") - -((emacs-lisp-mode - (indent-tabs-mode . nil) - (outline-regexp . ";;\\([;*]+ [^\s\t\n]\\|###autoload\\)\\|(") - (sentence-end-double-space . t) - ;; (lisp-indent-function . common-lisp-indent-function) - )) blob - b6166c2f1a843bd4a509ef85e226c528307b163b (mode 644) blob + /dev/null --- elpa/avy-0.5.0/ChangeLog +++ /dev/null @@ -1,56 +0,0 @@ -2019-05-13 Stefan Monnier - - * avy/targets/checkdoc.el, ivy/doc/scripts.el: copyright tweak - -2019-05-11 Oleh Krehel - - Merge commit '0e59474430cbfbe7caf2a41cf7935fc613648c49' from avy - -2017-11-30 Oleh Krehel - - Merge commit '4a2cee03519f98cf95b29905dec2566a39ff717e' from swiper - -2017-04-08 Oleh Krehel - - Merge commit 'ace01d5603ddf49b025eb811b612af72ec38dcfb' from swiper - -2016-07-11 Paul Eggert - - Fix some quoting problems in doc strings - - Most of these are minor issues involving, e.g., quoting `like this' - instead of 'like this'. A few involve escaping ` and ' with a preceding - \= when the characters should not be turned into curved single quotes. - -2016-01-23 Oleh Krehel - - Merge commit '11fbd70347a8cc62817c6d4ebf2291471ebdd607' from avy - -2015-06-25 Oleh Krehel - - Merge commit '8d38a898f23b3105c5d098f0cfb6c3383547e394' from avy - -2015-05-19 Oleh Krehel - - Merge commit '199c52606dcd614cb856bbcaca13b5fada0772b6' from avy - -2015-05-09 Oleh Krehel - - Merge commit 'e242f04e32c7d874c779fb83c86aa5bdbc508f18' from avy - - Conflicts: - packages/avy/avy.el - -2015-05-08 Stefan Monnier - - * avy.el: Fix up author email and top-level require. - -2015-05-08 Oleh Krehel - - Add 'packages/avy/' from commit - '32003515c8efa2cf38b62c45499dae30bc7cacb8' - - git-subtree-dir: packages/avy git-subtree-mainline: - 74b34f2bbb929a1caf5cf753e59c54c3ccb74f50 git-subtree-split: - 32003515c8efa2cf38b62c45499dae30bc7cacb8 - blob - 430da889faa4fb9b2535840d7653dec0a837e69e (mode 644) blob + /dev/null --- elpa/avy-0.5.0/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -emacs ?= emacs -# EMACS = emacs-24.3 - -LOAD = -l avy.el -l avy-test.el - -.PHONY: all test clean checkdoc - -all: compile test checkdoc - -test: - $(emacs) -batch $(LOAD) -f ert-run-tests-batch-and-exit - -checkdoc: - $(emacs) -batch -l targets/checkdoc.el - -compile: - $(emacs) -batch -l targets/avy-init.el - -run: - $(emacs) -Q -l targets/avy-init.el - -clean: - rm -f *.elc blob - 76455de4b28a8edf3dd93ef03596ccaa96e402ce (mode 644) blob + /dev/null --- elpa/avy-0.5.0/README.md +++ /dev/null @@ -1,127 +0,0 @@ -[![MELPA](https://melpa.org/packages/avy-badge.svg)](https://melpa.org/#/avy) -[![MELPA Stable](https://stable.melpa.org/packages/avy-badge.svg)](https://stable.melpa.org/#/avy) - -## Introduction - -`avy` is a GNU Emacs package for jumping to visible text using a char-based decision tree. See also [ace-jump-mode](https://github.com/winterTTr/ace-jump-mode) and [vim-easymotion](https://github.com/Lokaltog/vim-easymotion) - `avy` uses the same idea. - -![logo](https://raw.githubusercontent.com/wiki/abo-abo/avy/images/avy-avatar-1.png) - -## Command overview - -You can bind some of these useful commands in your config. - -### `avy-goto-char` - -> Input one char, jump to it with a tree. - -```elisp -(global-set-key (kbd "C-:") 'avy-goto-char) -``` - -After C-: b: - -![avy-goto-char](https://raw.githubusercontent.com/wiki/nloyola/avy/images/avy-goto-char.png) - -### `avy-goto-char-2` - -> Input two consecutive chars, jump to the first one with a tree. - -The advantage over the previous one is less candidates for the tree search. And it's not too inconvenient to enter two consecutive chars instead of one. - -```elisp -(global-set-key (kbd "C-'") 'avy-goto-char-2) -``` - -After C-' bu: - -![avy-goto-char-2](http://oremacs.com/download/avi-goto-char-2.png) - -### `avy-goto-char-timer` - -> Input an arbitrary amount of consecutive chars, jump to the first one with a tree. - -This is a more flexible version of `avy-goto-char-2`. First part works similarly to `isearch`: you type a query and it's highlighted dynamically on the screen. When you stop typing for `avy-timeout-seconds` (0.5s by default), you'll be able to select one of the candidates with `avy`. As you're inputting characters, you can use `C-h` (backspace) or `DEL` (delete) to -forget the last typed character and `RET` to end the input sequence immediately and select a candidate. - -### `avy-goto-line` - -> Input zero chars, jump to a line start with a tree. - -```elisp -(global-set-key (kbd "M-g f") 'avy-goto-line) -``` - -After M-g f: - -![avy-goto-line](http://oremacs.com/download/avi-goto-line.png) - -You can actually replace the M-g g binding of `goto-line`, since if you enter a digit for `avy-goto-line`, it will switch to `goto-line` with that digit already entered. - -### `avy-goto-word-1` - -> Input one char at word start, jump to a word start with a tree. - -```elisp -(global-set-key (kbd "M-g w") 'avy-goto-word-1) -``` - -After M-g wb: - -![avy-goto-word-1](http://oremacs.com/download/avi-goto-word-1.png) - -### `avy-goto-word-0` - -> Input zero chars, jump to a word start with a tree. - -Compared to `avy-goto-word-1`, there are a lot more candidates. But at a least there's not need to input the initial char. - -```elisp -(global-set-key (kbd "M-g e") 'avy-goto-word-0) -``` - -After M-g e: - -![avy-goto-word-0](http://oremacs.com/download/avi-goto-word-0.png) - -### Org-mode commands - - * `avy-org-goto-heading-timer`: Type part of an Org heading. When you stop typing, if only one heading on the screen matches, it will be jumped to; if more than one matches, you can jump to a heading with Avy. This is like `avy-goto-char-timer` but for Org headings. - * `avy-org-refile-as-child`: With point in an entry you want to refile, run this command, select a heading with Avy, and the entry will be refiled as its first child heading. This makes it quick and easy to refile to headings that are visible on-screen, even to other windows or buffers. - -### Other commands - -There are some more commands which you can explore yourself by looking at the code. - -### Bindings - -You add this to your config to bind some stuff: - -```elisp -(avy-setup-default) -(global-set-key (kbd "C-c C-j") 'avy-resume) -``` - -It will bind, for example, `avy-isearch` to C-' in `isearch-mode-map`, so that you can select one of the currently visible `isearch` candidates using `avy`. - -### Customization - -See the comprehensive custom variable list on [the defcustom wiki page](https://github.com/abo-abo/avy/wiki/defcustom). - -See how to write your own avy commands on [the custom-commands wiki page](https://github.com/abo-abo/avy/wiki/custom-commands). - -## Contributing - -### Copyright Assignment - -Avy is subject to the same [copyright assignment](http://www.gnu.org/prep/maintain/html_node/Copyright-Papers.html) policy as Emacs itself, org-mode, CEDET and other packages in [GNU ELPA](http://elpa.gnu.org/packages/). Any [legally significant](http://www.gnu.org/prep/maintain/html_node/Legally-Significant.html#Legally-Significant) contributions can only be accepted after the author has completed their paperwork. Please see [the request form](http://git.savannah.gnu.org/cgit/gnulib.git/tree/doc/Copyright/request-assign.future) if you want to proceed. - -The copyright assignment isn't a big deal, it just says that the copyright for your submitted changes to Emacs belongs to the FSF. This assignment works for all projects related to Emacs. To obtain it, you need to send one email, then send one letter (if you live in the US, it's digital), and wait for some time (in my case, I had to wait for one month). - -### Style - -The basic code style guide is to use `(setq indent-tabs-mode nil)`. It is provided for you in [.dir-locals.el](https://github.com/abo-abo/avy/blob/master/.dir-locals.el), please obey it. - -Before submitting the change, run `make compile` and `make test` to make sure that it doesn't introduce new compile warnings or test failures. Also run `make checkdoc` to see that your changes obey the documentation guidelines. - -Use your own judgment for the commit messages, I recommend a verbose style using `magit-commit-add-log`. blob - 2afd0ffb14ebf3a836e64561c2fe33a85171b65a (mode 644) blob + /dev/null --- elpa/avy-0.5.0/avy-autoloads.el +++ /dev/null @@ -1,232 +0,0 @@ -;;; avy-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from avy.el - -(autoload 'avy-goto-char "avy" "\ -Jump to the currently visible CHAR. -The window scope is determined by `avy-all-windows' (ARG negates it). - -(fn CHAR &optional ARG)" t) -(autoload 'avy-goto-char-in-line "avy" "\ -Jump to the currently visible CHAR in the current line. - -(fn CHAR)" t) -(autoload 'avy-goto-char-2 "avy" "\ -Jump to the currently visible CHAR1 followed by CHAR2. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched. - -(fn CHAR1 CHAR2 &optional ARG BEG END)" t) -(autoload 'avy-goto-char-2-above "avy" "\ -Jump to the currently visible CHAR1 followed by CHAR2. -This is a scoped version of `avy-goto-char-2', where the scope is -the visible part of the current buffer up to point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. - -(fn CHAR1 CHAR2 &optional ARG)" t) -(autoload 'avy-goto-char-2-below "avy" "\ -Jump to the currently visible CHAR1 followed by CHAR2. -This is a scoped version of `avy-goto-char-2', where the scope is -the visible part of the current buffer following point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. - -(fn CHAR1 CHAR2 &optional ARG)" t) -(autoload 'avy-isearch "avy" "\ -Jump to one of the current isearch candidates." t) -(autoload 'avy-goto-word-0 "avy" "\ -Jump to a word start. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched. - -(fn ARG &optional BEG END)" t) -(autoload 'avy-goto-word-1 "avy" "\ -Jump to the currently visible CHAR at a word start. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched. -When SYMBOL is non-nil, jump to symbol start instead of word start. - -(fn CHAR &optional ARG BEG END SYMBOL)" t) -(autoload 'avy-goto-word-1-above "avy" "\ -Jump to the currently visible CHAR at a word start. -This is a scoped version of `avy-goto-word-1', where the scope is -the visible part of the current buffer up to point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. - -(fn CHAR &optional ARG)" t) -(autoload 'avy-goto-word-1-below "avy" "\ -Jump to the currently visible CHAR at a word start. -This is a scoped version of `avy-goto-word-1', where the scope is -the visible part of the current buffer following point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. - -(fn CHAR &optional ARG)" t) -(autoload 'avy-goto-symbol-1 "avy" "\ -Jump to the currently visible CHAR at a symbol start. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. - -(fn CHAR &optional ARG)" t) -(autoload 'avy-goto-symbol-1-above "avy" "\ -Jump to the currently visible CHAR at a symbol start. -This is a scoped version of `avy-goto-symbol-1', where the scope is -the visible part of the current buffer up to point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. - -(fn CHAR &optional ARG)" t) -(autoload 'avy-goto-symbol-1-below "avy" "\ -Jump to the currently visible CHAR at a symbol start. -This is a scoped version of `avy-goto-symbol-1', where the scope is -the visible part of the current buffer following point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. - -(fn CHAR &optional ARG)" t) -(autoload 'avy-goto-subword-0 "avy" "\ -Jump to a word or subword start. -The window scope is determined by `avy-all-windows' (ARG negates it). - -When PREDICATE is non-nil it's a function of zero parameters that -should return true. - -BEG and END narrow the scope where candidates are searched. - -(fn &optional ARG PREDICATE BEG END)" t) -(autoload 'avy-goto-subword-1 "avy" "\ -Jump to the currently visible CHAR at a subword start. -The window scope is determined by `avy-all-windows' (ARG negates it). -The case of CHAR is ignored. - -(fn CHAR &optional ARG)" t) -(autoload 'avy-goto-word-or-subword-1 "avy" "\ -Forward to `avy-goto-subword-1' or `avy-goto-word-1'. -Which one depends on variable `subword-mode'." t) -(autoload 'avy-goto-line "avy" "\ -Jump to a line start in current buffer. - -When ARG is 1, jump to lines currently visible, with the option -to cancel to `goto-line' by entering a number. - -When ARG is 4, negate the window scope determined by -`avy-all-windows'. - -Otherwise, forward to `goto-line' with ARG. - -(fn &optional ARG)" t) -(autoload 'avy-goto-line-above "avy" "\ -Goto visible line above the cursor. -OFFSET changes the distance between the closest key to the cursor and -the cursor -When BOTTOM-UP is non-nil, display avy candidates from top to bottom - -(fn &optional OFFSET BOTTOM-UP)" t) -(autoload 'avy-goto-line-below "avy" "\ -Goto visible line below the cursor. -OFFSET changes the distance between the closest key to the cursor and -the cursor -When BOTTOM-UP is non-nil, display avy candidates from top to bottom - -(fn &optional OFFSET BOTTOM-UP)" t) -(autoload 'avy-goto-end-of-line "avy" "\ -Call `avy-goto-line' and move to the end of the line. - -(fn &optional ARG)" t) -(autoload 'avy-copy-line "avy" "\ -Copy a selected line above the current line. -ARG lines can be used. - -(fn ARG)" t) -(autoload 'avy-move-line "avy" "\ -Move a selected line above the current line. -ARG lines can be used. - -(fn ARG)" t) -(autoload 'avy-copy-region "avy" "\ -Select two lines and copy the text between them to point. - -The window scope is determined by `avy-all-windows' or -`avy-all-windows-alt' when ARG is non-nil. - -(fn ARG)" t) -(autoload 'avy-move-region "avy" "\ -Select two lines and move the text between them above the current line." t) -(autoload 'avy-kill-region "avy" "\ -Select two lines and kill the region between them. - -The window scope is determined by `avy-all-windows' or -`avy-all-windows-alt' when ARG is non-nil. - -(fn ARG)" t) -(autoload 'avy-kill-ring-save-region "avy" "\ -Select two lines and save the region between them to the kill ring. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. - -(fn ARG)" t) -(autoload 'avy-kill-whole-line "avy" "\ -Select line and kill the whole selected line. - -With a numerical prefix ARG, kill ARG line(s) starting from the -selected line. If ARG is negative, kill backward. - -If ARG is zero, kill the selected line but exclude the trailing -newline. - -\\[universal-argument] 3 \\[avy-kil-whole-line] kill three lines -starting from the selected line. \\[universal-argument] -3 - -\\[avy-kill-whole-line] kill three lines backward including the -selected line. - -(fn ARG)" t) -(autoload 'avy-kill-ring-save-whole-line "avy" "\ -Select line and save the whole selected line as if killed, but don’t kill it. - -This command is similar to `avy-kill-whole-line', except that it -saves the line(s) as if killed, but does not kill it(them). - -With a numerical prefix ARG, kill ARG line(s) starting from the -selected line. If ARG is negative, kill backward. - -If ARG is zero, kill the selected line but exclude the trailing -newline. - -(fn ARG)" t) -(autoload 'avy-setup-default "avy" "\ -Setup the default shortcuts.") -(autoload 'avy-goto-char-timer "avy" "\ -Read one or many consecutive chars and jump to the first one. -The window scope is determined by `avy-all-windows' (ARG negates it). - -(fn &optional ARG)" t) -(register-definition-prefixes "avy" '("avy-")) - -;;; End of scraped data - -(provide 'avy-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; avy-autoloads.el ends here blob - 55b1696ad5110c8bc06716cd6ae9b892be7ec3c8 (mode 644) blob + /dev/null --- elpa/avy-0.5.0/avy-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from avy.el -(define-package "avy" "0.5.0" "Jump to arbitrary positions in visible text and select text quickly." '((emacs "24.1") (cl-lib "0.5")) :url "https://github.com/abo-abo/avy" :keywords '("point" "location")) blob - bb03599f97ae132272906cdc3a672f6657af07b6 (mode 644) blob + /dev/null --- elpa/avy-0.5.0/avy-test.el +++ /dev/null @@ -1,72 +0,0 @@ -;;; avy-test.el --- Tests for avy - -;; Copyright (C) 2015 Free Software Foundation, Inc. - -;; Author: Oleh Krehel - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: -;; - -;;; Code: - -(require 'ert) -(require 'avy) - -(ert-deftest avy-subdiv () - (should - (equal (avy-subdiv 5 4) - '(1 1 1 2))) - (should - (equal (avy-subdiv 10 4) - '(1 1 4 4))) - (should - (equal (avy-subdiv 16 4) - '(4 4 4 4))) - (should - (equal (avy-subdiv 17 4) - '(4 4 4 5))) - (should - (equal (avy-subdiv 27 4) - '(4 4 4 15))) - (should - (equal (avy-subdiv 50 4) - '(4 14 16 16))) - (should - (equal (avy-subdiv 65 4) - '(16 16 16 17)))) - -(ert-deftest avy-tree () - (should - (equal - (avy-tree '(0 1 2 3 4 5 6 7 8 9 10) - '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) - '((97 leaf . 0) - (115 leaf . 1) - (100 leaf . 2) - (102 leaf . 3) - (103 leaf . 4) - (104 leaf . 5) - (106 leaf . 6) - (107 leaf . 7) - (108 (97 leaf . 8) - (115 leaf . 9) - (100 leaf . 10)))))) - -(provide 'avy-test) - -;;; avy-test.el ends here blob - 8b258da0c9681506f2a298e7fec902eb6b280783 (mode 644) blob + /dev/null --- elpa/avy-0.5.0/avy.el +++ /dev/null @@ -1,2144 +0,0 @@ -;;; avy.el --- Jump to arbitrary positions in visible text and select text quickly. -*- lexical-binding: t -*- - -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. - -;; Author: Oleh Krehel -;; URL: https://github.com/abo-abo/avy -;; Version: 0.5.0 -;; Package-Requires: ((emacs "24.1") (cl-lib "0.5")) -;; Keywords: point, location - -;; This file is part of GNU Emacs. - -;; 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 program 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. - -;; For a full copy of the GNU General Public License -;; see . - -;;; Commentary: -;; -;; With Avy, you can move point to any position in Emacs – even in a -;; different window – using very few keystrokes. For this, you look at -;; the position where you want point to be, invoke Avy, and then enter -;; the sequence of characters displayed at that position. -;; -;; If the position you want to jump to can be determined after only -;; issuing a single keystroke, point is moved to the desired position -;; immediately after that keystroke. In case this isn't possible, the -;; sequence of keystrokes you need to enter is comprised of more than -;; one character. Avy uses a decision tree where each candidate position -;; is a leaf and each edge is described by a character which is distinct -;; per level of the tree. By entering those characters, you navigate the -;; tree, quickly arriving at the desired candidate position, such that -;; Avy can move point to it. -;; -;; Note that this only makes sense for positions you are able to see -;; when invoking Avy. These kinds of positions are supported: -;; -;; * character positions -;; * word or subword start positions -;; * line beginning positions -;; * link positions -;; * window positions -;; -;; If you're familiar with the popular `ace-jump-mode' package, this -;; package does all that and more, without the implementation -;; headache. - -;;; Code: -(require 'cl-lib) -(require 'ring) - -;;* Customization -(defgroup avy nil - "Jump to things tree-style." - :group 'convenience - :prefix "avy-") - -(defcustom avy-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l) - "Default keys for jumping. -Any key is either a character representing a self-inserting -key (letters, digits, punctuation, etc.) or a symbol denoting a -non-printing key like an arrow key (left, right, up, down). For -non-printing keys, a corresponding entry in -`avy-key-to-char-alist' must exist in order to visualize the key -in the avy overlays. - -If `avy-style' is set to words, make sure there are at least three -keys different than the following: a, e, i, o, u, y" - :type '(repeat :tag "Keys" (choice - (character :tag "char") - (symbol :tag "non-printing key")))) - -(defconst avy--key-type - '(choice :tag "Command" - (const avy-goto-char) - (const avy-goto-char-2) - (const avy-isearch) - (const avy-goto-line) - (const avy-goto-subword-0) - (const avy-goto-subword-1) - (const avy-goto-word-0) - (const avy-goto-word-1) - (const avy-copy-line) - (const avy-copy-region) - (const avy-move-line) - (const avy-move-region) - (const avy-kill-whole-line) - (const avy-kill-region) - (const avy-kill-ring-save-whole-line) - (const avy-kill-ring-save-region) - (function :tag "Other command"))) - -(defcustom avy-keys-alist nil - "Alist of avy-jump commands to `avy-keys' overriding the default `avy-keys'." - :type `(alist - :key-type ,avy--key-type - :value-type (repeat :tag "Keys" character))) - -(defcustom avy-orders-alist '((avy-goto-char . avy-order-closest)) - "Alist of candidate ordering functions. -Usually, candidates appear in their point position order." - :type `(alist - :key-type ,avy--key-type - :value-type function)) - -(defcustom avy-words - '("am" "by" "if" "is" "it" "my" "ox" "up" - "ace" "act" "add" "age" "ago" "aim" "air" "ale" "all" "and" "ant" "any" - "ape" "apt" "arc" "are" "arm" "art" "ash" "ate" "awe" "axe" "bad" "bag" - "ban" "bar" "bat" "bay" "bed" "bee" "beg" "bet" "bid" "big" "bit" "bob" - "bot" "bow" "box" "boy" "but" "cab" "can" "cap" "car" "cat" "cog" "cop" - "cow" "cry" "cup" "cut" "day" "dew" "did" "die" "dig" "dim" "dip" "dog" - "dot" "dry" "dub" "dug" "dye" "ear" "eat" "eel" "egg" "ego" "elf" "eve" - "eye" "fan" "far" "fat" "fax" "fee" "few" "fin" "fit" "fix" "flu" "fly" - "foe" "fog" "for" "fox" "fry" "fun" "fur" "gag" "gap" "gas" "gel" "gem" - "get" "gig" "gin" "gnu" "god" "got" "gum" "gun" "gut" "guy" "gym" "had" - "hag" "ham" "has" "hat" "her" "hid" "him" "hip" "his" "hit" "hop" "hot" - "how" "hub" "hue" "hug" "hut" "ice" "icy" "imp" "ink" "inn" "ion" "ire" - "ivy" "jab" "jam" "jar" "jaw" "jet" "job" "jog" "joy" "key" "kid" "kit" - "lag" "lap" "lay" "let" "lid" "lie" "lip" "lit" "lob" "log" "lot" "low" - "mad" "man" "map" "mat" "may" "men" "met" "mix" "mob" "mop" "mud" "mug" - "nag" "nap" "new" "nil" "nod" "nor" "not" "now" "nun" "oak" "odd" "off" - "oil" "old" "one" "orb" "ore" "ork" "our" "out" "owl" "own" "pad" "pan" - "par" "pat" "paw" "pay" "pea" "pen" "pet" "pig" "pin" "pit" "pod" "pot" - "pry" "pub" "pun" "put" "rag" "ram" "ran" "rat" "raw" "ray" "red" "rib" - "rim" "rip" "rob" "rod" "rot" "row" "rub" "rug" "rum" "run" "sad" "sat" - "saw" "say" "sea" "see" "sew" "she" "shy" "sin" "sip" "sit" "six" "ski" - "sky" "sly" "sob" "son" "soy" "spy" "sum" "sun" "tab" "tad" "tag" "tan" - "tap" "tar" "tax" "tea" "the" "tie" "tin" "tip" "toe" "ton" "too" "top" - "toy" "try" "tub" "two" "urn" "use" "van" "war" "was" "wax" "way" "web" - "wed" "wet" "who" "why" "wig" "win" "wit" "woe" "won" "wry" "you" "zap" - "zip" "zoo") - "Words to use in case `avy-style' is set to `words'. -Every word should contain at least one vowel i.e. one of the following -characters: a, e, i, o, u, y -They do not have to be sorted but no word should be a prefix of another one." - :type '(repeat string)) - -(defcustom avy-style 'at-full - "The default method of displaying the overlays. -Use `avy-styles-alist' to customize this per-command." - :type '(choice - (const :tag "Pre" pre) - (const :tag "At" at) - (const :tag "At Full" at-full) - (const :tag "Post" post) - (const :tag "De Bruijn" de-bruijn) - (const :tag "Words" words))) - -(defcustom avy-styles-alist nil - "Alist of avy-jump commands to the style for each command. -If the commands isn't on the list, `avy-style' is used." - :type '(alist - :key-type (choice :tag "Command" - (const avy-goto-char) - (const avy-goto-char-2) - (const avy-isearch) - (const avy-goto-line) - (const avy-goto-subword-0) - (const avy-goto-subword-1) - (const avy-goto-word-0) - (const avy-goto-word-1) - (const avy-copy-line) - (const avy-copy-region) - (const avy-move-line) - (const avy-move-region) - (const avy-kill-whole-line) - (const avy-kill-region) - (const avy-kill-ring-save-whole-line) - (const avy-kill-ring-save-region) - (function :tag "Other command")) - :value-type (choice - (const :tag "Pre" pre) - (const :tag "At" at) - (const :tag "At Full" at-full) - (const :tag "Post" post) - (const :tag "De Bruijn" de-bruijn) - (const :tag "Words" words)))) - -(defcustom avy-dispatch-alist - '((?x . avy-action-kill-move) - (?X . avy-action-kill-stay) - (?t . avy-action-teleport) - (?m . avy-action-mark) - (?n . avy-action-copy) - (?y . avy-action-yank) - (?i . avy-action-ispell) - (?z . avy-action-zap-to-char)) - "List of actions for `avy-handler-default'. - -Each item is (KEY . ACTION). When KEY not on `avy-keys' is -pressed during the dispatch, ACTION is set to replace the default -`avy-action-goto' once a candidate is finally selected." - :type - '(alist - :key-type (choice (character :tag "Char")) - :value-type (choice - (const :tag "Mark" avy-action-mark) - (const :tag "Copy" avy-action-copy) - (const :tag "Kill and move point" avy-action-kill-move) - (const :tag "Kill" avy-action-kill-stay)))) - -(defcustom avy-background nil - "When non-nil, a gray background will be added during the selection." - :type 'boolean) - -(defcustom avy-all-windows t - "Determine the list of windows to consider in search of candidates." - :type - '(choice - (const :tag "All Frames" all-frames) - (const :tag "This Frame" t) - (const :tag "This Window" nil))) - -(defcustom avy-case-fold-search t - "Non-nil if searches should ignore case." - :type 'boolean) - -(defcustom avy-word-punc-regexp "[!-/:-@[-`{-~]" - "Regexp of punctuation chars that count as word starts for `avy-goto-word-1'. -When nil, punctuation chars will not be matched. - -\"[!-/:-@[-\\=`{-~]\" will match all printable punctuation chars." - :type 'regexp) - -(defcustom avy-goto-word-0-regexp "\\b\\sw" - "Regexp that determines positions for `avy-goto-word-0'." - :type '(choice - (const :tag "Default" "\\b\\sw") - (const :tag "Symbol" "\\_<\\(\\sw\\|\\s_\\)") - (const :tag "Not whitespace" "[^ \r\n\t]+") - (regexp :tag "Regex"))) - -(defcustom avy-ignored-modes '(image-mode doc-view-mode pdf-view-mode) - "List of modes to ignore when searching for candidates. -Typically, these modes don't use the text representation." - :type 'list) - -(defcustom avy-single-candidate-jump t - "In case there is only one candidate jumps directly to it." - :type 'boolean) - -(defcustom avy-del-last-char-by '(8 127) - "List of event types, i.e. key presses, that delete the last -character read. The default represents `C-h' and `DEL'. See -`event-convert-list'." - :type 'list) - -(defvar avy-ring (make-ring 20) - "Hold the window and point history.") - -(defvar avy-translate-char-function #'identity - "Function to translate user input key into another key. -For example, to make SPC do the same as ?a, use -\(lambda (c) (if (= c 32) ?a c)).") - -(defface avy-lead-face-0 - '((t (:foreground "white" :background "#4f57f9"))) - "Face used for first non-terminating leading chars.") - -(defface avy-lead-face-1 - '((t (:foreground "white" :background "gray"))) - "Face used for matched leading chars.") - -(defface avy-lead-face-2 - '((t (:foreground "white" :background "#f86bf3"))) - "Face used for leading chars.") - -(defface avy-lead-face - '((t (:foreground "white" :background "#e52b50"))) - "Face used for the leading chars.") - -(defface avy-background-face - '((t (:foreground "gray40"))) - "Face for whole window background during selection.") - -(defface avy-goto-char-timer-face - '((t (:inherit highlight))) - "Face for matches during reading chars using `avy-goto-char-timer'.") - -(defconst avy-lead-faces '(avy-lead-face - avy-lead-face-0 - avy-lead-face-2 - avy-lead-face - avy-lead-face-0 - avy-lead-face-2) - "Face sequence for `avy--overlay-at-full'.") - -(defvar avy-key-to-char-alist '((left . ?◀) - (right . ?▶) - (up . ?▲) - (down . ?▼) - (prior . ?△) - (next . ?▽)) - "An alist from non-character keys to printable chars used in avy overlays. -This alist must contain all keys used in `avy-keys' which are not -self-inserting keys and thus aren't read as characters.") - -;;* Internals -;;** Tree -(defmacro avy-multipop (lst n) - "Remove LST's first N elements and return them." - `(if (<= (length ,lst) ,n) - (prog1 ,lst - (setq ,lst nil)) - (prog1 ,lst - (setcdr - (nthcdr (1- ,n) (prog1 ,lst (setq ,lst (nthcdr ,n ,lst)))) - nil)))) - -(defun avy--de-bruijn (keys n) - "De Bruijn sequence for alphabet KEYS and subsequences of length N." - (let* ((k (length keys)) - (a (make-list (* n k) 0)) - sequence) - (cl-labels ((db (T p) - (if (> T n) - (if (eq (% n p) 0) - (setq sequence - (append sequence - (cl-subseq a 1 (1+ p))))) - (setf (nth T a) (nth (- T p) a)) - (db (1+ T) p) - (cl-loop for j from (1+ (nth (- T p) a)) to (1- k) do - (setf (nth T a) j) - (db (1+ T) T))))) - (db 1 1) - (mapcar (lambda (n) - (nth n keys)) - sequence)))) - -(defun avy--path-alist-1 (lst seq-len keys) - "Build a De Bruin sequence from LST. -SEQ-LEN is how many elements of KEYS it takes to identify a match." - (let ((db-seq (avy--de-bruijn keys seq-len)) - prev-pos prev-seq prev-win path-alist) - ;; The De Bruijn seq is cyclic, so append the seq-len - 1 first chars to - ;; the end. - (setq db-seq (nconc db-seq (cl-subseq db-seq 0 (1- seq-len)))) - (cl-labels ((subseq-and-pop () - (when (nth (1- seq-len) db-seq) - (prog1 (cl-subseq db-seq 0 seq-len) - (pop db-seq))))) - (while lst - (let* ((cur (car lst)) - (pos (cond - ;; ace-window has matches of the form (pos . wnd) - ((integerp (car cur)) (car cur)) - ;; avy-jump have form ((start . end) . wnd) - ((consp (car cur)) (caar cur)) - (t (error "Unexpected match representation: %s" cur)))) - (win (cdr cur)) - (path (if prev-pos - (let ((diff (if (eq win prev-win) - (- pos prev-pos) - 0))) - (when (and (> diff 0) (< diff seq-len)) - (while (and (nth (1- seq-len) db-seq) - (not - (eq 0 - (cl-search - (cl-subseq prev-seq diff) - (cl-subseq db-seq 0 seq-len))))) - (pop db-seq))) - (subseq-and-pop)) - (subseq-and-pop)))) - (if (not path) - (setq lst nil - path-alist nil) - (push (cons path (car lst)) path-alist) - (setq prev-pos pos - prev-seq path - prev-win win - lst (cdr lst)))))) - (nreverse path-alist))) - -(defun avy-order-closest (x) - (abs (- (caar x) (point)))) - -(defvar avy-command nil - "Store the current command symbol. -E.g. 'avy-goto-line or 'avy-goto-char.") - -(defun avy-tree (lst keys) - "Coerce LST into a balanced tree. -The degree of the tree is the length of KEYS. -KEYS are placed appropriately on internal nodes." - (let* ((len (length keys)) - (order-fn (cdr (assq avy-command avy-orders-alist))) - (lst (if order-fn - (cl-sort lst #'< :key order-fn) - lst))) - (cl-labels - ((rd (ls) - (let ((ln (length ls))) - (if (< ln len) - (cl-pairlis keys - (mapcar (lambda (x) (cons 'leaf x)) ls)) - (let ((ks (copy-sequence keys)) - res) - (dolist (s (avy-subdiv ln len)) - (push (cons (pop ks) - (if (eq s 1) - (cons 'leaf (pop ls)) - (rd (avy-multipop ls s)))) - res)) - (nreverse res)))))) - (rd lst)))) - -(defun avy-subdiv (n b) - "Distribute N in B terms in a balanced way." - (let* ((p (1- (floor (+ (log n b) 1e-6)))) - (x1 (expt b p)) - (x2 (* b x1)) - (delta (- n x2)) - (n2 (/ delta (- x2 x1))) - (n1 (- b n2 1))) - (append - (make-list n1 x1) - (list - (- n (* n1 x1) (* n2 x2))) - (make-list n2 x2)))) - -(defun avy-traverse (tree walker &optional recur-key) - "Traverse TREE generated by `avy-tree'. -WALKER is a function that takes KEYS and LEAF. - -RECUR-KEY is used in recursion. - -LEAF is a member of LST argument of `avy-tree'. - -KEYS is the path from the root of `avy-tree' to LEAF." - (dolist (br tree) - (let ((key (cons (car br) recur-key))) - (if (eq (cadr br) 'leaf) - (funcall walker key (cddr br)) - (avy-traverse (cdr br) walker key))))) - -(defvar avy-action nil - "Function to call at the end of select.") - -(defun avy-handler-default (char) - "The default handler for a bad CHAR." - (let (dispatch) - (cond ((setq dispatch (assoc char avy-dispatch-alist)) - (setq avy-action (cdr dispatch)) - (throw 'done 'restart)) - ((memq char '(27 ?\C-g)) - ;; exit silently - (throw 'done 'exit)) - ((eq char ??) - (avy-show-dispatch-help) - (throw 'done 'restart)) - ((mouse-event-p char) - (signal 'user-error (list "Mouse event not handled" char))) - (t - (message "No such candidate: %s, hit `C-g' to quit." - (if (characterp char) (string char) char)))))) - -(defun avy-show-dispatch-help () - "Display action shortucts in echo area." - (let ((len (length "avy-action-"))) - (message "%s" (mapconcat - (lambda (x) - (format "%s: %s" - (propertize - (char-to-string (car x)) - 'face 'aw-key-face) - (substring (symbol-name (cdr x)) len))) - avy-dispatch-alist - " ")))) - -(defvar avy-handler-function 'avy-handler-default - "A function to call for a bad `read-key' in `avy-read'.") - -(defvar avy-current-path "" - "Store the current incomplete path during `avy-read'.") - -(defun avy-mouse-event-window (char) - "If CHAR is a mouse event, return the window of the event if any or the selected window. -Return nil if not a mouse event." - (when (mouse-event-p char) - (cond ((windowp (posn-window (event-start char))) - (posn-window (event-start char))) - ((framep (posn-window (event-start char))) - (frame-selected-window (posn-window (event-start char)))) - (t (selected-window))))) - -(defun avy-read (tree display-fn cleanup-fn) - "Select a leaf from TREE using consecutive `read-key'. - -DISPLAY-FN should take CHAR and LEAF and signify that LEAFs -associated with CHAR will be selected if CHAR is pressed. This is -commonly done by adding a CHAR overlay at LEAF position. - -CLEANUP-FN should take no arguments and remove the effects of -multiple DISPLAY-FN invocations." - (catch 'done - (setq avy-current-path "") - (while tree - (let ((avy--leafs nil)) - (avy-traverse tree - (lambda (path leaf) - (push (cons path leaf) avy--leafs))) - (dolist (x avy--leafs) - (funcall display-fn (car x) (cdr x)))) - (let ((char (funcall avy-translate-char-function (read-key))) - window - branch) - (funcall cleanup-fn) - (if (setq window (avy-mouse-event-window char)) - (throw 'done (cons char window)) - ;; Ensure avy-current-path stores the full path prior to - ;; exit so other packages can utilize its value. - (setq avy-current-path - (concat avy-current-path (string (avy--key-to-char char)))) - (if (setq branch (assoc char tree)) - (if (eq (car (setq tree (cdr branch))) 'leaf) - (throw 'done (cdr tree))) - (funcall avy-handler-function char))))))) - -(defun avy-read-de-bruijn (lst keys) - "Select from LST dispatching on KEYS." - ;; In theory, the De Bruijn sequence B(k,n) has k^n subsequences of length n - ;; (the path length) usable as paths, thus that's the lower bound. Due to - ;; partially overlapping matches, not all subsequences may be usable, so it's - ;; possible that the path-len must be incremented, e.g., if we're matching - ;; for x and a buffer contains xaxbxcx only every second subsequence is - ;; usable for the four matches. - (catch 'done - (let* ((path-len (ceiling (log (length lst) (length keys)))) - (alist (avy--path-alist-1 lst path-len keys))) - (while (not alist) - (cl-incf path-len) - (setq alist (avy--path-alist-1 lst path-len keys))) - (let* ((len (length (caar alist))) - (i 0)) - (setq avy-current-path "") - (while (< i len) - (dolist (x (reverse alist)) - (avy--overlay-at-full (reverse (car x)) (cdr x))) - (let ((char (funcall avy-translate-char-function (read-key)))) - (avy--remove-leading-chars) - (setq alist - (delq nil - (mapcar (lambda (x) - (when (eq (caar x) char) - (cons (cdr (car x)) (cdr x)))) - alist))) - (setq avy-current-path - (concat avy-current-path (string (avy--key-to-char char)))) - (cl-incf i) - (unless alist - (funcall avy-handler-function char)))) - (cdar alist))))) - -(defun avy-read-words (lst words) - "Select from LST using WORDS." - (catch 'done - (let ((num-words (length words)) - (num-entries (length lst)) - alist) - ;; If there are not enough words to cover all the candidates, - ;; we use a De Bruijn sequence to generate the remaining ones. - (when (< num-words num-entries) - (let ((keys avy-keys) - (bad-keys '(?a ?e ?i ?o ?u ?y)) - (path-len 1) - (num-remaining (- num-entries num-words)) - tmp-alist) - ;; Delete all keys which could lead to duplicates. - ;; We want at least three keys left to work with. - (dolist (x bad-keys) - (when (memq x keys) - (setq keys (delq ?a keys)))) - (when (< (length keys) 3) - (signal 'user-error - '("Please add more keys to the variable `avy-keys'."))) - ;; Generate the sequence and add the keys to the existing words. - (while (not tmp-alist) - (cl-incf path-len) - (setq tmp-alist (avy--path-alist-1 lst path-len keys))) - (while (>= (cl-decf num-remaining) 0) - (push (mapconcat 'string (caar tmp-alist) nil) (cdr (last words))) - (setq tmp-alist (cdr tmp-alist))))) - (dolist (x lst) - (push (cons (string-to-list (pop words)) x) alist)) - (setq avy-current-path "") - (while (or (> (length alist) 1) - (caar alist)) - (dolist (x (reverse alist)) - (avy--overlay-at-full (reverse (car x)) (cdr x))) - (let ((char (funcall avy-translate-char-function (read-key)))) - (avy--remove-leading-chars) - (setq alist - (delq nil - (mapcar (lambda (x) - (when (eq (caar x) char) - (cons (cdr (car x)) (cdr x)))) - alist))) - (setq avy-current-path - (concat avy-current-path (string (avy--key-to-char char)))) - (unless alist - (funcall avy-handler-function char)))) - (cdar alist)))) - -;;** Rest -(defun avy-window-list () - "Return a list of windows depending on `avy-all-windows'." - (cond ((eq avy-all-windows 'all-frames) - (cl-mapcan #'window-list (frame-list))) - - ((eq avy-all-windows t) - (window-list)) - - ((null avy-all-windows) - (list (selected-window))) - - (t - (error "Unrecognized option: %S" avy-all-windows)))) - -(defcustom avy-all-windows-alt nil - "The alternative `avy-all-windows' for use with \\[universal-argument]." - :type '(choice - (const :tag "Current window" nil) - (const :tag "All windows on the current frame" t) - (const :tag "All windows on all frames" all-frames))) - -(defmacro avy-dowindows (flip &rest body) - "Depending on FLIP and `avy-all-windows' run BODY in each or selected window." - (declare (indent 1) - (debug (form body))) - `(let ((avy-all-windows (if ,flip - avy-all-windows-alt - avy-all-windows))) - (dolist (wnd (avy-window-list)) - (with-selected-window wnd - (unless (memq major-mode avy-ignored-modes) - ,@body))))) - -(defun avy-resume () - "Stub to hold last avy command. -Commands using `avy-with' macro can be resumed." - (interactive)) - -(defmacro avy-with (command &rest body) - "Set `avy-keys' according to COMMAND and execute BODY. -Set `avy-style' according to COMMMAND as well." - (declare (indent 1) - (debug (form body))) - `(let ((avy-keys (or (cdr (assq ',command avy-keys-alist)) - avy-keys)) - (avy-style (or (cdr (assq ',command avy-styles-alist)) - avy-style)) - (avy-command ',command)) - (setq avy-action nil) - (setf (symbol-function 'avy-resume) - (lambda () - (interactive) - ,@body)) - ,@body)) - -(defun avy-action-goto (pt) - "Goto PT." - (let ((frame (window-frame (selected-window)))) - (unless (equal frame (selected-frame)) - (select-frame-set-input-focus frame) - (raise-frame frame)) - (goto-char pt))) - -(defun avy-forward-item () - (if (eq avy-command 'avy-goto-line) - (end-of-line) - (forward-sexp)) - (point)) - -(defun avy-action-mark (pt) - "Mark sexp at PT." - (goto-char pt) - (set-mark (point)) - (avy-forward-item)) - -(defun avy-action-copy (pt) - "Copy sexp starting on PT." - (save-excursion - (let (str) - (goto-char pt) - (avy-forward-item) - (setq str (buffer-substring pt (point))) - (kill-new str) - (message "Copied: %s" str))) - (let ((dat (ring-ref avy-ring 0))) - (select-frame-set-input-focus - (window-frame (cdr dat))) - (select-window (cdr dat)) - (goto-char (car dat)))) - -(defun avy-action-yank (pt) - "Yank sexp starting at PT at the current point." - (avy-action-copy pt) - (yank) - t) - -(defun avy-action-kill-move (pt) - "Kill sexp at PT and move there." - (goto-char pt) - (avy-forward-item) - (kill-region pt (point)) - (message "Killed: %s" (current-kill 0)) - (point)) - -(defun avy-action-kill-stay (pt) - "Kill sexp at PT." - (save-excursion - (goto-char pt) - (avy-forward-item) - (kill-region pt (point)) - (just-one-space)) - (message "Killed: %s" (current-kill 0)) - (select-window - (cdr - (ring-ref avy-ring 0))) - t) - -(defun avy-action-zap-to-char (pt) - "Kill from point up to PT." - (if (> pt (point)) - (kill-region (point) pt) - (kill-region pt (point)))) - -(defun avy-action-teleport (pt) - "Kill sexp starting on PT and yank into the current location." - (avy-action-kill-stay pt) - (select-window - (cdr - (ring-ref avy-ring 0))) - (save-excursion - (yank)) - t) - -(declare-function flyspell-correct-word-before-point "flyspell") - -(defun avy-action-ispell (pt) - "Auto correct word at PT." - (save-excursion - (goto-char pt) - (cond - ((eq avy-command 'avy-goto-line) - (ispell-region - (line-beginning-position) - (line-end-position))) - ((bound-and-true-p flyspell-mode) - (flyspell-correct-word-before-point)) - ((looking-at-p "\\b") - (ispell-word)) - (t - (progn - (backward-word) - (when (looking-at-p "\\b") - (ispell-word))))))) - -(defvar avy-pre-action #'avy-pre-action-default - "Function to call before `avy-action' is called.") - -(defun avy-pre-action-default (res) - (avy-push-mark) - (when (and (consp res) - (windowp (cdr res))) - (let* ((window (cdr res)) - (frame (window-frame window))) - (unless (equal frame (selected-frame)) - (select-frame-set-input-focus frame)) - (select-window window)))) - -(defun avy--process-1 (candidates overlay-fn &optional cleanup-fn) - (let ((len (length candidates))) - (cond ((= len 0) - nil) - ((and (= len 1) avy-single-candidate-jump) - (car candidates)) - (t - (unwind-protect - (progn - (avy--make-backgrounds - (avy-window-list)) - (cond ((eq avy-style 'de-bruijn) - (avy-read-de-bruijn - candidates avy-keys)) - ((eq avy-style 'words) - (avy-read-words - candidates avy-words)) - (t - (avy-read (avy-tree candidates avy-keys) - overlay-fn - (or cleanup-fn #'avy--remove-leading-chars))))) - (avy--done)))))) - -(defvar avy-last-candidates nil - "Store the last candidate list.") - -(defun avy--last-candidates-cycle (advancer) - (let* ((avy-last-candidates - (cl-remove-if-not - (lambda (x) (equal (cdr x) (selected-window))) - avy-last-candidates)) - (min-dist - (apply #'min - (mapcar (lambda (x) (abs (- (caar x) (point)))) avy-last-candidates))) - (pos - (cl-position-if - (lambda (x) - (= (- (caar x) (point)) min-dist)) - avy-last-candidates))) - (funcall advancer pos avy-last-candidates))) - -(defun avy-prev () - "Go to the previous candidate of the last `avy-read'." - (interactive) - (avy--last-candidates-cycle - (lambda (pos lst) - (when (> pos 0) - (goto-char (caar (nth (1- pos) lst))))))) - -(defun avy-next () - "Go to the next candidate of the last `avy-read'." - (interactive) - (avy--last-candidates-cycle - (lambda (pos lst) - (when (< pos (1- (length lst))) - (goto-char (caar (nth (1+ pos) lst))))))) - -(defun avy-process (candidates &optional overlay-fn cleanup-fn) - "Select one of CANDIDATES using `avy-read'. -Use OVERLAY-FN to visualize the decision overlay. -CLEANUP-FN should take no arguments and remove the effects of -multiple OVERLAY-FN invocations." - (setq overlay-fn (or overlay-fn (avy--style-fn avy-style))) - (setq cleanup-fn (or cleanup-fn #'avy--remove-leading-chars)) - (unless (and (consp (car candidates)) - (windowp (cdar candidates))) - (setq candidates - (mapcar (lambda (x) (cons x (selected-window))) - candidates))) - (setq avy-last-candidates (copy-sequence candidates)) - (let ((original-cands (copy-sequence candidates)) - (res (avy--process-1 candidates overlay-fn cleanup-fn))) - (cond - ((null res) - (message "zero candidates") - t) - ((eq res 'restart) - (avy-process original-cands overlay-fn cleanup-fn)) - ;; ignore exit from `avy-handler-function' - ((eq res 'exit)) - (t - (funcall avy-pre-action res) - (setq res (car res)) - (funcall (or avy-action 'avy-action-goto) - (if (consp res) - (car res) - res)) - res)))) - -(define-obsolete-function-alias 'avy--process 'avy-process - "0.4.0") - -(defvar avy--overlays-back nil - "Hold overlays for when `avy-background' is t.") - -(defun avy--make-backgrounds (wnd-list) - "Create a dim background overlay for each window on WND-LIST." - (when avy-background - (setq avy--overlays-back - (mapcar (lambda (w) - (let ((ol (make-overlay - (window-start w) - (window-end w) - (window-buffer w)))) - (overlay-put ol 'face 'avy-background-face) - (overlay-put ol 'window w) - ol)) - wnd-list)))) - -(defun avy--done () - "Clean up overlays." - (mapc #'delete-overlay avy--overlays-back) - (setq avy--overlays-back nil) - (avy--remove-leading-chars)) - -(defun avy--visible-p (s) - (let ((invisible (get-char-property s 'invisible))) - (or (null invisible) - (eq t buffer-invisibility-spec) - (null (assoc invisible buffer-invisibility-spec))))) - -(defun avy--next-visible-point () - "Return the next closest point without `invisible' property." - (let ((s (point))) - (while (and (not (= (point-max) (setq s (next-char-property-change s)))) - (not (avy--visible-p s)))) - s)) - -(defun avy--next-invisible-point () - "Return the next closest point with `invisible' property." - (let ((s (point))) - (while (and (not (= (point-max) (setq s (next-char-property-change s)))) - (avy--visible-p s))) - s)) - -(defun avy--find-visible-regions (rbeg rend) - "Return a list of all visible regions between RBEG and REND." - (setq rbeg (max rbeg (point-min))) - (setq rend (min rend (point-max))) - (when (< rbeg rend) - (let (visibles beg) - (save-excursion - (save-restriction - (narrow-to-region rbeg rend) - (setq beg (goto-char (point-min))) - (while (not (= (point) (point-max))) - (goto-char (avy--next-invisible-point)) - (push (cons beg (point)) visibles) - (setq beg (goto-char (avy--next-visible-point)))) - (nreverse visibles)))))) - -(defun avy--regex-candidates (regex &optional beg end pred group) - "Return all elements that match REGEX. -Each element of the list is ((BEG . END) . WND) -When PRED is non-nil, it's a filter for matching point positions. -When GROUP is non-nil, (BEG . END) should delimit that regex group." - (setq group (or group 0)) - (let ((case-fold-search (or avy-case-fold-search - (string= regex (downcase regex)))) - candidates) - (avy-dowindows current-prefix-arg - (dolist (pair (avy--find-visible-regions - (or beg (window-start)) - (or end (window-end (selected-window) t)))) - (save-excursion - (goto-char (car pair)) - (while (re-search-forward regex (cdr pair) t) - (when (avy--visible-p (1- (point))) - (when (or (null pred) - (funcall pred)) - (push (cons (cons (match-beginning group) - (match-end group)) - wnd) candidates))))))) - (nreverse candidates))) - -(defvar avy--overlay-offset 0 - "The offset to apply in `avy--overlay'.") - -(defvar avy--overlays-lead nil - "Hold overlays for leading chars.") - -(defun avy--remove-leading-chars () - "Remove leading char overlays." - (mapc #'delete-overlay avy--overlays-lead) - (setq avy--overlays-lead nil)) - -(defun avy--old-str (pt wnd) - "Return a one-char string at PT in WND." - (let ((old-str (with-selected-window wnd - (buffer-substring pt (1+ pt))))) - (if avy-background - (propertize old-str 'face 'avy-background-face) - old-str))) - -(defun avy--overlay (str beg end wnd &optional compose-fn) - "Create an overlay with STR from BEG to END in WND. -COMPOSE-FN is a lambda that concatenates the old string at BEG with STR." - (let ((eob (with-selected-window wnd (point-max)))) - (when (<= beg eob) - (let* ((beg (+ beg avy--overlay-offset)) - (ol (make-overlay beg (or end (1+ beg)) (window-buffer wnd))) - (old-str (if (eq beg eob) "" (avy--old-str beg wnd))) - (os-line-prefix (get-text-property 0 'line-prefix old-str)) - (os-wrap-prefix (get-text-property 0 'wrap-prefix old-str)) - other-ol) - (when os-line-prefix - (add-text-properties 0 1 `(line-prefix ,os-line-prefix) str)) - (when os-wrap-prefix - (add-text-properties 0 1 `(wrap-prefix ,os-wrap-prefix) str)) - (when (setq other-ol (cl-find-if - (lambda (o) (overlay-get o 'goto-address)) - (overlays-at beg))) - (add-text-properties - 0 (length old-str) - `(face ,(overlay-get other-ol 'face)) old-str)) - (overlay-put ol 'window wnd) - (overlay-put ol 'category 'avy) - (overlay-put ol (if (eq beg eob) - 'after-string - 'display) - (funcall - (or compose-fn #'concat) - str old-str)) - (push ol avy--overlays-lead))))) - -(defcustom avy-highlight-first nil - "When non-nil highlight the first decision char with `avy-lead-face-0'. -Do this even when the char is terminating." - :type 'boolean) - -(defun avy--key-to-char (c) - "If C is no character, translate it using `avy-key-to-char-alist'." - (cond ((characterp c) c) - ((cdr (assoc c avy-key-to-char-alist))) - ((mouse-event-p c) c) - (t - (error "Unknown key %s" c)))) - -(defun avy-candidate-beg (leaf) - "Return the start position for LEAF." - (cond ((numberp leaf) - leaf) - ((consp (car leaf)) - (caar leaf)) - (t - (car leaf)))) - -(defun avy-candidate-end (leaf) - "Return the end position for LEAF." - (cond ((numberp leaf) - leaf) - ((consp (car leaf)) - (cdar leaf)) - (t - (car leaf)))) - -(defun avy-candidate-wnd (leaf) - "Return the window for LEAF." - (if (consp leaf) - (cdr leaf) - (selected-window))) - -(defun avy--overlay-pre (path leaf) - "Create an overlay with PATH at LEAF. -PATH is a list of keys from tree root to LEAF. -LEAF is normally ((BEG . END) . WND)." - (let* ((path (mapcar #'avy--key-to-char path)) - (str (propertize (apply #'string (reverse path)) - 'face 'avy-lead-face))) - (when (or avy-highlight-first (> (length str) 1)) - (set-text-properties 0 1 '(face avy-lead-face-0) str)) - (setq str (concat - (propertize avy-current-path - 'face 'avy-lead-face-1) - str)) - (avy--overlay - str - (avy-candidate-beg leaf) nil - (avy-candidate-wnd leaf)))) - -(defun avy--overlay-at (path leaf) - "Create an overlay with PATH at LEAF. -PATH is a list of keys from tree root to LEAF. -LEAF is normally ((BEG . END) . WND)." - (let* ((path (mapcar #'avy--key-to-char path)) - (str (propertize - (string (car (last path))) - 'face 'avy-lead-face))) - (avy--overlay - str - (avy-candidate-beg leaf) nil - (avy-candidate-wnd leaf) - (lambda (str old-str) - (cond ((string= old-str "\n") - (concat str "\n")) - ;; add padding for wide-width character - ((eq (string-width old-str) 2) - (concat str " ")) - (t - str)))))) - -(defun avy--overlay-at-full (path leaf) - "Create an overlay with PATH at LEAF. -PATH is a list of keys from tree root to LEAF. -LEAF is normally ((BEG . END) . WND)." - (let* ((path (mapcar #'avy--key-to-char path)) - (str (propertize - (apply #'string (reverse path)) - 'face 'avy-lead-face)) - (len (length path)) - (beg (avy-candidate-beg leaf)) - (wnd (cdr leaf)) - end) - (dotimes (i len) - (set-text-properties i (1+ i) - `(face ,(nth i avy-lead-faces)) - str)) - (when (eq avy-style 'de-bruijn) - (setq str (concat - (propertize avy-current-path - 'face 'avy-lead-face-1) - str)) - (setq len (length str))) - (with-selected-window wnd - (save-excursion - (goto-char beg) - (let* ((lep (if (bound-and-true-p visual-line-mode) - (save-excursion - (end-of-visual-line) - (point)) - (line-end-position))) - ;; `end-of-visual-line' is bugged sometimes - (lep (if (< lep beg) - (line-end-position) - lep)) - (len-and-str (avy--update-offset-and-str len str lep))) - (setq len (car len-and-str)) - (setq str (cdr len-and-str)) - (setq end (if (= beg lep) - (1+ beg) - (min (+ beg - (if (eq (char-after) ?\t) - 1 - len)) - lep))) - (when (and (bound-and-true-p visual-line-mode) - (> len (- end beg)) - (not (eq lep beg))) - (setq len (- end beg)) - (let ((old-str (apply #'string (reverse path)))) - (setq str - (substring - (propertize - old-str - 'face - (if (= (length old-str) 1) - 'avy-lead-face - 'avy-lead-face-0)) - 0 len))))))) - (avy--overlay - str beg end wnd - (lambda (str old-str) - (cond ((string= old-str "\n") - (concat str "\n")) - ((string= old-str "\t") - (concat str (make-string (max (- tab-width len) 0) ?\ ))) - (t - ;; add padding for wide-width character - (if (eq (string-width old-str) 2) - (concat str " ") - str))))))) - -(defun avy--overlay-post (path leaf) - "Create an overlay with PATH at LEAF. -PATH is a list of keys from tree root to LEAF. -LEAF is normally ((BEG . END) . WND)." - (let* ((path (mapcar #'avy--key-to-char path)) - (str (propertize (apply #'string (reverse path)) - 'face 'avy-lead-face))) - (when (or avy-highlight-first (> (length str) 1)) - (set-text-properties 0 1 '(face avy-lead-face-0) str)) - (setq str (concat - (propertize avy-current-path - 'face 'avy-lead-face-1) - str)) - (avy--overlay - str - (avy-candidate-end leaf) nil - (avy-candidate-wnd leaf)))) - -(defun avy--update-offset-and-str (offset str lep) - "Recalculate the length of the new overlay at point. - -OFFSET is the previous overlay length. -STR is the overlay string that we wish to add. -LEP is the line end position. - -We want to add an overlay between point and END=point+OFFSET. -When other overlays already exist between point and END, set -OFFSET to be the difference between the start of the first -overlay and point. This is equivalent to truncating our new -overlay, so that it doesn't intersect with overlays that already -exist." - (let* ((wnd (selected-window)) - (beg (point)) - (oov (delq nil - (mapcar - (lambda (o) - (and (eq (overlay-get o 'category) 'avy) - (eq (overlay-get o 'window) wnd) - (overlay-start o))) - (overlays-in beg (min (+ beg offset) lep)))))) - (when oov - (setq offset (- (apply #'min oov) beg)) - (setq str (substring str 0 offset))) - (let ((other-ov (cl-find-if - (lambda (o) - (and (eq (overlay-get o 'category) 'avy) - (eq (overlay-start o) beg) - (not (eq (overlay-get o 'window) wnd)))) - (overlays-in (point) (min (+ (point) offset) lep))))) - (when (and other-ov - (> (overlay-end other-ov) - (+ beg offset))) - (setq str (concat str (buffer-substring - (+ beg offset) - (overlay-end other-ov)))) - (setq offset (- (overlay-end other-ov) - beg)))) - (cons offset str))) - -(defun avy--style-fn (style) - "Transform STYLE symbol to a style function." - (cl-case style - (pre #'avy--overlay-pre) - (at #'avy--overlay-at) - (at-full 'avy--overlay-at-full) - (post #'avy--overlay-post) - (de-bruijn #'avy--overlay-at-full) - (words #'avy--overlay-at-full) - (ignore #'ignore) - (t (error "Unexpected style %S" style)))) - -(cl-defun avy-jump (regex &key window-flip beg end action pred) - "Jump to REGEX. -The window scope is determined by `avy-all-windows'. -When WINDOW-FLIP is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched. -ACTION is a function that takes point position as an argument. -When PRED is non-nil, it's a filter for matching point positions." - (setq avy-action (or action avy-action)) - (let ((avy-all-windows - (if window-flip - (not avy-all-windows) - avy-all-windows))) - (avy-process - (avy--regex-candidates regex beg end pred)))) - -(defun avy--generic-jump (regex window-flip &optional beg end) - "Jump to REGEX. -The window scope is determined by `avy-all-windows'. -When WINDOW-FLIP is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched." - (declare (obsolete avy-jump "0.4.0")) - (let ((avy-all-windows - (if window-flip - (not avy-all-windows) - avy-all-windows))) - (avy-process - (avy--regex-candidates regex beg end)))) - -;;* Commands -;;;###autoload -(defun avy-goto-char (char &optional arg) - "Jump to the currently visible CHAR. -The window scope is determined by `avy-all-windows' (ARG negates it)." - (interactive (list (read-char "char: " t) - current-prefix-arg)) - (avy-with avy-goto-char - (avy-jump - (if (= 13 char) - "\n" - (regexp-quote (string char))) - :window-flip arg))) - -;;;###autoload -(defun avy-goto-char-in-line (char) - "Jump to the currently visible CHAR in the current line." - (interactive (list (read-char "char: " t))) - (avy-with avy-goto-char - (avy-jump - (regexp-quote (string char)) - :beg (line-beginning-position) - :end (line-end-position)))) - -;;;###autoload -(defun avy-goto-char-2 (char1 char2 &optional arg beg end) - "Jump to the currently visible CHAR1 followed by CHAR2. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched." - (interactive (list (read-char "char 1: " t) - (read-char "char 2: " t) - current-prefix-arg - nil nil)) - (when (eq char1 ? -) - (setq char1 ?\n)) - (when (eq char2 ? -) - (setq char2 ?\n)) - (avy-with avy-goto-char-2 - (avy-jump - (regexp-quote (string char1 char2)) - :window-flip arg - :beg beg - :end end))) - -;;;###autoload -(defun avy-goto-char-2-above (char1 char2 &optional arg) - "Jump to the currently visible CHAR1 followed by CHAR2. -This is a scoped version of `avy-goto-char-2', where the scope is -the visible part of the current buffer up to point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive (list (read-char "char 1: " t) - (read-char "char 2: " t) - current-prefix-arg)) - (avy-with avy-goto-char-2-above - (avy-goto-char-2 - char1 char2 arg - (window-start) (point)))) - -;;;###autoload -(defun avy-goto-char-2-below (char1 char2 &optional arg) - "Jump to the currently visible CHAR1 followed by CHAR2. -This is a scoped version of `avy-goto-char-2', where the scope is -the visible part of the current buffer following point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive (list (read-char "char 1: " t) - (read-char "char 2: " t) - current-prefix-arg)) - (avy-with avy-goto-char-2-below - (avy-goto-char-2 - char1 char2 arg - (point) (window-end (selected-window) t)))) - -;;;###autoload -(defun avy-isearch () - "Jump to one of the current isearch candidates." - (interactive) - (avy-with avy-isearch - (let ((avy-background nil)) - (avy-process - (avy--regex-candidates (if isearch-regexp - isearch-string - (regexp-quote isearch-string)))) - (isearch-done)))) - -;;;###autoload -(defun avy-goto-word-0 (arg &optional beg end) - "Jump to a word start. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched." - (interactive "P") - (avy-with avy-goto-word-0 - (avy-jump avy-goto-word-0-regexp - :window-flip arg - :beg beg - :end end))) - -(defun avy-goto-word-0-above (arg) - "Jump to a word start between window start and point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive "P") - (avy-with avy-goto-word-0 - (avy-goto-word-0 arg (window-start) (point)))) - -(defun avy-goto-word-0-below (arg) - "Jump to a word start between point and window end. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive "P") - (avy-with avy-goto-word-0 - (avy-goto-word-0 arg (point) (window-end (selected-window) t)))) - -;;;###autoload -(defun avy-goto-word-1 (char &optional arg beg end symbol) - "Jump to the currently visible CHAR at a word start. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched. -When SYMBOL is non-nil, jump to symbol start instead of word start." - (interactive (list (read-char "char: " t) - current-prefix-arg)) - (avy-with avy-goto-word-1 - (let* ((str (string char)) - (regex (cond ((string= str ".") - "\\.") - ((and avy-word-punc-regexp - (string-match avy-word-punc-regexp str)) - (regexp-quote str)) - ((<= char 26) - str) - (t - (concat - (if symbol "\\_<" "\\b") - str))))) - (avy-jump regex - :window-flip arg - :beg beg - :end end)))) - -;;;###autoload -(defun avy-goto-word-1-above (char &optional arg) - "Jump to the currently visible CHAR at a word start. -This is a scoped version of `avy-goto-word-1', where the scope is -the visible part of the current buffer up to point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive (list (read-char "char: " t) - current-prefix-arg)) - (avy-with avy-goto-word-1 - (avy-goto-word-1 char arg (window-start) (point)))) - -;;;###autoload -(defun avy-goto-word-1-below (char &optional arg) - "Jump to the currently visible CHAR at a word start. -This is a scoped version of `avy-goto-word-1', where the scope is -the visible part of the current buffer following point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive (list (read-char "char: " t) - current-prefix-arg)) - (avy-with avy-goto-word-1 - (avy-goto-word-1 char arg (point) (window-end (selected-window) t)))) - -;;;###autoload -(defun avy-goto-symbol-1 (char &optional arg) - "Jump to the currently visible CHAR at a symbol start. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive (list (read-char "char: " t) - current-prefix-arg)) - (avy-with avy-goto-symbol-1 - (avy-goto-word-1 char arg nil nil t))) - -;;;###autoload -(defun avy-goto-symbol-1-above (char &optional arg) - "Jump to the currently visible CHAR at a symbol start. -This is a scoped version of `avy-goto-symbol-1', where the scope is -the visible part of the current buffer up to point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive (list (read-char "char: " t) - current-prefix-arg)) - (avy-with avy-goto-symbol-1-above - (avy-goto-word-1 char arg (window-start) (point) t))) - -;;;###autoload -(defun avy-goto-symbol-1-below (char &optional arg) - "Jump to the currently visible CHAR at a symbol start. -This is a scoped version of `avy-goto-symbol-1', where the scope is -the visible part of the current buffer following point. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive (list (read-char "char: " t) - current-prefix-arg)) - (avy-with avy-goto-symbol-1-below - (avy-goto-word-1 char arg (point) (window-end (selected-window) t) t))) - -(declare-function subword-backward "subword") -(defvar subword-backward-regexp) - -(defcustom avy-subword-extra-word-chars '(?{ ?= ?} ?* ?: ?> ?<) - "A list of characters that should temporarily match \"\\w\". -This variable is used by `avy-goto-subword-0' and `avy-goto-subword-1'." - :type '(repeat character)) - -;;;###autoload -(defun avy-goto-subword-0 (&optional arg predicate beg end) - "Jump to a word or subword start. -The window scope is determined by `avy-all-windows' (ARG negates it). - -When PREDICATE is non-nil it's a function of zero parameters that -should return true. - -BEG and END narrow the scope where candidates are searched." - (interactive "P") - (require 'subword) - (avy-with avy-goto-subword-0 - (let ((case-fold-search nil) - (subword-backward-regexp - "\\(\\(\\W\\|[[:lower:][:digit:]]\\)\\([!-/:@`~[:upper:]]+\\W*\\)\\|\\W\\w+\\)") - candidates) - (avy-dowindows arg - (let ((syn-tbl (copy-syntax-table))) - (dolist (char avy-subword-extra-word-chars) - (modify-syntax-entry char "w" syn-tbl)) - (with-syntax-table syn-tbl - (let ((ws (or beg (window-start))) - window-cands) - (save-excursion - (goto-char (or end (window-end (selected-window) t))) - (subword-backward) - (while (> (point) ws) - (when (or (null predicate) - (and predicate (funcall predicate))) - (unless (not (avy--visible-p (point))) - (push (cons (point) (selected-window)) window-cands))) - (subword-backward)) - (and (= (point) ws) - (or (null predicate) - (and predicate (funcall predicate))) - (not (get-char-property (point) 'invisible)) - (push (cons (point) (selected-window)) window-cands))) - (setq candidates (nconc candidates window-cands)))))) - (avy-process candidates)))) - -;;;###autoload -(defun avy-goto-subword-1 (char &optional arg) - "Jump to the currently visible CHAR at a subword start. -The window scope is determined by `avy-all-windows' (ARG negates it). -The case of CHAR is ignored." - (interactive (list (read-char "char: " t) - current-prefix-arg)) - (avy-with avy-goto-subword-1 - (let ((char (downcase char))) - (avy-goto-subword-0 - arg (lambda () - (and (char-after) - (eq (downcase (char-after)) char))))))) - -;;;###autoload -(defun avy-goto-word-or-subword-1 () - "Forward to `avy-goto-subword-1' or `avy-goto-word-1'. -Which one depends on variable `subword-mode'." - (interactive) - (if (bound-and-true-p subword-mode) - (call-interactively #'avy-goto-subword-1) - (call-interactively #'avy-goto-word-1))) - -(defvar visual-line-mode) - -(defcustom avy-indent-line-overlay nil - "When non-nil, `avy-goto-line' will display the line overlay next to the first non-whitespace character of each line." - :type 'boolean) - -(defun avy--line-cands (&optional arg beg end bottom-up) - "Get candidates for selecting a line. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched. -When BOTTOM-UP is non-nil, display avy candidates from top to bottom" - (let (candidates) - (avy-dowindows arg - (let ((ws (or beg (window-start)))) - (save-excursion - (save-restriction - (narrow-to-region ws (or end (window-end (selected-window) t))) - (goto-char (point-min)) - (while (< (point) (point-max)) - (when (member (get-char-property - (max (1- (point)) ws) 'invisible) '(nil org-link)) - (push (cons - (if (eq avy-style 'post) - (line-end-position) - (save-excursion - (when avy-indent-line-overlay - (skip-chars-forward " \t")) - (point))) - (selected-window)) candidates)) - (if visual-line-mode - (progn - (setq temporary-goal-column 0) - (line-move-visual 1 t)) - (forward-line 1))))))) - (if bottom-up - candidates - (nreverse candidates)))) - -(defun avy--linum-strings () - "Get strings for `avy-linum-mode'." - (let* ((lines (mapcar #'car (avy--line-cands))) - (line-tree (avy-tree lines avy-keys)) - (line-list nil)) - (avy-traverse - line-tree - (lambda (path _leaf) - (let ((str (propertize (apply #'string (reverse path)) - 'face 'avy-lead-face))) - (when (> (length str) 1) - (set-text-properties 0 1 '(face avy-lead-face-0) str)) - (push str line-list)))) - (nreverse line-list))) - -(defvar linum-available) -(defvar linum-overlays) -(defvar linum-format) -(declare-function linum--face-width "linum") - -(define-minor-mode avy-linum-mode - "Minor mode that uses avy hints for `linum-mode'." - :group 'avy - (if avy-linum-mode - (progn - (require 'linum) - (advice-add 'linum-update-window :around 'avy--linum-update-window) - (linum-mode 1)) - (advice-remove 'linum-update-window 'avy--linum-update-window) - (linum-mode -1))) - -(defun avy--linum-update-window (_ win) - "Update line numbers for the portion visible in window WIN." - (goto-char (window-start win)) - (let ((line (line-number-at-pos)) - (limit (window-end win t)) - (fmt (cond ((stringp linum-format) linum-format) - ((eq linum-format 'dynamic) - (let ((w (length (number-to-string - (count-lines (point-min) (point-max)))))) - (concat "%" (number-to-string w) "d"))))) - (width 0) - (avy-strs (when avy-linum-mode - (avy--linum-strings)))) - (run-hooks 'linum-before-numbering-hook) - ;; Create an overlay (or reuse an existing one) for each - ;; line visible in this window, if necessary. - (while (and (not (eobp)) (< (point) limit)) - (let* ((str - (cond (avy-linum-mode - (pop avy-strs)) - (fmt - (propertize (format fmt line) 'face 'linum)) - (t - (funcall linum-format line)))) - (visited (catch 'visited - (dolist (o (overlays-in (point) (point))) - (when (equal-including-properties - (overlay-get o 'linum-str) str) - (unless (memq o linum-overlays) - (push o linum-overlays)) - (setq linum-available (delq o linum-available)) - (throw 'visited t)))))) - (setq width (max width (length str))) - (unless visited - (let ((ov (if (null linum-available) - (make-overlay (point) (point)) - (move-overlay (pop linum-available) (point) (point))))) - (push ov linum-overlays) - (overlay-put ov 'before-string - (propertize " " 'display `((margin left-margin) ,str))) - (overlay-put ov 'linum-str str)))) - ;; Text may contain those nasty intangible properties, but that - ;; shouldn't prevent us from counting those lines. - (let ((inhibit-point-motion-hooks t)) - (forward-line)) - (setq line (1+ line))) - (when (display-graphic-p) - (setq width (ceiling - (/ (* width 1.0 (linum--face-width 'linum)) - (frame-char-width))))) - (set-window-margins win width (cdr (window-margins win))))) - -(defun avy--line (&optional arg beg end bottom-up) - "Select a line. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'. -BEG and END narrow the scope where candidates are searched. -When BOTTOM-UP is non-nil, display avy candidates from top to bottom" - (let ((avy-action #'identity) - (avy-style (if avy-linum-mode - (progn - (message "Goto line:") - 'ignore) - avy-style))) - (avy-process - (avy--line-cands arg beg end bottom-up)))) - -;;;###autoload -(defun avy-goto-line (&optional arg) - "Jump to a line start in current buffer. - -When ARG is 1, jump to lines currently visible, with the option -to cancel to `goto-line' by entering a number. - -When ARG is 4, negate the window scope determined by -`avy-all-windows'. - -Otherwise, forward to `goto-line' with ARG." - (interactive "p") - (setq arg (or arg 1)) - (if (not (memq arg '(1 4))) - (progn - (goto-char (point-min)) - (forward-line (1- arg))) - (avy-with avy-goto-line - (let* ((avy-handler-old avy-handler-function) - (avy-handler-function - (lambda (char) - (if (or (< char ?0) - (> char ?9)) - (funcall avy-handler-old char) - (let ((line (read-from-minibuffer - "Goto line: " (string char)))) - (when line - (avy-push-mark) - (save-restriction - (widen) - (goto-char (point-min)) - (forward-line (1- (string-to-number line)))) - (throw 'done 'exit)))))) - (r (avy--line (eq arg 4)))) - (unless (eq r t) - (avy-action-goto r)))))) - -;;;###autoload -(defun avy-goto-line-above (&optional offset bottom-up) - "Goto visible line above the cursor. -OFFSET changes the distance between the closest key to the cursor and -the cursor -When BOTTOM-UP is non-nil, display avy candidates from top to bottom" - (interactive) - (if offset - (setq offset (+ 2 (- offset)))) - (let* ((avy-all-windows nil) - (r (avy--line nil (window-start) - (line-beginning-position (or offset 1)) - bottom-up))) - (unless (eq r t) - (avy-action-goto r)))) - -;;;###autoload -(defun avy-goto-line-below (&optional offset bottom-up) - "Goto visible line below the cursor. -OFFSET changes the distance between the closest key to the cursor and -the cursor -When BOTTOM-UP is non-nil, display avy candidates from top to bottom" - (interactive) - (if offset - (setq offset (+ offset 1))) - (let* ((avy-all-windows nil) - (r (avy--line - nil (line-beginning-position (or offset 2)) - (window-end (selected-window) t) - bottom-up))) - (unless (eq r t) - (avy-action-goto r)))) - -(defcustom avy-line-insert-style 'above - "How to insert the newly copied/cut line." - :type '(choice - (const :tag "Above" above) - (const :tag "Below" below))) - -;;;###autoload -(defun avy-goto-end-of-line (&optional arg) - "Call `avy-goto-line' and move to the end of the line." - (interactive "p") - (avy-goto-line arg) - (end-of-line)) - -;;;###autoload -(defun avy-copy-line (arg) - "Copy a selected line above the current line. -ARG lines can be used." - (interactive "p") - (let ((initial-window (selected-window))) - (avy-with avy-copy-line - (let* ((start (avy--line)) - (str (buffer-substring-no-properties - start - (save-excursion - (goto-char start) - (move-end-of-line arg) - (point))))) - (select-window initial-window) - (cond ((eq avy-line-insert-style 'above) - (beginning-of-line) - (save-excursion - (insert str "\n"))) - ((eq avy-line-insert-style 'below) - (end-of-line) - (insert "\n" str) - (beginning-of-line)) - (t - (user-error "Unexpected `avy-line-insert-style'"))))))) - -;;;###autoload -(defun avy-move-line (arg) - "Move a selected line above the current line. -ARG lines can be used." - (interactive "p") - (let ((initial-window (selected-window))) - (avy-with avy-move-line - (let ((start (avy--line))) - (save-excursion - (goto-char start) - (kill-whole-line arg)) - (select-window initial-window) - (cond ((eq avy-line-insert-style 'above) - (beginning-of-line) - (save-excursion - (insert - (current-kill 0)))) - ((eq avy-line-insert-style 'below) - (end-of-line) - (newline) - (save-excursion - (insert (substring (current-kill 0) 0 -1)))) - (t - (user-error "Unexpected `avy-line-insert-style'"))))))) - -;;;###autoload -(defun avy-copy-region (arg) - "Select two lines and copy the text between them to point. - -The window scope is determined by `avy-all-windows' or -`avy-all-windows-alt' when ARG is non-nil." - (interactive "P") - (let ((initial-window (selected-window))) - (avy-with avy-copy-region - (let* ((beg (save-selected-window - (avy--line arg))) - (end (avy--line arg)) - (str (buffer-substring-no-properties - beg - (save-excursion - (goto-char end) - (line-end-position))))) - (select-window initial-window) - (cond ((eq avy-line-insert-style 'above) - (beginning-of-line) - (save-excursion - (insert str "\n"))) - ((eq avy-line-insert-style 'below) - (end-of-line) - (newline) - (save-excursion - (insert str))) - (t - (user-error "Unexpected `avy-line-insert-style'"))))))) - -;;;###autoload -(defun avy-move-region () - "Select two lines and move the text between them above the current line." - (interactive) - (avy-with avy-move-region - (let* ((initial-window (selected-window)) - (beg (avy--line)) - (end (avy--line)) - text) - (when (> beg end) - (cl-rotatef beg end)) - (setq end (save-excursion - (goto-char end) - (1+ (line-end-position)))) - (setq text (buffer-substring beg end)) - (move-beginning-of-line nil) - (delete-region beg end) - (select-window initial-window) - (insert text)))) - -;;;###autoload -(defun avy-kill-region (arg) - "Select two lines and kill the region between them. - -The window scope is determined by `avy-all-windows' or -`avy-all-windows-alt' when ARG is non-nil." - (interactive "P") - (let ((initial-window (selected-window))) - (avy-with avy-kill-region - (let* ((beg (save-selected-window - (list (avy--line arg) (selected-window)))) - (end (list (avy--line arg) (selected-window)))) - (cond - ((not (numberp (car beg))) - (user-error "Fail to select the beginning of region")) - ((not (numberp (car end))) - (user-error "Fail to select the end of region")) - ;; Restrict operation to same window. It's better if it can be - ;; different windows but same buffer; however, then the cloned - ;; buffers with different narrowed regions might cause problem. - ((not (equal (cdr beg) (cdr end))) - (user-error "Selected points are not in the same window")) - ((< (car beg) (car end)) - (save-excursion - (kill-region - (car beg) - (progn (goto-char (car end)) (forward-visible-line 1) (point))))) - (t - (save-excursion - (kill-region - (progn (goto-char (car beg)) (forward-visible-line 1) (point)) - (car end))))))) - (select-window initial-window))) - -;;;###autoload -(defun avy-kill-ring-save-region (arg) - "Select two lines and save the region between them to the kill ring. -The window scope is determined by `avy-all-windows'. -When ARG is non-nil, do the opposite of `avy-all-windows'." - (interactive "P") - (let ((initial-window (selected-window))) - (avy-with avy-kill-ring-save-region - (let* ((beg (save-selected-window - (list (avy--line arg) (selected-window)))) - (end (list (avy--line arg) (selected-window)))) - (cond - ((not (numberp (car beg))) - (user-error "Fail to select the beginning of region")) - ((not (numberp (car end))) - (user-error "Fail to select the end of region")) - ((not (equal (cdr beg) (cdr end))) - (user-error "Selected points are not in the same window")) - ((< (car beg) (car end)) - (save-excursion - (kill-ring-save - (car beg) - (progn (goto-char (car end)) (forward-visible-line 1) (point))))) - (t - (save-excursion - (kill-ring-save - (progn (goto-char (car beg)) (forward-visible-line 1) (point)) - (car end))))))) - (select-window initial-window))) - -;;;###autoload -(defun avy-kill-whole-line (arg) - "Select line and kill the whole selected line. - -With a numerical prefix ARG, kill ARG line(s) starting from the -selected line. If ARG is negative, kill backward. - -If ARG is zero, kill the selected line but exclude the trailing -newline. - -\\[universal-argument] 3 \\[avy-kil-whole-line] kill three lines -starting from the selected line. \\[universal-argument] -3 - -\\[avy-kill-whole-line] kill three lines backward including the -selected line." - (interactive "P") - (let ((initial-window (selected-window))) - (avy-with avy-kill-whole-line - (let* ((start (avy--line))) - (if (not (numberp start)) - (user-error "Fail to select the line to kill") - (save-excursion (goto-char start) - (kill-whole-line arg))))) - (select-window initial-window))) - -;;;###autoload -(defun avy-kill-ring-save-whole-line (arg) - "Select line and save the whole selected line as if killed, but don’t kill it. - -This command is similar to `avy-kill-whole-line', except that it -saves the line(s) as if killed, but does not kill it(them). - -With a numerical prefix ARG, kill ARG line(s) starting from the -selected line. If ARG is negative, kill backward. - -If ARG is zero, kill the selected line but exclude the trailing -newline." - (interactive "P") - (let ((initial-window (selected-window))) - (avy-with avy-kill-ring-save-whole-line - (let* ((start (avy--line))) - (if (not (numberp start)) - (user-error "Fail to select the line to kill") - (save-excursion - (let ((kill-read-only-ok t) - (buffer-read-only t)) - (goto-char start) - (kill-whole-line arg)))))) - (select-window initial-window))) - -;;;###autoload -(defun avy-setup-default () - "Setup the default shortcuts." - (eval-after-load "isearch" - '(define-key isearch-mode-map (kbd "C-'") 'avy-isearch))) - -(defcustom avy-timeout-seconds 0.5 - "How many seconds to wait for the second char." - :type 'float) - -(defcustom avy-enter-times-out t - "Whether enter exits avy-goto-char-timer early. If nil it matches newline" - :type 'boolean) - -(defun avy--read-candidates (&optional re-builder) - "Read as many chars as possible and return their occurrences. -At least one char must be read, and then repeatedly one next char -may be read if it is entered before `avy-timeout-seconds'. DEL -deletes the last char entered, and RET exits with the currently -read string immediately instead of waiting for another char for -`avy-timeout-seconds'. -The format of the result is the same as that of `avy--regex-candidates'. -This function obeys `avy-all-windows' setting. -RE-BUILDER is a function that takes a string and returns a regex. -When nil, `regexp-quote' is used. -If a group is captured, the first group is highlighted. -Otherwise, the whole regex is highlighted." - (let ((str "") - (re-builder (or re-builder #'regexp-quote)) - char break overlays regex) - (unwind-protect - (progn - (avy--make-backgrounds - (avy-window-list)) - (while (and (not break) - (setq char - (read-char (format "%d char%s: " - (length overlays) - (if (string= str "") - str - (format " (%s)" str))) - t - (and (not (string= str "")) - avy-timeout-seconds)))) - ;; Unhighlight - (dolist (ov overlays) - (delete-overlay ov)) - (setq overlays nil) - (cond - ;; Handle RET - ((= char 13) - (if avy-enter-times-out - (setq break t) - (setq str (concat str (list ?\n))))) - ;; Handle C-h, DEL - ((memq char avy-del-last-char-by) - (let ((l (length str))) - (when (>= l 1) - (setq str (substring str 0 (1- l)))))) - ;; Handle ESC - ((= char 27) - (keyboard-quit)) - (t - (setq str (concat str (list char))))) - ;; Highlight - (when (>= (length str) 1) - (let ((case-fold-search - (or avy-case-fold-search (string= str (downcase str)))) - found) - (avy-dowindows current-prefix-arg - (dolist (pair (avy--find-visible-regions - (window-start) - (window-end (selected-window) t))) - (save-excursion - (goto-char (car pair)) - (setq regex (funcall re-builder str)) - (while (re-search-forward regex (cdr pair) t) - (unless (not (avy--visible-p (1- (point)))) - (let* ((idx (if (= (length (match-data)) 4) 1 0)) - (ov (make-overlay - (match-beginning idx) (match-end idx)))) - (setq found t) - (push ov overlays) - (overlay-put - ov 'window (selected-window)) - (overlay-put - ov 'face 'avy-goto-char-timer-face))))))) - ;; No matches at all, so there's surely a typo in the input. - (unless found (beep))))) - (nreverse (mapcar (lambda (ov) - (cons (cons (overlay-start ov) - (overlay-end ov)) - (overlay-get ov 'window))) - overlays))) - (dolist (ov overlays) - (delete-overlay ov)) - (avy--done)))) - -;;;###autoload -(defun avy-goto-char-timer (&optional arg) - "Read one or many consecutive chars and jump to the first one. -The window scope is determined by `avy-all-windows' (ARG negates it)." - (interactive "P") - (let ((avy-all-windows (if arg - (not avy-all-windows) - avy-all-windows))) - (avy-with avy-goto-char-timer - (avy-process - (avy--read-candidates))))) - -(defun avy-push-mark () - "Store the current point and window." - (let ((inhibit-message t)) - (ring-insert avy-ring - (cons (point) (selected-window))) - (unless (region-active-p) - (push-mark)))) - -(defun avy-pop-mark () - "Jump back to the last location of `avy-push-mark'." - (interactive) - (let (res) - (condition-case nil - (progn - (while (not (window-live-p - (cdr (setq res (ring-remove avy-ring 0)))))) - (let* ((window (cdr res)) - (frame (window-frame window))) - (when (and (frame-live-p frame) - (not (eq frame (selected-frame)))) - (select-frame-set-input-focus frame)) - (select-window window) - (goto-char (car res)))) - (error - (set-mark-command 4))))) - -;; ** Org-mode -(defvar org-reverse-note-order) -(declare-function org-refile "org") -(declare-function org-back-to-heading "org") -(declare-function org-reveal "org") - -(defvar org-after-refile-insert-hook) - -(defun avy-org-refile-as-child () - "Refile current heading as first child of heading selected with `avy.'" - ;; Inspired by `org-teleport': http://kitchingroup.cheme.cmu.edu/blog/2016/03/18/Org-teleport-headlines/ - (interactive) - (let* ((org-reverse-note-order t) - (marker (save-excursion - (avy-with avy-goto-line - (unless (eq 't (avy-jump (rx bol (1+ "*") (1+ space)))) - ;; `avy-jump' returns t when aborted with C-g. - (point-marker))))) - (filename (buffer-file-name (or (buffer-base-buffer (marker-buffer marker)) - (marker-buffer marker)))) - (rfloc (list nil filename nil marker)) - ;; Ensure the refiled heading is visible. - (org-after-refile-insert-hook (if (member 'org-reveal org-after-refile-insert-hook) - org-after-refile-insert-hook - (cons #'org-reveal org-after-refile-insert-hook)))) - (when marker - ;; Only attempt refile if avy session was not aborted. - (org-refile nil nil rfloc)))) - -(defun avy-org-goto-heading-timer (&optional arg) - "Read one or many characters and jump to matching Org headings. -The window scope is determined by `avy-all-windows' (ARG negates it)." - (interactive "P") - (let ((avy-all-windows (if arg - (not avy-all-windows) - avy-all-windows))) - (avy-with avy-goto-char-timer - (avy-process - (avy--read-candidates - (lambda (input) - (format "^\\*+ .*\\(%s\\)" input)))) - (org-back-to-heading)))) - -(provide 'avy) - -;;; avy.el ends here blob - 1c9ea2fb525180c241de87bd4fee8680916fa2e5 (mode 644) blob + /dev/null --- elpa/avy-0.5.0/doc/Changelog.org +++ /dev/null @@ -1,498 +0,0 @@ -* 0.3.0 -** Fixes -*** Candidate window reversal -See [[https://github.com/abo-abo/avy/issues/27][#27]]. - -*** Jumping to newlines with =at-full= style -See [[https://github.com/abo-abo/avy/issues/5][#5]]. - -*** Stop =at-full= style from shifting text sometimes -See [[https://github.com/abo-abo/avy/issues/5][#5]]. - -*** Fix =at-full= interaction with tabs -When at a tab, visualize it using =tab-width= spaces. - -See [[https://github.com/abo-abo/avy/issues/43][#43]]. - -*** Fix overlay issue when the same buffer is in two windows - -See [[https://github.com/abo-abo/avy/issues/47][#47]] and http://debbugs.gnu.org/cgi/bugreport.cgi?bug=20607. - -*** Quote punctuation chars - -See [[https://github.com/abo-abo/avy/issues/63][#63]]. - -*** Update screenshot for =avy-goto-char= in README.md -Use ~C-:~ as the new suggested binding instead of the pi char. - -See [[https://github.com/abo-abo/avy/issues/64][#64]]. - -** New Features -*** =avy-goto-line= can now break into =goto-line= -Just enter a digit and you'll be transferred into =goto-line= prompt -with that digit already entered. This means that you can just bind -~M-g g~ to =avy-goto-line= without losing anything. - -See [[https://github.com/abo-abo/avy/issues/29][#29]]. - -*** =avy-goto-line= now works with all kinds of overlay styles -Any of the following do something different now: - -#+begin_src elisp -(setq avy-styles-alist - '((avy-goto-line . post))) -(setq avy-styles-alist - '((avy-goto-line . at))) -(setq avy-styles-alist - '((avy-goto-line . at-full))) -(setq avy-styles-alist - '((avy-goto-line . pre))) -#+end_src - -See [[https://github.com/abo-abo/ace-link/issues/17][#17]]. - -*** New defcustom =avy-case-fold-search= -Non-nil when searches should ignore case, so e.g. =avy-goto-char= "b" -will match both "b" and "B". On by default. Use this to turn off this -behavior: - -#+begin_src elisp -(setq avy-case-fold-search nil) -#+end_src - -See [[https://github.com/abo-abo/avy/issues/34][#34]]. - -*** New command =avy-goto-word-or-subword-1= - -Enter one char, and select a visible word or subword that starts with -it, depending on =subword-mode=. Move the point there. - -See [[https://github.com/abo-abo/avy/issues/33][#33]]. - -*** =avy-move-line= should remove empty line after original one is moved - -See [[https://github.com/abo-abo/avy/issues/40][#40]]. - -*** =avy-move-line= now takes a prefix arg -Use e.g. ~M-3~ before =avy-move-line= to move 3 lines at once. - -*** Most commands can be used non-interactively -Example: -#+begin_src elisp -(defun avy-goto-lp () - (interactive) - (avy-goto-char ?\()) -#+end_src - -This command only goes to the "(" character. This is actually very -similar to [[http://oremacs.com/lispy/#lispy-ace-paren][=lispy-ace-paren=]], except the implementation is only one -line. - -See [[https://github.com/abo-abo/avy/issues/44][#44]]. - -*** (almost) all defcustoms are explained on the wiki - -See [[https://github.com/abo-abo/avy/wiki/defcustom][the defcustom wiki page]]. - -*** Allow all operations to work across frames - -You have to customize =avy-all-windows= for this. By default, it's set -to work on all windows on the current frame. - -To make it work only on the current window, use: -#+begin_src elisp -(setq avy-all-windows nil) -#+end_src - -To make it work on all frames, use: -#+begin_src elisp -(setq avy-all-windows 'all-frames) -#+end_src - -*** New command =avy-goto-char-in-line= - -This is =avy-goto-char= reduced only to the current line. Few -candidates means very short decision chars path. - -See [[https://github.com/abo-abo/avy/issues/49][#49]]. - -*** New overlay style =de-bruijn= - -How to use it: - -#+begin_src elisp -(setq avy-style 'de-bruijn) -#+end_src - -What it does: when your leading chars are clumped up together, it's -impossible to overlay the decision path without shifting the buffer -text a bit. For example, with the word "buffer", you =avy-goto-char= "b", and: - -- the path for the first "f" is "aj" -- the path for the second "f" is "ak" - -It's not possible to overlay 4 characters over "ff" in "buffer". But -to with =de-bruijn= style, which results in the path being "aj" and -"jk". It's possible to overlay "ajk" just fine. - -Pros and cons of =de-bruijn= over other styles: - -- a pro is that it's possible to display the full decision path for - clumped up chars, which is truncated for other styles -- a con is that the decision path is of the same length (e.g. 2 or 3) - for all candidates, while with other styles it's possible to have a - few candidates with a shorter path. - -See [[https://github.com/abo-abo/avy/issues/51][#51]] and [[https://github.com/abo-abo/avy/issues/5][#5]]. - -*** New defcustom =avy-ignored-modes= - -This is meant for visual modes like =doc-view-mode= or =image-mode= -that can have a huge number of chars in a single window. Which results -in a huge number of candidates even in other windows. - -Current setting: - -#+begin_src elisp -(setq avy-ignored-modes '(image-mode doc-view-mode pdf-view-mode)) -#+end_src - -See [[https://github.com/abo-abo/avy/issues/57][#57]]. - -*** New tutorial on writing custom commands - -See the [[https://github.com/abo-abo/avy/wiki/custom-commands][the custom-commands wiki page]] and [[https://github.com/abo-abo/avy/issues/55][#55]]. - -*** New face setup -New variable =avy-lead-faces= will determine the faces used to color -the current decision depth you're in. For example, if to select a -particular candidate you need to press "abc": - -- "a" will be highlighted with a face that corresponds to depth 3 -- "b" will be highlighted with a face that corresponds to depth 2 -- "c" will be highlighted with a face that corresponds to depth 1 - -But if another candidate needs "ef": - -- "e" will be highlighted with a face that corresponds to depth 2 -- "f" will be highlighted with a face that corresponds to depth 1 - -See [[https://github.com/abo-abo/avy/issues/53][#53]]. - -*** New variable =avy-translate-char-function= - -You can use this, for example, to interpret one character as another in =avy-keys=. - -Example: -#+begin_src elisp -(setq avy-translate-char-function - (lambda (c) (if (= c 32) ?a c))) -#+end_src - -This will translate ~SPC~ (32) into ~a~. So you can press either ~a~ or ~SPC~ to mean "a". - -*** =avy-isearch= works for different styles - -See [[https://github.com/abo-abo/avy/issues/61][#61]]. - -*** Switch the default style from =pre= to =at-full= - -I've come to like =at-full= more than =pre= over time. The difference -is that =pre= hides no chars in your buffer, while =at-full= doesn't -shift text. - -Use this to restore the previous default behavior: -#+begin_src elisp -(setq avy-style 'pre) -#+end_src -* 0.4.0 -** Fixes -*** =avy-goto-char-timer= obeys =avy-styles-alist= -See [[https://github.com/abo-abo/avy/issues/67][#67]]. -*** Add =de-bruijn= to the defcustom of =avy-styles-alist= -See [[https://github.com/abo-abo/avy/issues/73][#73]]. -*** Respect the current input method for target chars -See [[https://github.com/abo-abo/avy/issues/76][#76]]. -*** =avy-goto-subword-0= shouldn't offer invisible chars -See [[https://github.com/abo-abo/avy/issues/90][#90]]. -*** Better =case-fold-search= handling -See [[https://github.com/abo-abo/avy/issues/87][#87]]. -*** Add misc punctuation to subword commands -See [[https://github.com/abo-abo/avy/issues/93][#93]]. -*** Add padding for wide-width chars (ex. Japanese and Chinese) -See [[https://github.com/abo-abo/avy/issues/96][#96]]. -*** =avy-goto-line= -**** Push mark for numeric line -See [[https://github.com/abo-abo/avy/issues/74][#74]]. -**** Allow numeric prefix arg -The old behavior remains for ARG 1 or 4. For all other ARG, simply go -to that line. -See [[https://github.com/abo-abo/avy/issues/86][#86]]. -**** Work for =visual-line-mode= -See [[https://github.com/abo-abo/avy/issues/91][#91]]. -**** Don't error on end of buffer -See [[https://github.com/abo-abo/avy/issues/91][#91]]. -**** Obey =avy-background= -See [[https://github.com/abo-abo/avy/issues/94][#94]]. -**** Fix for narrowed regions -See [[https://github.com/abo-abo/avy/issues/122][#122]], [[https://github.com/abo-abo/avy/issues/123][#123]]. -**** Don't modify =avy-action= -See [[https://github.com/abo-abo/avy/issues/124][#124]]. -*** =avy-goto-char-timer= -**** May read as many chars as you want -See [[https://github.com/abo-abo/avy/issues/97][#97]]. -**** Highlight matches while reading chars -See [[https://github.com/abo-abo/avy/issues/98][#98]]. -**** Highlight depending on =avy-all-windows= -See [[https://github.com/abo-abo/avy/issues/104][#104]]. -**** Make faster for =org-mode= -See [[https://github.com/abo-abo/avy/issues/100][#100]]. -**** Add case fold search -See [[https://github.com/abo-abo/avy/issues/128][#128]]. -*** =avy-copy-region= -**** Keep the same selectors for the second pass -See [[https://github.com/abo-abo/avy/issues/120][#120]], [[https://github.com/abo-abo/avy/issues/121][#121]]. -**** Copy/move to initial window -See [[https://github.com/abo-abo/avy/issues/131][#131]]. -*** Search only in the visible region -See [[https://github.com/abo-abo/avy/issues/108][#108]], [[https://github.com/abo-abo/avy/issues/109][#109]]. -*** Fix jumping to the last char of a folded Org outline -See [[https://github.com/abo-abo/avy/issues/108][#108]]. -*** Fix for both =org-indent-mode= and =visual-line-mode= -See [[https://github.com/abo-abo/avy/issues/110][#110]]. -*** Beep when there are no matches -See [[https://github.com/abo-abo/avy/issues/111][#111]]. -*** Simplify overlay code -Most functions reuse =avy--overlay= now. -*** Fix de-bruijn "no catch for tag" -See [[https://github.com/abo-abo/avy/issues/116][#116]]. -*** Fix overlays at =point-max= -See [[https://github.com/abo-abo/avy/issues/125][#125]]. -*** Improve =case-fold-search= condition -See [[https://github.com/abo-abo/avy/issues/126][#126]]. -*** Don't shorten selector string for =visual-line-mode= and =bolp= -See [[https://github.com/abo-abo/avy/issues/129][#129]]. -*** Fix interaction with =goto-address-mode= -** New Features -*** Allow non-printing keys in =avy-keys= -Now you can set avy-keys also to the arrow keys and page up/down, e.g. - -#+begin_src elisp -(setq avy-keys '(left right up down prior next)) -#+end_src - -and those will be displayed as ▲, ▼, ◀, ▶, △, ▽ in the overlays. The -display is controlled by the variable =avy-key-to-char-alist=. - -See [[https://github.com/abo-abo/avy/issues/77][#77]]. -*** Allow to switch action midway from goto to kill/mark/copy -For example, suppose you have: - -#+begin_src elisp -(global-set-key (kbd "M-t") 'avy-goto-word-1) -#+end_src - -- To jump to a certain word starting with "w" (e.g. first one on - screen): ~M-t w a~ -- To copy the word instead of jumping to it: ~M-t w na~. -- To mark the word after jumping to it: ~M-t w ma~. -- To kill the word after jumping to it: ~M-t w xa~. - -You can customize =avy-dispatch-alist= to modify these actions. - -See [[https://github.com/abo-abo/avy/issues/78][#78]]. - -*** New command =avy-pop-mark= -Goes back to the last location of =push-mark=: - -- has its own history, -- handles multiple frames. - -See [[https://github.com/abo-abo/avy/issues/81][#81]] [[https://github.com/abo-abo/avy/issues/88][#88]] [[https://github.com/abo-abo/avy/issues/69][#69]]. -*** New commands =avy-goto-line-above= and =avy-goto-line-below= -See [[https://github.com/abo-abo/avy/issues/106][#106]]. -*** New defcustom =avy-line-insert-style= -Allows to modify the behavior of =avy-copy-line=, =avy-move-line=, and =avy-copy-region=. -See [[https://github.com/abo-abo/avy/issues/117][#117]]. -*** New defcustom =avy-all-windows-alt= -Allows to customize the behavior of =universal-argument= modifying -=avy-all-windows=. -See [[https://github.com/abo-abo/avy/issues/118][#118]]. -*** New defcustom =avy-subword-extra-word-chars= -Allows to customize the behavior of =avy-goto-subword-0= and -=avy-goto-subword-1= by adding extra chars that should match as word -constituents. -See [[https://github.com/abo-abo/avy/issues/116][#116]]. -* 0.5.0 -** Fixes -*** el:avy-action-copy -Save selected window and frame. See [[https://github.com/abo-abo/avy/issues/133][#133]]. - -Copy line for el:avy-goto-line. See [[https://github.com/abo-abo/avy/issues/191][#191]]. -*** el:avy-read -Make ~C-g~ and ~ESC~ fail silently when reading char. See [[https://github.com/abo-abo/avy/issues/137][#137]]. - -Display error message on mouse clicks. See [[https://github.com/abo-abo/avy/issues/226][#226]]. - -Update el:avy-current-path before returning. See [[https://github.com/abo-abo/avy/issues/226][#226]]. - -Quit on ~ESC~. See [[https://github.com/abo-abo/avy/issues/249][#249]]. - -Fix for el:org-toggle-link-display. See [[https://github.com/abo-abo/avy/issues/261][#261]]. - -Fix for el:buffer-invisibility-spec being t. See [[https://github.com/abo-abo/avy/issues/264][#264]]. - -Allow "invisible" 'org-link. See [[https://github.com/abo-abo/avy/issues/269][#269]]. -*** el:avy-goto-word-1 -Works for "^A"-"^Z", see [[https://github.com/abo-abo/avy/issues/167][#167]]. -*** el:avy-goto-subword-0 -Add char at window start if empty, See [[https://github.com/abo-abo/avy/issues/145][#145]]. - -Add option to limit scope. See [[https://github.com/abo-abo/avy/issues/235][#235]]. -*** el:avy-goto-subword-1 -Check el:char-after. See [[https://github.com/abo-abo/avy/issues/163][#163]]. -*** el:avy-isearch -Escape regex. See [[https://github.com/abo-abo/avy/issues/147][#147]]. -*** el:avy-goto-char-2 -Translate ~RET~ to ~C-j~. See [[https://github.com/abo-abo/avy/issues/153][#153]]. -*** el:avy-action-goto -Add el:raise-frame. -*** el:avy-goto-char-timer -Allow ~C-h~ to delete. See [[https://github.com/abo-abo/avy/issues/193][#193]]. - -Obey el:avy-background for the initial search. See [[https://github.com/abo-abo/avy/issues/259][#259]]. -*** el:avy-goto-line -Fix for empty buffer. See [[https://github.com/abo-abo/avy/issues/238][#238]]. - -Add ability to display candidates from bottom to top. See [[https://github.com/abo-abo/avy/issues/236][#236]]. -*** el:avy--overlay-at-full -More consistent face order. -See [[https://github.com/abo-abo/avy/issues/270][#270]]. -*** documentation -See [[https://github.com/abo-abo/avy/issues/223][#223]], [[https://github.com/abo-abo/avy/issues/226][#226]], [[https://github.com/abo-abo/avy/issues/218][#218]], [[https://github.com/abo-abo/avy/issues/245][#245]], [[https://github.com/abo-abo/avy/issues/262][#262]]. - -** New Features -*** API -New functions have been added as drop-in replacements of double-dash (private) avy -functions that were used in other packages and configs. Please replace the references to -the obsolete functions. - -**** el:avy-jump -New API function to replace el:avy--generic-jump. See [[https://github.com/abo-abo/avy/issues/265][#265]], [[https://github.com/abo-abo/avy/issues/267][#267]]. -**** el:avy-process -New API function to replace el:avy--process. See [[https://github.com/abo-abo/avy/issues/266][#266]]. - -*** New actions -**** el:avy-action-kill-stay -Kill a word with el:avy-goto-char without moving there. -Bound to ~X~. -**** el:avy-action-ispell -Auto-correct word at point. See [[https://github.com/abo-abo/avy/issues/142][#142]], [[https://github.com/abo-abo/avy/issues/160][#160]], [[https://github.com/abo-abo/avy/issues/161][#161]]. -Bound to ~i~. -**** el:avy-action-yank -Yank sexp starting at selected point at the current point. See [[https://github.com/abo-abo/avy/issues/183][#183]]. -Bound to ~y~. -**** el:avy-action-teleport -Kill sexp starting on selected point and yank into the current location. See [[https://github.com/abo-abo/avy/issues/207][#207]]. -Bound to ~t~. -**** el:avy-action-zap-to-char -Kill from point up to selected point. See [[https://github.com/abo-abo/avy/issues/234][#234]]. -Bound to ~z~. - -*** New defcustoms -**** New el:avy-style setting: 'words -Use this setting: -#+begin_src elisp -(setq avy-style 'words) -#+end_src -And you'll see overlays like "by", "if", "is", "it", "my" for 2-letter sequences, and -"can", "car", "cog" for 3-letter sequences. You might find them easier to type than "hla", -"lls" and "jhl". But you will have to adjust your el:avy-dispatch-alist, e.g. to use only -upper case characters. - -See [[https://github.com/abo-abo/avy/issues/210][#210]], [[https://github.com/abo-abo/avy/issues/219][#219]]. -**** el:avy-orders-alist -Use it to customize the order of candidates with relation to point. The default is for -el:avy-goto-char to have the shortest overlay for candidates closest to point. -See [[https://github.com/abo-abo/avy/issues/242][#242]]. -**** el:avy-indent-line-overlay -When non-nil el:avy-goto-line will display the line overlay next to the first -non-whitespace character of each line. See [[https://github.com/abo-abo/avy/issues/244][#244]]. -**** el:avy-single-candidate-jump -When non-nil, and there is only one candidate, jump there. See [[https://github.com/abo-abo/avy/issues/250][#250]]. -**** el:avy-del-last-char-by -Customize keys which delete the last read char. The defaults are ~C-h~ and ~DEL~. See [[https://github.com/abo-abo/avy/issues/251][#251]]. -**** el:avy-goto-word-0-regexp -Customize el:avy-goto-word-0. See [[https://github.com/abo-abo/avy/issues/136][#136]], [[https://github.com/abo-abo/avy/issues/156][#156]]. -**** el:avy-pre-action -Function to all before el:avy-action. See [[https://github.com/abo-abo/avy/issues/260][#260]]. -**** el:avy-enter-times-out -When non-nil (the default), ~RET~ exists el:avy-goto-char-timer early. -When nil, it matches a newline. -See [[https://github.com/abo-abo/avy/issues/220][#220]], [[https://github.com/abo-abo/avy/issues/225][#225]]. - -*** New commands -**** el:avy-move-region -Select two lines and move the text between them above the current line. -See [[https://github.com/abo-abo/avy/issues/75][#75]], [[https://github.com/abo-abo/avy/issues/187][#187]], [[https://github.com/abo-abo/avy/issues/188][#188]]. -**** el:avy-goto-end-of-line -Call el:avy-goto-line and move to the end of the line. See [[https://github.com/abo-abo/avy/issues/240][#240]]. - -**** el:avy-linum-mode -Minor mode that uses avy hints for el:linum-mode. - -**** el:avy-resume -Holds last command avy command after user input. This is a quick way to bring back the -same markers after a jump. See [[https://github.com/abo-abo/avy/issues/157][#157]], [[https://github.com/abo-abo/avy/issues/165][#165]]. -**** el:avy-next -Go to the next candidate after el:avy-read. -Example config: - -#+begin_src elisp -(defhydra hydra-avy-cycle () - ("j" avy-next "next") - ("k" avy-prev "prev") - ("q" nil "quit")) - -(global-set-key (kbd "C-M-'") 'hydra-avy-cycle/body) -#+end_src - -After e.g. el:avy-goto-char or el:avy-goto-char-timer, use the above hydra to cycle -between the last candidates. See [[https://github.com/abo-abo/avy/issues/254][#254]]. -**** *-above and *-below variants -Command versions restricted to matches before or after the point. - -See [[https://github.com/abo-abo/avy/issues/148][#148]]: -- el:avy-goto-char-2-above -- el:avy-goto-char-2-below - -See [[https://github.com/abo-abo/avy/issues/151][#151]]: -- el:avy-goto-word-1-above -- el:avy-goto-word-1-below - -See [[https://github.com/abo-abo/avy/issues/156][#156]]: -- el:avy-goto-symbol-1-above -- el:avy-goto-symbol-1-below - -See [[https://github.com/abo-abo/avy/issues/186][#186]]: -- el:avy-goto-word-0-below -- el:avy-goto-word-0-above -**** kill and save region functionality -New avy-enabled commands: -- el:avy-kill-whole-line -- el:avy-kill-region -- el:avy-kill-ring-save-whole-line -- el:avy-kill-ring-save-region - -See [[https://github.com/abo-abo/avy/issues/158][#158]]. -**** org-mode functionality -New avy-enabled commands: -- el:avy-org-refile-as-child -- el:avy-org-goto-heading-timer -See [[https://github.com/abo-abo/avy/issues/214][#214]], [[https://github.com/abo-abo/avy/issues/258][#258]]. -*** el:avy-goto-char-timer -Show the number of matches so far in the prompt. See [[https://github.com/abo-abo/avy/issues/253][#253]]. -*** el:avy-read -Ignore mistyping when no candidates are available. See [[https://github.com/abo-abo/avy/issues/256][#256]]. - -When the overlays are shown, press ~?~ to get dispatch help. blob - 6751b748b26ceac65d8c126bf48058634214d5f2 (mode 644) blob + /dev/null --- elpa/avy-0.5.0/targets/avy-init.el +++ /dev/null @@ -1,26 +0,0 @@ -;;; avy-init.el --- bare avy init - -;; Copyright (C) 2015 Free Software Foundation, Inc. - -;; Author: Oleh Krehel - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -(add-to-list 'load-path default-directory) -(mapc #'byte-compile-file '("avy.el")) -(require 'avy) -(global-set-key (kbd "C-c j") 'avy-goto-char) -(global-set-key (kbd "C-'") 'avy-goto-char-2) blob - eb2e9f61d50df06b6c1d6935dc37b86625cb88cb (mode 644) blob + /dev/null --- elpa/avy-0.5.0/targets/checkdoc.el +++ /dev/null @@ -1,4 +0,0 @@ -;; Copyright (C) 2019 Free Software Foundation, Inc. - -(checkdoc-file "avy-test.el") -(checkdoc-file "avy.el") blob - c6f3887ff1a487a061ec6d2ae4010301dd51fab9 (mode 644) blob + /dev/null --- elpa/avy-0.5.0.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2019-09-21T19:54:31+0200 using RSA \ No newline at end of file blob - c466a003a7fd3f8b88e899ebcf10ce560171dc64 (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/NEWS.org +++ /dev/null @@ -1,323 +0,0 @@ -#+link: compat-srht https://todo.sr.ht/~pkal/compat/ -#+link: compat-gh https://github.com/emacs-compat/compat/issues/ -#+options: toc:nil num:nil author:nil - -* Release of "Compat" Version 29.1.4.5 - -- Minor optimization of ~plist-get~ and ~plist-put~. -- Minor compatibility adjustments for the Emacs 30 development version. -- A minimal version of =compat.el= will be part of Emacs 30. Emacs :core packages - can directly ~(require 'compat)~ without the ~NOERROR~ flag. Furthermore Compat - will not be installed unnecessarily. If a package depending on Emacs 25.1 and - Compat 29.1 is installed on Emacs 30.1, Compat 29.1 will not be installed from - ELPA, since Emacs 30.1 already provides the required functionality. - -(Release <2024-03-16 Sat>) - -* Release of "Compat" Version 29.1.4.4 - -- Fix ~Package-Requires~ header in compat.el -- Fix ~Maintainer~ header in compat.el - -(Release <2023-11-13 Mon>) - -* Release of "Compat" Version 29.1.4.3 - -- compat-29: Add function =char-uppercase-p=. -- compat-29: Add function =window-configuration-equal-p=. - -(Release <2023-11-04 Sat>) - -* Release of "Compat" Version 29.1.4.2 - -- compat-28: Improve =make-separator-line= visuals on graphic displays. -- compat-28: Add =native-comp-available-p=, which always returns nil. -- compat-29: Add variable =lisp-directory=. - -(Release <2023-07-30 Sun>) - -* Release of "Compat" Version 29.1.4.1 - -- compat-29: Add ~directory-abbrev-apply~. -- compat-29: Add ~directory-abbrev-make-regexp~. - -(Release <2023-03-26 Sun>) - -* Release of "Compat" Version 29.1.4.0 - -- compat-27: Drop obsolete ~compat-call dired-get-marked-files~. -- compat-28: Add support for ~defcustom~ type ~natnum~. -- compat-29: Add ~with-restriction~ and ~without-restriction~. -- compat-29: Add ~cl-constantly~. -- compat-29: Drop ~with-narrowing~ which was renamed to ~with-restriction~. -- compat-28: Add support for ~defcustom~ type ~key~. - -(Release <2023-03-05 Sun>) - -* Release of "Compat" Version 29.1.3.4 - -- Ensure that ~seq~ is required properly both at compile time and runtime, such - that compilation of downstream packages works even if Compat itself is not - compiled. Magit uses a complex continuous integration system, where Magit is - compiled and tested, while the Compat dependency is not compiled. -- compat-28: Add ~process-lines-handling-status~ and ~process-lines-ignore-status~. - -(Release <2023-02-11 Sat>) - -* Release of "Compat" Version 29.1.3.3 - -- compat-27: Add ~with-suppressed-warnings~. -- compat-29: Add ~cl-with-gensyms~ and ~cl-once-only~. -- compat-29: Load ~seq~, which is preloaded on Emacs 29. - -(Release <2023-02-08 Wed>) - -* Release of "Compat" Version 29.1.3.2 - -- compat-26: Add ~make-temp-file~ with optional argument TEXT. -- compat-27: Mark ~compat-call dired-get-marked-files~ as obsolete. See the - section limitations in the Compat manual. -- compat-29: Add ~funcall-with-delayed-message~ and ~with-delayed-message~. -- compat-29: Add ~ert-with-temp-file~ and ~ert-with-temp-directory~. -- compat-29: Add ~set-transient-map~ with optional arguments MESSAGE and TIMEOUT. - -(Release <2023-02-01 Wed>) - -* Release of "Compat" Version 29.1.3.1 - -- Fix regression, which prevented loading Compat in interpreted mode. We ensure - that Compat works interpreted and byte compiled by running the entire test - suite twice in the CI. See https://github.com/magit/magit/issues/4858 for the - corresponding Magit issue. -- compat-27: Add ~file-name-unquote~. -- compat-28: Add ~mark-thing-at-mouse~. -- compat-29: Replace ~string-lines~ with version from Emacs 29, support optional - KEEP-NEWLINES argument. - -(Release <2023-01-25 Wed>) - -* Release of "Compat" Version 29.1.3.0 - -- compat-25: Add ~hash-table-empty-p~. -- compat-25: Add ~macroexp-parse-body~ and ~macroexp-quote~. -- compat-25: Add ~region-noncontiguous-p~. -- compat-25: Add ~save-mark-and-excursion~. -- compat-26: Add ~read-answer~. -- compat-26: Add ~region-bounds~. -- compat-27: Add ~date-ordinal-to-time~. -- compat-27: Add ~file-size-human-readable-iec~. -- compat-27: Add ~major-mode-suspend~ and ~major-mode-restore~. -- compat-27: Add ~make-decoded-time~. -- compat-27: Add ~minibuffer-history-value~. -- compat-27: Add ~read-char-from-minibuffer~. -- compat-27: Add ~ring-resize~. -- compat-28: Add ~color-dark-p~. -- compat-28: Add ~directory-files-and-attributes~ with COUNT argument. -- compat-28: Add ~text-quoting-style~. -- compat-28: Add ~with-window-non-dedicated~. -- compat-29: Add ~buffer-local-set-state~ and ~buffer-local-restore-state~. -- compat-29: Add ~compiled-function-p~. -- compat-29: Add ~count-sentences~. -- compat-29: Add ~delete-line~. -- compat-29: Add ~get-scratch-buffer-create~. -- compat-29: Add ~list-of-strings-p~. -- compat-29: Add ~plist-get~ generalized variable. -- compat-29: Add ~plistp~. -- compat-29: Add ~read-multiple-choice~ with LONG-FORM argument. -- compat-29: Add ~readablep~. -- compat-29: Add ~substitute-quotes~. -- compat-29: Add ~use-region-beginning~, ~use-region-end~ and ~use-region-noncontiguous-p~. -- compat-29: Add ~with-narrowing~. - -(Release <2023-01-22 Sun>) - -* Release of "Compat" Version 29.1.2.0 - -- All compatibility functions are covered by tests! -- Add links from compatibility definitions to tests. -- BREAKING: Drop JSON parsing support (libjansson API, unused downstream). -- BREAKING: Drop ~null-device~ (unused downstream). -- BREAKING: Drop ~unlock-buffer~ (unused downstream). -- compat-26: Add ~buffer-hash~. -- compat-27: Add ~fixnump~ and ~bignump~. -- compat-27: Add ~with-minibuffer-selected-window~. -- compat-27: Add generalized variables for ~decoded-time-*~. -- compat-28: Add ~macroexp-warn-and-return~. -- compat-28: Add ~subr-native-elisp-p~. -- compat-28: Add ~bounds-of-thing-at-mouse~. -- compat-29: Add ~with-buffer-unmodified-if-unchanged~. -- compat-29: Fix and test ~define-key~ with REMOVE argument. - -(Release <2023-01-16 Mon>) - -* Release of "Compat" Version 29.1.1.1 - -- Add tests, 167 out of 203 definitions tested (82%). -- compat-25: Improve algorithmic complexity of ~sort~. -- compat-28: Add ~make-separator-line~. -- compat-29: Minor fixes to ~keymap-*~ functions. -- compat-29: Add ~with-memoization~. -- compat-29: Add ~buttonize~ and ~buttonize-region~. - -(Release <2023-01-14 Sat>) - -* Release of "Compat" Version 29.1.1.0 - -- The macros in ~compat-macs.el~ have been rewritten and simplified. The - refactoring allows to further refine the criteria under which compatibility - aliases, functions, macros and variables are installed. -- Remove deprecated, prefixed compatibility functions. -- Remove deprecated features ~compat-help~, ~compat-font-lock~ and ~compat-24~. -- Compat uses runtime checks (~boundp~, ~fboundp~) to ensure that existing - definitions are never overridden, when Compat is loaded on a newer Emacs than - it was compiled on. -- Compat compiles without byte compilation warnings on all supported Emacs - versions. Warnings are treated as errors in the test suite. -- Compat takes great care to remove unneeded definitions at compile time. On - recent Emacs 29 the byte compiled files are empty and not loaded, such that - Compat does not any cost to the Emacs process. -- compat-26: Fix and test ~image-property~ setter. -- compat-26: Fix and test ~read-multiple-choice~. -- compat-28: Fix and test ~with-existing-directory~. -- compat-28: Drop obsolete function ~make-directory-autoloads~. -- compat-29: Drop broken functions ~string-pixel-width~ and - ~buffer-text-pixel-size~. These functions had poor performance which lead to a - downstream issue in the doom-modeline package. If a more efficient solution is - possible, the function will be added back. See [[compat-gh:8]] for the bug report. -- compat-29: Drop broken function ~string-limit~. -- compat-29: Drop broken macro ~with-buffer-unmodified-if-unchanged~, which relied - on ~buffer-hash~ which does not exist on all supported Emacs versions. -- compat-29: Add ~pos-bol~ and ~pos-eol~. - -(Release <2023-01-07 Sat>) - -* Release of "Compat" Version 29.1.0.1 - -- Add multiple new tests for existing APIs. -- Fix bugs in compatibility functions: ~setq-local~, ~proper-list-p, prop-match-p~, - ~file-name-concat~, ~replace-regexp-in-region~, ~replace-string-in-region~. -- Add new Emacs 29 APIs. Some of them are still untested and may change. If you - intend to use an Emacs 29 API please be careful and if possible contribute - test cases. All untested functions are marked in the Compat code. Over time - tests for all functions will be added gradually. -- Add the macros ~compat-call~ and ~compat-function~ to call compatibility - functions. Since Compat avoids overwriting already existing functions, we must - define separate compatibility function definitions for functions which changed - their calling convention or behavior. These compatibility definitions can be - looked up using ~compat-function~ and called with ~compat-call~. For example ~assoc~ - can be called with a ~TESTFN~ since Emacs 26. In Emacs 25 and older the calling - convention was ~(assoc KEY ALIST)~. In order to use the new calling convention - you can use ~(compat-call assoc KEY ALIST TESTFN)~. -- Deprecate all ~compat-*~ prefixed functions. Instead use the aforementioned - ~compat-call~ or ~compat-function~ macros. -- Deprecate ~compat-help.el~ and ~compat-font-lock.el.~ -- Development moved to GitHub. -- BREAKING: Drop broken function ~func-arity~. Using ~func-arity~ is generally - discouraged and the function is hard to implement properly due to all the - various function types. There it is unlikely that the function will get - reintroduced in Compat. -- BREAKING: Drop broken function ~directory-files-recursively~. In case you need - this function, a patch including tests is welcome. -- BREAKING: Drop support for Emacs 24.3. Emacs 24.4 is required now. In case you - still need Emacs 24.3 support, you can rely on Compat 28.1.2.2. - -(Release <2023-01-05 Thu>) - -* Release of "Compat" Version 28.1.2.2 - -This is a minor release that hopes to address [[compat-srht:7]]. - -(Release <2022-08-25 Thu>) - -* Release of "Compat" Version 28.1.2.1 - -This is a minor release adding the following changes: - -- Add =derived-mode-p= defined in Emacs 27 -- Add =provided-mode-derived-p= defined in Emacs 27 -- Add =read-multiple-choice= defined in Emacs 26 -- Add =file-name-absolute-p= defined in Emacs 28 - -The only other notable change is that the manual has been rewritten to -include much more documentation that had been the case previously. - -(Release <2022-08-24 Wed>) - -* Release of "Compat" Version 28.1.2.0 - -The main change of this release has been the major simplification of -Compat's initialisation system, improving the situation around issues -people had been reporting ([[compat-srht:4]], once again) with unconventional -or unpopular packaging systems. - -In addition to this, the following functional changes have been made: - -- Fix =format-prompt= of an empty string as "default" argument -- Add =decoded-time-period= defined in Emacs 28 -- Add =subr-primitive-p= defined in Emacs 28 - -Minor improvements to manual are also part of this release. - -(Release <2022-07-18 Mon>) - -* Release of "Compat" Version 28.1.1.3 - -This release just contains a hot-fix for an issue introduced in the -last version, where compat.el raises an error during byte compilation. -See [[compat-srht:4]]. - -(Release <2022-06-19 Sun>) - -* Release of "Compat" Version 28.1.1.2 - -Two main changes have necessitated a new patch release: - -1. Fix issues related to the loading of compat when uncompiled. See - [[https://lists.sr.ht/~pkal/compat-devel/%3C20220530191000.2183047-1-jonas%40bernoul.li%3E][this thread]] for more details on the problem. -2. Fix issues related to the loading of compat on old pre-releases - (think of 28.0.50). See [[https://lists.sr.ht/~pkal/compat-devel/%3Cf8635d7d-e233-448f-b325-9e850363241c%40www.fastmail.com%3E][this thread]] for more details on the - problem. - -(Released <2022-06-22 Wed>) - -* Release of "Compat" Version 28.1.1.1 - -This is a minor release fixing a bug in =json-serialize=, that could -cause unintended side-effects, not related to packages using Compat -directly (see [[compat-srht:2]]). - -(Released <2022-05-05 Thu>) - -* Release of "Compat" Version 28.1.1.0 - -This release mostly fixes a number of smaller bugs that were not -identified as of 28.1.0.0. Nevertheless these warrant a version bump, -as some of these changes a functional. These include: - -- The addition of the =file-attribute-*= accessor functions. -- The addition of =file-attribute-collect=. -- Improvements to the Texinfo manual (via Jonas Bernoulli's recent - work on =ox-texinfo=). For the time being, the Texinfo file is - maintained in the repository itself, next to the =MANUAL= file. - This might change in the future. -- Adding a prefix to =string-trim=, =string-trim-left= and - =string-trim-right= (i.e. now =compat-string-trim=, - =compat-string-trim-left= and =compat-string-trim-right=) -- Improving the version inference used in the =compat-*= macros. - This improves the compile-time optimisation that strips away - functions that are known to be defined for a specific version. -- The addition of generalised variable (=setf=) support for - =compat-alist-get=. -- The addition of =image-property= and generalised variable support - for =image-property=. -- The addition of the function =compat-executable-find=. -- The addition of the function =compat-dired-get-marked-files=. -- The addition of the function =exec-path=. -- The addition of the function =make-lock-file-name=. -- The addition of the function =null-device=. -- The addition of the function =time-equal-p=. -- The addition of the function =date-days-in-month=. -- Handling out-of-directory byte compilation better. -- Fixing the usage and edge-cases of =and-let*=. - -(Released <2022-04-22 Fri>) blob - 182f5a352be8523d76a618674ec9f1357f58543f (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/compat-25.el +++ /dev/null @@ -1,271 +0,0 @@ -;;; compat-25.el --- Functionality added in Emacs 25.1 -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Functionality added in Emacs 25.1, needed by older Emacs versions. - -;;; Code: - -(eval-when-compile (load "compat-macs.el" nil t t)) - -(compat-version "25.1") - -;;;; Defined in alloc.c - -(compat-defun bool-vector (&rest objects) ;; - "Return a new bool-vector with specified arguments as elements. -Allows any number of arguments, including zero. -usage: (bool-vector &rest OBJECTS)" - (let ((vec (make-bool-vector (length objects) nil)) - (i 0)) - (while objects - (when (car objects) - (aset vec i t)) - (setq objects (cdr objects) - i (1+ i))) - vec)) - -;;;; Defined in fns.c - -(compat-defun sort (seq predicate) ;; - "Handle vector SEQ." - :extended t - (cond - ((listp seq) - (sort seq predicate)) - ((vectorp seq) - (let* ((list (sort (append seq nil) predicate)) - (p list) (i 0)) - (while p - (aset seq i (car p)) - (setq i (1+ i) p (cdr p))) - (apply #'vector list))) - (t (signal 'wrong-type-argument (list 'list-or-vector-p seq))))) - -;;;; Defined in editfns.c - -(compat-defalias format-message format) ;; - -;;;; Defined in fileio.c - -(compat-defun directory-name-p (name) ;; - "Return non-nil if NAME ends with a directory separator character." - (eq (eval-when-compile - (if (memq system-type '(cygwin windows-nt ms-dos)) - ?\\ ?/)) - (aref name (1- (length name))))) - -;;;; Defined in doc.c - -(compat-defvar text-quoting-style nil ;; - "Style to use for single quotes in help and messages. - -The value of this variable determines substitution of grave accents -and apostrophes in help output (but not for display of Info -manuals) and in functions like `message' and `format-message', but not -in `format'. - -The value should be one of these symbols: - `curve': quote with curved single quotes ‘like this’. - `straight': quote with straight apostrophes \\='like this\\='. - `grave': quote with grave accent and apostrophe \\=`like this\\='; - i.e., do not alter the original quote marks. - nil: like `curve' if curved single quotes are displayable, - and like `grave' otherwise. This is the default. - -You should never read the value of this variable directly from a Lisp -program. Use the function `text-quoting-style' instead, as that will -compute the correct value for the current terminal in the nil case.") - -;;;; Defined in simple.el - -;; `save-excursion' behaved like `save-mark-and-excursion' before 25.1. -(compat-defalias save-mark-and-excursion save-excursion) ;; - -(declare-function region-bounds nil) ;; Defined in compat-26.el -(compat-defun region-noncontiguous-p () ;; - "Return non-nil if the region contains several pieces. -An example is a rectangular region handled as a list of -separate contiguous regions for each line." - (let ((bounds (region-bounds))) (and (cdr bounds) bounds))) - -;;;; Defined in subr.el - -(compat-defun string-greaterp (string1 string2) ;; - "Return non-nil if STRING1 is greater than STRING2 in lexicographic order. -Case is significant. -Symbols are also allowed; their print names are used instead." - (string-lessp string2 string1)) - -(compat-defmacro with-file-modes (modes &rest body) ;; - "Execute BODY with default file permissions temporarily set to MODES. -MODES is as for `set-default-file-modes'." - (declare (indent 1) (debug t)) - (let ((umask (make-symbol "umask"))) - `(let ((,umask (default-file-modes))) - (unwind-protect - (progn - (set-default-file-modes ,modes) - ,@body) - (set-default-file-modes ,umask))))) - -(compat-defmacro if-let (spec then &rest else) ;; - "Bind variables according to SPEC and evaluate THEN or ELSE. -Evaluate each binding in turn, as in `let*', stopping if a -binding value is nil. If all are non-nil return the value of -THEN, otherwise the last form in ELSE. - -Each element of SPEC is a list (SYMBOL VALUEFORM) that binds -SYMBOL to the value of VALUEFORM. An element can additionally be -of the form (VALUEFORM), which is evaluated and checked for nil; -i.e. SYMBOL can be omitted if only the test result is of -interest. It can also be of the form SYMBOL, then the binding of -SYMBOL is checked for nil. - -As a special case, interprets a SPEC of the form \(SYMBOL SOMETHING) -like \((SYMBOL SOMETHING)). This exists for backward compatibility -with an old syntax that accepted only one binding." - (declare (indent 2) - (debug ([&or (symbolp form) - (&rest [&or symbolp (symbolp form) (form)])] - body))) - (when (and (<= (length spec) 2) (not (listp (car spec)))) - ;; Adjust the single binding case - (setq spec (list spec))) - (let ((empty (make-symbol "s")) - (last t) list) - (dolist (var spec) - (push `(,(if (cdr var) (car var) empty) - (and ,last ,(if (cdr var) (cadr var) (car var)))) - list) - (when (or (cdr var) (consp (car var))) - (setq last (caar list)))) - `(let* ,(nreverse list) - (if ,(caar list) ,then ,@else)))) - -(compat-defmacro when-let (spec &rest body) ;; - "Bind variables according to SPEC and conditionally evaluate BODY. -Evaluate each binding in turn, stopping if a binding value is nil. -If all are non-nil, return the value of the last form in BODY. - -The variable list SPEC is the same as in `if-let'." - (declare (indent 1) (debug if-let)) - (list 'if-let spec (macroexp-progn body))) - -;;;; Defined in subr-x.el - -(compat-defun hash-table-empty-p (hash-table) ;; - "Check whether HASH-TABLE is empty (has 0 elements)." - (zerop (hash-table-count hash-table))) - -(compat-defmacro thread-first (&rest forms) ;; - "Thread FORMS elements as the first argument of their successor. -Example: - (thread-first - 5 - (+ 20) - (/ 25) - - - (+ 40)) -Is equivalent to: - (+ (- (/ (+ 5 20) 25)) 40) -Note how the single `-' got converted into a list before -threading." - (declare (indent 1) - (debug (form &rest [&or symbolp (sexp &rest form)]))) - (let ((body (car forms))) - (dolist (form (cdr forms)) - (when (symbolp form) - (setq form (list form))) - (setq body (append (list (car form)) - (list body) - (cdr form)))) - body)) - -(compat-defmacro thread-last (&rest forms) ;; - "Thread FORMS elements as the last argument of their successor. -Example: - (thread-last - 5 - (+ 20) - (/ 25) - - - (+ 40)) -Is equivalent to: - (+ 40 (- (/ 25 (+ 20 5)))) -Note how the single `-' got converted into a list before -threading." - (declare (indent 1) (debug thread-first)) - (let ((body (car forms))) - (dolist (form (cdr forms)) - (when (symbolp form) - (setq form (list form))) - (setq body (append form (list body)))) - body)) - -;;;; Defined in macroexp.el - -(compat-defun macroexp-parse-body (body) ;; - "Parse a function BODY into (DECLARATIONS . EXPS)." - (let ((decls ())) - (while (and (cdr body) - (let ((e (car body))) - (or (stringp e) - (memq (car-safe e) - '(:documentation declare interactive cl-declare))))) - (push (pop body) decls)) - (cons (nreverse decls) body))) - -(compat-defun macroexp-quote (v) ;; - "Return an expression E such that `(eval E)' is V. - -E is either V or (quote V) depending on whether V evaluates to -itself or not." - (if (and (not (consp v)) - (or (keywordp v) - (not (symbolp v)) - (memq v '(nil t)))) - v - (list 'quote v))) - -(compat-defun macroexpand-1 (form &optional environment) ;; - "Perform (at most) one step of macro expansion." - (cond - ((consp form) - (let* ((head (car form)) - (env-expander (assq head environment))) - (if env-expander - (if (cdr env-expander) - (apply (cdr env-expander) (cdr form)) - form) - (if (not (and (symbolp head) (fboundp head))) - form - (let ((def (autoload-do-load (symbol-function head) head 'macro))) - (cond - ;; Follow alias, but only for macros, otherwise we may end up - ;; skipping an important compiler-macro (e.g. cl--block-wrapper). - ((and (symbolp def) (macrop def)) (cons def (cdr form))) - ((not (consp def)) form) - (t - (if (eq 'macro (car def)) - (apply (cdr def) (cdr form)) - form)))))))) - (t form))) - -(provide 'compat-25) -;;; compat-25.el ends here blob - c91a4866362986f863c4dcbf9b06fbc14be428d3 (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/compat-26.el +++ /dev/null @@ -1,554 +0,0 @@ -;;; compat-26.el --- Functionality added in Emacs 26.1 -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Functionality added in Emacs 26.1, needed by older Emacs versions. - -;;; Code: - -(eval-when-compile (load "compat-macs.el" nil t t)) -(compat-require compat-25 "25.1") - -(compat-version "26.1") - -;;;; Defined in fns.c - -(compat-defun buffer-hash (&optional buffer-or-name) ;; - "Return a hash of the contents of BUFFER-OR-NAME. -This hash is performed on the raw internal format of the buffer, -disregarding any coding systems. If nil, use the current buffer. - -This function is useful for comparing two buffers running in the same -Emacs, but is not guaranteed to return the same hash between different -Emacs versions. It should be somewhat more efficient on larger -buffers than `secure-hash' is, and should not allocate more memory. - -It should not be used for anything security-related. See -`secure-hash' for these applications." - (with-current-buffer (or buffer-or-name (current-buffer)) - (save-restriction - (widen) - (sha1 (current-buffer) (point-min) (point-max))))) - -(compat-defun mapcan (func sequence) ;; - "Apply FUNC to each element of SEQUENCE. -Concatenate the results by altering them (using `nconc'). -SEQUENCE may be a list, a vector, a boolean vector, or a string." - (apply #'nconc (mapcar func sequence))) - -(compat-defun line-number-at-pos (&optional position absolute) ;; - "Handle optional argument ABSOLUTE." - :extended t - (if absolute - (save-restriction - (widen) - (line-number-at-pos position)) - (line-number-at-pos position))) - -;;;; Defined in simple.el - -(compat-defun region-bounds () ;; - "Return the boundaries of the region. -Value is a list of one or more cons cells of the form (START . END). -It will have more than one cons cell when the region is non-contiguous, -see `region-noncontiguous-p' and `extract-rectangle-bounds'." - (if (eval-when-compile (< emacs-major-version 25)) - ;; FIXME: The `region-extract-function' of Emacs 24 has no support for the - ;; bounds argument. - (list (cons (region-beginning) (region-end))) - (funcall region-extract-function 'bounds))) - -;;;; Defined in subr.el - -(compat-defun provided-mode-derived-p (mode &rest modes) ;; - "Non-nil if MODE is derived from one of MODES. -Uses the `derived-mode-parent' property of the symbol to trace backwards. -If you just want to check `major-mode', use `derived-mode-p'." - ;; If MODE is an alias, then look up the real mode function first. - (let ((alias (symbol-function mode))) - (when (and alias (symbolp alias)) - (setq mode alias))) - (while - (and - (not (memq mode modes)) - (let* ((parent (get mode 'derived-mode-parent)) - (parentfn (symbol-function parent))) - (setq mode (if (and parentfn (symbolp parentfn)) parentfn parent))))) - mode) - -(compat-defun assoc (key alist &optional testfn) ;; - "Handle the optional TESTFN." - :extended t - (cond - ((or (eq testfn #'eq) - (and (not testfn) (or (symbolp key) (integerp key)))) ;; eq_comparable_value - (assq key alist)) - ((or (eq testfn #'equal) (not testfn)) - (assoc key alist)) - (t - (catch 'found - (dolist (ent alist) - (when (funcall testfn (car ent) key) - (throw 'found ent))))))) - -(compat-defun alist-get (key alist &optional default remove testfn) ;; - "Handle optional argument TESTFN." - :extended "25.1" - (ignore remove) - (let ((x (if (not testfn) - (assq key alist) - (compat--assoc key alist testfn)))) - (if x (cdr x) default))) - -(compat-guard t ;; - (gv-define-expander compat--alist-get - (lambda (do key alist &optional default remove testfn) - (macroexp-let2 macroexp-copyable-p k key - (gv-letplace (getter setter) alist - (macroexp-let2 nil p `(compat--assoc ,k ,getter ,testfn) - (funcall do (if (null default) `(cdr ,p) - `(if ,p (cdr ,p) ,default)) - (lambda (v) - (macroexp-let2 nil v v - (let ((set-exp - `(if ,p (setcdr ,p ,v) - ,(funcall setter - `(cons (setq ,p (cons ,k ,v)) - ,getter))))) - `(progn - ,(cond - ((null remove) set-exp) - ((or (eql v default) - (and (eq (car-safe v) 'quote) - (eq (car-safe default) 'quote) - (eql (cadr v) (cadr default)))) - `(if ,p ,(funcall setter `(delq ,p ,getter)))) - (t - `(cond - ((not (eql ,default ,v)) ,set-exp) - (,p ,(funcall setter - `(delq ,p ,getter)))))) - ,v)))))))))) - (unless (get 'alist-get 'gv-expander) - (put 'alist-get 'gv-expander (get 'compat--alist-get 'gv-expander)))) - -(compat-defun string-trim-left (string &optional regexp) ;; - "Handle optional argument REGEXP." - :extended t - (if (string-match (concat "\\`\\(?:" (or regexp "[ \t\n\r]+") "\\)") string) - (substring string (match-end 0)) - string)) - -(compat-defun string-trim-right (string &optional regexp) ;; - "Handle optional argument REGEXP." - :extended t - (let ((i (string-match-p - (concat "\\(?:" (or regexp "[ \t\n\r]+") "\\)\\'") - string))) - (if i (substring string 0 i) string))) - -(compat-defun string-trim (string &optional trim-left trim-right) ;; - "Handle optional arguments TRIM-LEFT and TRIM-RIGHT." - :extended t - (compat--string-trim-left - (compat--string-trim-right - string - trim-right) - trim-left)) - -(compat-defun caaar (x) ;; - "Return the `car' of the `car' of the `car' of X." - (declare (pure t)) - (car (car (car x)))) - -(compat-defun caadr (x) ;; - "Return the `car' of the `car' of the `cdr' of X." - (declare (pure t)) - (car (car (cdr x)))) - -(compat-defun cadar (x) ;; - "Return the `car' of the `cdr' of the `car' of X." - (declare (pure t)) - (car (cdr (car x)))) - -(compat-defun caddr (x) ;; - "Return the `car' of the `cdr' of the `cdr' of X." - (declare (pure t)) - (car (cdr (cdr x)))) - -(compat-defun cdaar (x) ;; - "Return the `cdr' of the `car' of the `car' of X." - (declare (pure t)) - (cdr (car (car x)))) - -(compat-defun cdadr (x) ;; - "Return the `cdr' of the `car' of the `cdr' of X." - (declare (pure t)) - (cdr (car (cdr x)))) - -(compat-defun cddar (x) ;; - "Return the `cdr' of the `cdr' of the `car' of X." - (declare (pure t)) - (cdr (cdr (car x)))) - -(compat-defun cdddr (x) ;; - "Return the `cdr' of the `cdr' of the `cdr' of X." - (declare (pure t)) - (cdr (cdr (cdr x)))) - -(compat-defun caaaar (x) ;; - "Return the `car' of the `car' of the `car' of the `car' of X." - (declare (pure t)) - (car (car (car (car x))))) - -(compat-defun caaadr (x) ;; - "Return the `car' of the `car' of the `car' of the `cdr' of X." - (declare (pure t)) - (car (car (car (cdr x))))) - -(compat-defun caadar (x) ;; - "Return the `car' of the `car' of the `cdr' of the `car' of X." - (declare (pure t)) - (car (car (cdr (car x))))) - -(compat-defun caaddr (x) ;; - "Return the `car' of the `car' of the `cdr' of the `cdr' of X." - (declare (pure t)) - (car (car (cdr (cdr x))))) - -(compat-defun cadaar (x) ;; - "Return the `car' of the `cdr' of the `car' of the `car' of X." - (declare (pure t)) - (car (cdr (car (car x))))) - -(compat-defun cadadr (x) ;; - "Return the `car' of the `cdr' of the `car' of the `cdr' of X." - (declare (pure t)) - (car (cdr (car (cdr x))))) - -(compat-defun caddar (x) ;; - "Return the `car' of the `cdr' of the `cdr' of the `car' of X." - (declare (pure t)) - (car (cdr (cdr (car x))))) - -(compat-defun cadddr (x) ;; - "Return the `car' of the `cdr' of the `cdr' of the `cdr' of X." - (declare (pure t)) - (car (cdr (cdr (cdr x))))) - -(compat-defun cdaaar (x) ;; - "Return the `cdr' of the `car' of the `car' of the `car' of X." - (declare (pure t)) - (cdr (car (car (car x))))) - -(compat-defun cdaadr (x) ;; - "Return the `cdr' of the `car' of the `car' of the `cdr' of X." - (declare (pure t)) - (cdr (car (car (cdr x))))) - -(compat-defun cdadar (x) ;; - "Return the `cdr' of the `car' of the `cdr' of the `car' of X." - (declare (pure t)) - (cdr (car (cdr (car x))))) - -(compat-defun cdaddr (x) ;; - "Return the `cdr' of the `car' of the `cdr' of the `cdr' of X." - (declare (pure t)) - (cdr (car (cdr (cdr x))))) - -(compat-defun cddaar (x) ;; - "Return the `cdr' of the `cdr' of the `car' of the `car' of X." - (declare (pure t)) - (cdr (cdr (car (car x))))) - -(compat-defun cddadr (x) ;; - "Return the `cdr' of the `cdr' of the `car' of the `cdr' of X." - (declare (pure t)) - (cdr (cdr (car (cdr x))))) - -(compat-defun cdddar (x) ;; - "Return the `cdr' of the `cdr' of the `cdr' of the `car' of X." - (declare (pure t)) - (cdr (cdr (cdr (car x))))) - -(compat-defun cddddr (x) ;; - "Return the `cdr' of the `cdr' of the `cdr' of the `cdr' of X." - (declare (pure t)) - (cdr (cdr (cdr (cdr x))))) - -(compat-defvar gensym-counter 0 ;; - "Number used to construct the name of the next symbol created by `gensym'.") - -(compat-defun gensym (&optional prefix) ;; - "Return a new uninterned symbol. -The name is made by appending `gensym-counter' to PREFIX. -PREFIX is a string, and defaults to \"g\"." - (let ((num (prog1 gensym-counter - (setq gensym-counter - (1+ gensym-counter))))) - (make-symbol (format "%s%d" (or prefix "g") num)))) - -(compat-defmacro if-let* (varlist then &rest else) ;; - "Bind variables according to VARLIST and evaluate THEN or ELSE. -This is like `if-let' but doesn't handle a VARLIST of the form -\(SYMBOL SOMETHING) specially." - (declare (indent 2) - (debug ((&rest [&or symbolp (symbolp form) (form)]) - body))) - (let ((empty (make-symbol "s")) - (last t) list) - (dolist (var varlist) - (push `(,(if (cdr var) (car var) empty) - (and ,last ,(if (cdr var) (cadr var) (car var)))) - list) - (when (or (cdr var) (consp (car var))) - (setq last (caar list)))) - `(let* ,(nreverse list) - (if ,(caar list) ,then ,@else)))) - -(compat-defmacro when-let* (varlist &rest body) ;; - "Bind variables according to VARLIST and conditionally evaluate BODY. -This is like `when-let' but doesn't handle a VARLIST of the form -\(SYMBOL SOMETHING) specially." - (declare (indent 1) (debug if-let*)) - (list 'if-let* varlist (macroexp-progn body))) - -(compat-defmacro and-let* (varlist &rest body) ;; - "Bind variables according to VARLIST and conditionally evaluate BODY. -Like `when-let*', except if BODY is empty and all the bindings -are non-nil, then the result is non-nil." - (declare (indent 1) - (debug ((&rest [&or symbolp (symbolp form) (form)]) - body))) - (let ((empty (make-symbol "s")) - (last t) list) - (dolist (var varlist) - (push `(,(if (cdr var) (car var) empty) - (and ,last ,(if (cdr var) (cadr var) (car var)))) - list) - (when (or (cdr var) (consp (car var))) - (setq last (caar list)))) - `(let* ,(nreverse list) - (if ,(caar list) ,(macroexp-progn (or body '(t))))))) - -;;;; Defined in files.el - -(compat-defvar mounted-file-systems ;; - (eval-when-compile - (if (memq system-type '(windows-nt cygwin)) - "^//[^/]+/" - (concat - "^" (regexp-opt '("/afs/" "/media/" "/mnt" "/net/" "/tmp_mnt/"))))) - "File systems that ought to be mounted.") - -(compat-defun file-local-name (file) ;; - "Return the local name component of FILE. -This function removes from FILE the specification of the remote host -and the method of accessing the host, leaving only the part that -identifies FILE locally on the remote system. -The returned file name can be used directly as argument of -`process-file', `start-file-process', or `shell-command'." - (or (file-remote-p file 'localname) file)) - -(compat-defun temporary-file-directory () ;; - "The directory for writing temporary files. -In case of a remote `default-directory', this is a directory for -temporary files on that remote host. If such a directory does -not exist, or `default-directory' ought to be located on a -mounted file system (see `mounted-file-systems'), the function -returns `default-directory'. -For a non-remote and non-mounted `default-directory', the value of -the variable `temporary-file-directory' is returned." - ;; NOTE: The handler may fail with an error, since the - ;; `temporary-file-directory' handler was introduced in Emacs 26. - (let ((handler (find-file-name-handler - default-directory 'temporary-file-directory))) - (or (and handler (ignore-errors (funcall handler 'temporary-file-directory))) - (if-let ((remote (file-remote-p default-directory))) - (concat remote "/tmp/") ;; FIXME: Guess /tmp on remote host - (if (string-match mounted-file-systems default-directory) - default-directory - temporary-file-directory))))) - -(compat-defun make-temp-file (prefix &optional dir-flag suffix text) ;; - "Handle optional argument TEXT." - :extended t - (let ((file (make-temp-file prefix dir-flag suffix))) - (when text - (with-temp-buffer - (insert text) - (write-region (point-min) (point-max) file))) - file)) - -(compat-defun make-nearby-temp-file (prefix &optional dir-flag suffix) ;; - "Create a temporary file as close as possible to `default-directory'. -If PREFIX is a relative file name, and `default-directory' is a -remote file name or located on a mounted file systems, the -temporary file is created in the directory returned by the -function `temporary-file-directory'. Otherwise, the function -`make-temp-file' is used. PREFIX, DIR-FLAG and SUFFIX have the -same meaning as in `make-temp-file'." - ;; NOTE: The handler may fail with an error, since the - ;; `make-nearby-temp-file' handler was introduced in Emacs 26. - (let ((handler (and (not (file-name-absolute-p default-directory)) - (find-file-name-handler - default-directory 'make-nearby-temp-file)))) - (or (and handler (ignore-errors (funcall handler 'make-nearby-temp-file - prefix dir-flag suffix))) - (let ((temporary-file-directory (temporary-file-directory))) - (make-temp-file prefix dir-flag suffix))))) - -(compat-defun file-attribute-type (attributes) ;; - "The type field in ATTRIBUTES returned by `file-attributes'. -The value is either t for directory, string (name linked to) for -symbolic link, or nil." - (nth 0 attributes)) - -(compat-defun file-attribute-link-number (attributes) ;; - "Return the number of links in ATTRIBUTES returned by `file-attributes'." - (nth 1 attributes)) - -(compat-defun file-attribute-user-id (attributes) ;; - "The UID field in ATTRIBUTES returned by `file-attributes'. -This is either a string or a number. If a string value cannot be -looked up, a numeric value, either an integer or a float, is -returned." - (nth 2 attributes)) - -(compat-defun file-attribute-group-id (attributes) ;; - "The GID field in ATTRIBUTES returned by `file-attributes'. -This is either a string or a number. If a string value cannot be -looked up, a numeric value, either an integer or a float, is -returned." - (nth 3 attributes)) - -(compat-defun file-attribute-access-time (attributes) ;; - "The last access time in ATTRIBUTES returned by `file-attributes'. -This a Lisp timestamp in the style of `current-time'." - (nth 4 attributes)) - -(compat-defun file-attribute-modification-time (attributes) ;; - "The modification time in ATTRIBUTES returned by `file-attributes'. -This is the time of the last change to the file's contents, and -is a Lisp timestamp in the style of `current-time'." - (nth 5 attributes)) - -(compat-defun file-attribute-status-change-time (attributes) ;; - "The status modification time in ATTRIBUTES returned by `file-attributes'. -This is the time of last change to the file's attributes: owner -and group, access mode bits, etc., and is a Lisp timestamp in the -style of `current-time'." - (nth 6 attributes)) - -(compat-defun file-attribute-size (attributes) ;; - "The integer size (in bytes) in ATTRIBUTES returned by `file-attributes'." - (nth 7 attributes)) - -(compat-defun file-attribute-modes (attributes) ;; - "The file modes in ATTRIBUTES returned by `file-attributes'. -This is a string of ten letters or dashes as in ls -l." - (nth 8 attributes)) - -(compat-defun file-attribute-inode-number (attributes) ;; - "The inode number in ATTRIBUTES returned by `file-attributes'. -It is a nonnegative integer." - (nth 10 attributes)) - -(compat-defun file-attribute-device-number (attributes) ;; - "The file system device number in ATTRIBUTES returned by `file-attributes'. -It is an integer." - (nth 11 attributes)) - -(compat-defun file-attribute-collect (attributes &rest attr-names) ;; - "Return a sublist of ATTRIBUTES returned by `file-attributes'. -ATTR-NAMES are symbols with the selected attribute names. - -Valid attribute names are: type, link-number, user-id, group-id, -access-time, modification-time, status-change-time, size, modes, -inode-number and device-number." - (let ((idx '((type . 0) - (link-number . 1) - (user-id . 2) - (group-id . 3) - (access-time . 4) - (modification-time . 5) - (status-change-time . 6) - (size . 7) - (modes . 8) - (inode-number . 10) - (device-number . 11))) - result) - (while attr-names - (let ((attr (pop attr-names))) - (if (assq attr idx) - (push (nth (cdr (assq attr idx)) - attributes) - result) - (error "Wrong attribute name '%S'" attr)))) - (nreverse result))) - -;;;; Defined in mouse.el - -(compat-defvar mouse-select-region-move-to-beginning nil ;; - "Effect of selecting a region extending backward from double click. -Nil means keep point at the position clicked (region end); -non-nil means move point to beginning of region.") - -;;;; Defined in image.el - -(compat-defun image-property (image property) ;; - "Return the value of PROPERTY in IMAGE. -Properties can be set with - - (setf (image-property IMAGE PROPERTY) VALUE) - -If VALUE is nil, PROPERTY is removed from IMAGE." - :feature image - (plist-get (cdr image) property)) - -;;;; Defined in rmc.el - -(compat-defun read-multiple-choice (prompt choices) ;; - "Ask user to select an entry from CHOICES, promting with PROMPT. -This function allows to ask the user a multiple-choice question. - -CHOICES should be a list of the form (KEY NAME [DESCRIPTION]). -KEY is a character the user should type to select the entry. -NAME is a short name for the entry to be displayed while prompting -\(if there's no room, it might be shortened). - -NOTE: This is a partial implementation of `read-multiple-choice', that -among other things doesn't offer any help and ignores the -optional DESCRIPTION field." - (let ((options - (mapconcat - (lambda (opt) - (format - "[%s] %s" - (key-description (string (car opt))) - (cadr opt))) - choices " ")) - choice) - (setq prompt (concat prompt " (" options "): ")) - (while (not (setq choice (assq (read-event prompt) choices))) - (message "Invalid choice") - (sit-for 1)) - choice)) - -(provide 'compat-26) -;;; compat-26.el ends here blob - 3de78a49dce55b73285d04c10b3369bdd3011b59 (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/compat-27.el +++ /dev/null @@ -1,885 +0,0 @@ -;;; compat-27.el --- Functionality added in Emacs 27.1 -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Functionality added in Emacs 27.1, needed by older Emacs versions. - -;;; Code: - -(eval-when-compile (load "compat-macs.el" nil t t)) -(compat-require compat-26 "26.1") - -(compat-version "27.1") - -;;;; Defined in fns.c - -(compat-defun proper-list-p (object) ;; - "Return OBJECT's length if it is a proper list, nil otherwise. -A proper list is neither circular nor dotted (i.e., its last cdr -is nil)." - (if (eval-when-compile (< emacs-major-version 26)) - ;; On older Emacs than 26.1 use Tortoise and Hare algorithm - (when (listp object) - (catch 'cycle - (let ((hare object) (tortoise object) - (max 2) (q 2)) - (while (consp hare) - (setq hare (cdr hare)) - (when (and (or (/= 0 (setq q (1- q))) - (ignore - (setq max (ash max 1) - q max - tortoise hare))) - (eq hare tortoise)) - (throw 'cycle nil))) - (and (null hare) (length object))))) - ;; Errors on 26.1 and newer - (and (listp object) (ignore-errors (length object))))) - -(compat-defun string-distance (string1 string2 &optional bytecompare) ;; - "Return Levenshtein distance between STRING1 and STRING2. -The distance is the number of deletions, insertions, and substitutions -required to transform STRING1 into STRING2. -If BYTECOMPARE is nil or omitted, compute distance in terms of characters. -If BYTECOMPARE is non-nil, compute distance in terms of bytes. -Letter-case is significant, but text properties are ignored." - ;; https://en.wikipedia.org/wiki/Levenshtein_distance - (let ((s1 (if bytecompare - (encode-coding-string string1 'raw-text) - (concat string1 ""))) - (s2 (if bytecompare - (encode-coding-string string2 'raw-text) - string2))) - (let* ((len1 (length s1)) - (len2 (length s2)) - (column (make-vector (1+ len1) 0))) - (dotimes (y len1) - (setf (aref column (1+ y)) y)) - (dotimes (x len2) - (setf (aref column 0) (1+ x)) - (let ((lastdiag x) olddiag) - (dotimes (y len1) - (setf olddiag (aref column (1+ y)) - (aref column (1+ y)) - (min (+ (if (= (aref s1 y) (aref s2 x)) 0 1) - lastdiag) - (1+ (aref column (1+ y))) - (1+ (aref column y))) - lastdiag olddiag)))) - (aref column len1)))) - -;;;; Defined in window.c - -(compat-defun recenter (&optional arg redisplay) ;; - "Handle optional argument REDISPLAY." - :extended t - (recenter arg) - (when (and redisplay recenter-redisplay) - (redisplay))) - -;;;; Defined in keymap.c - -(compat-defun lookup-key (keymap key &optional accept-default) ;; - "Allow for KEYMAP to be a list of keymaps." - :extended t - (cond - ((keymapp keymap) - (lookup-key keymap key accept-default)) - ((listp keymap) - (catch 'found - (dolist (map keymap) - (when-let ((fn (lookup-key map key accept-default))) - (throw 'found fn))))) - ((signal 'wrong-type-argument (list 'keymapp keymap))))) - -;;;; Defined in timefns.c - -(compat-defun time-equal-p (t1 t2) ;; - "Return non-nil if time value T1 is equal to time value T2. -A nil value for either argument stands for the current time. - -NOTE: This function is not as accurate as the actual `time-equal-p'." - (cond - ((eq t1 t2)) - ((and (consp t1) (consp t2)) - (equal t1 t2)) - (t - ;; Due to inaccuracies and the relatively slow evaluating of - ;; Emacs Lisp compared to C, we allow for slight inaccuracies - ;; (less than a millisecond) when comparing time values. - (< (abs (- (float-time t1) (float-time t2))) - (if (and t1 t2) 1e-6 1e-5))))) - -;;;; Defined in subr.el - -(compat-defalias fixnump integerp) ;; -(compat-defalias bignump ignore) ;; - -(compat-defmacro setq-local (&rest pairs) ;; - "Handle multiple assignments." - :extended t - (unless (zerop (mod (length pairs) 2)) - (error "PAIRS must have an even number of variable/value members")) - (let (body) - (while pairs - (let* ((sym (pop pairs)) - (val (pop pairs))) - (unless (symbolp sym) - (error "Attempting to set a non-symbol: %s" (car pairs))) - (push `(set (make-local-variable ',sym) ,val) - body))) - (cons 'progn (nreverse body)))) - -(compat-defmacro ignore-error (condition &rest body) ;; - "Execute BODY; if the error CONDITION occurs, return nil. -Otherwise, return result of last form in BODY. - -CONDITION can also be a list of error conditions." - (declare (debug t) (indent 1)) - `(condition-case nil (progn ,@body) (,condition nil))) - -(compat-defmacro dolist-with-progress-reporter (spec reporter-or-message &rest body) ;; - "Loop over a list and report progress in the echo area. -Evaluate BODY with VAR bound to each car from LIST, in turn. -Then evaluate RESULT to get return value, default nil. - -REPORTER-OR-MESSAGE is a progress reporter object or a string. In the latter -case, use this string to create a progress reporter. - -At each iteration, print the reporter message followed by progress -percentage in the echo area. After the loop is finished, -print the reporter message followed by the word \"done\". - -\(fn (VAR LIST [RESULT]) REPORTER-OR-MESSAGE BODY...)" - (declare (indent 2) (debug ((symbolp form &optional form) form body))) - (let ((prep (make-symbol "--dolist-progress-reporter--")) - (count (make-symbol "--dolist-count--")) - (list (make-symbol "--dolist-list--"))) - `(let ((,prep ,reporter-or-message) - (,count 0) - (,list ,(cadr spec))) - (when (stringp ,prep) - (setq ,prep (make-progress-reporter ,prep 0 (length ,list)))) - (dolist (,(car spec) ,list) - ,@body - (progress-reporter-update ,prep (setq ,count (1+ ,count)))) - (progress-reporter-done ,prep) - (or ,@(cdr (cdr spec)) nil)))) - -(compat-defun flatten-tree (tree) ;; - "Return a \"flattened\" copy of TREE. -In other words, return a list of the non-nil terminal nodes, or -leaves, of the tree of cons cells rooted at TREE. Leaves in the -returned list are in the same order as in TREE. - -\(flatten-tree \\='(1 (2 . 3) nil (4 5 (6)) 7)) -=> (1 2 3 4 5 6 7)" - (let (elems) - (while (consp tree) - (let ((elem (pop tree))) - (while (consp elem) - (push (cdr elem) tree) - (setq elem (car elem))) - (if elem (push elem elems)))) - (if tree (push tree elems)) - (nreverse elems))) - -(compat-defun xor (cond1 cond2) ;; - "Return the boolean exclusive-or of COND1 and COND2. -If only one of the arguments is non-nil, return it; otherwise -return nil." - (declare (pure t) (side-effect-free error-free)) - (cond ((not cond1) cond2) - ((not cond2) cond1))) - -(compat-defvar regexp-unmatchable "\\`a\\`" ;; - "Standard regexp guaranteed not to match any string at all." - :constant t) - -(compat-defun assoc-delete-all (key alist &optional test) ;; - "Handle optional argument TEST." - :extended "26.2" - (unless test (setq test #'equal)) - (while (and (consp (car alist)) - (funcall test (caar alist) key)) - (setq alist (cdr alist))) - (let ((tail alist) tail-cdr) - (while (setq tail-cdr (cdr tail)) - (if (and (consp (car tail-cdr)) - (funcall test (caar tail-cdr) key)) - (setcdr tail (cdr tail-cdr)) - (setq tail tail-cdr)))) - alist) - -(compat-defvar major-mode--suspended nil ;; - "Suspended major mode." - :local permanent) - -(compat-defun major-mode-suspend () ;; - "Exit current major mode, remembering it." - (let* ((prev-major-mode (or major-mode--suspended - (unless (eq major-mode 'fundamental-mode) - major-mode)))) - (kill-all-local-variables) - (setq-local major-mode--suspended prev-major-mode))) - -(compat-defun major-mode-restore (&optional avoided-modes) ;; - "Restore major mode earlier suspended with `major-mode-suspend'. -If there was no earlier suspended major mode, then fallback to `normal-mode', -though trying to avoid AVOIDED-MODES." - (if major-mode--suspended - (funcall (prog1 major-mode--suspended - (kill-local-variable 'major-mode--suspended))) - (let ((auto-mode-alist - (let ((alist (copy-sequence auto-mode-alist))) - (dolist (mode avoided-modes) - (setq alist (rassq-delete-all mode alist))) - alist)) - (magic-fallback-mode-alist - (let ((alist (copy-sequence magic-fallback-mode-alist))) - (dolist (mode avoided-modes) - (setq alist (rassq-delete-all mode alist))) - alist))) - (normal-mode)))) - -(compat-defun read-char-from-minibuffer-insert-char () ;; - "Insert the character you type into the minibuffer and exit minibuffer. -Discard all previous input before inserting and exiting the minibuffer." - (interactive) - (when (minibufferp) - (delete-minibuffer-contents) - (insert last-command-event) - (exit-minibuffer))) - -(compat-defun read-char-from-minibuffer-insert-other () ;; - "Reject a disallowed character typed into the minibuffer. -This command is intended to be bound to keys that users are not -allowed to type into the minibuffer. When the user types any -such key, this command discard all minibuffer input and displays -an error message." - (interactive) - (when (minibufferp) - (delete-minibuffer-contents) - (ding) - (discard-input) - (minibuffer-message "Wrong answer") - (sit-for 2))) - -(compat-defvar read-char-history nil ;; - "The default history for the `read-char-from-minibuffer' function.") - -(compat-defvar read-char-from-minibuffer-map ;; - (let ((map (make-sparse-keymap))) - (set-keymap-parent map minibuffer-local-map) - (define-key map [remap self-insert-command] #'read-char-from-minibuffer-insert-char) - (define-key map [remap exit-minibuffer] #'read-char-from-minibuffer-insert-other) - map) - "Keymap for the `read-char-from-minibuffer' function.") - -(compat-defvar read-char-from-minibuffer-map-hash ;; - (make-hash-table :test 'equal) - "Hash table of keymaps used by `read-char-from-minibuffer'." - :constant t) - -(compat-defun read-char-from-minibuffer (prompt &optional chars history) ;; - "Read a character from the minibuffer, prompting for it with PROMPT. -Like `read-char', but uses the minibuffer to read and return a character. -Optional argument CHARS, if non-nil, should be a list of characters; -the function will ignore any input that is not one of CHARS. -Optional argument HISTORY, if non-nil, should be a symbol that -specifies the history list variable to use for navigating in input -history using \\`M-p' and \\`M-n', with \\`RET' to select a character from -history. -If you bind the variable `help-form' to a non-nil value -while calling this function, then pressing `help-char' -causes it to evaluate `help-form' and display the result. -There is no need to explicitly add `help-char' to CHARS; -`help-char' is bound automatically to `help-form-show'." - (let* ((map (if (consp chars) - (or (gethash (list help-form (cons help-char chars)) - read-char-from-minibuffer-map-hash) - (let ((map (make-sparse-keymap)) - (msg help-form)) - (set-keymap-parent map read-char-from-minibuffer-map) - ;; If we have a dynamically bound `help-form' - ;; here, then the `C-h' (i.e., `help-char') - ;; character should output that instead of - ;; being a command char. - (when help-form - (define-key map (vector help-char) - (lambda () - (interactive) - (let ((help-form msg)) ; lexically bound msg - (help-form-show))))) - (dolist (char chars) - (define-key map (vector char) - #'read-char-from-minibuffer-insert-char)) - (define-key map [remap self-insert-command] - #'read-char-from-minibuffer-insert-other) - (puthash (list help-form (cons help-char chars)) - map read-char-from-minibuffer-map-hash) - map)) - read-char-from-minibuffer-map)) - ;; Protect this-command when called from pre-command-hook (bug#45029) - (this-command this-command) - (result (read-from-minibuffer prompt nil map nil (or history t))) - (char - (if (> (length result) 0) - ;; We have a string (with one character), so return the first one. - (elt result 0) - ;; The default value is RET. - (when history (push "\r" (symbol-value history))) - ?\r))) - ;; Display the question with the answer. - (message "%s%s" prompt (char-to-string char)) - char)) - -;;;; Defined in simple.el - -(compat-guard (not (fboundp 'decoded-time-second)) ;; - (cl-defstruct (decoded-time - (:constructor nil) - (:copier nil) - (:type list)) - (second nil :documentation "\ -This is an integer or a Lisp timestamp (TICKS . HZ) representing a nonnegative -number of seconds less than 61. (If not less than 60, it is a leap second, -which only some operating systems support.)") - (minute nil :documentation "This is an integer between 0 and 59 (inclusive).") - (hour nil :documentation "This is an integer between 0 and 23 (inclusive).") - (day nil :documentation "This is an integer between 1 and 31 (inclusive).") - (month nil :documentation "\ -This is an integer between 1 and 12 (inclusive). January is 1.") - (year nil :documentation "This is a four digit integer.") - (weekday nil :documentation "\ -This is a number between 0 and 6, and 0 is Sunday.") - (dst -1 :documentation "\ -This is t if daylight saving time is in effect, nil if it is not -in effect, and -1 if daylight saving information is not available. -Also see `decoded-time-dst'.") - (zone nil :documentation "\ -This is an integer indicating the UTC offset in seconds, i.e., -the number of seconds east of Greenwich."))) - -(compat-defun minibuffer-history-value () ;; - "Return the value of the minibuffer input history list. -If `minibuffer-history-variable' points to a buffer-local variable and -the minibuffer is active, return the buffer-local value for the buffer -that was current when the minibuffer was activated." - (buffer-local-value minibuffer-history-variable - (window-buffer (minibuffer-selected-window)))) - -;;;; Defined in minibuffer.el - -(compat-defmacro with-minibuffer-selected-window (&rest body) ;; - "Execute the forms in BODY from the minibuffer in its original window. -When used in a minibuffer window, select the window selected just before -the minibuffer was activated, and execute the forms." - (declare (indent 0) (debug t)) - `(when-let ((window (minibuffer-selected-window))) - (with-selected-window window - ,@body))) - -;;;; Defined in byte-run.el - -(compat-defmacro with-suppressed-warnings (_warnings &rest body) ;; - "Like `progn', but prevents compiler WARNINGS in BODY. -NOTE: The compatibility version behaves like `with-no-warnings'." - `(with-no-warnings ,@body)) - -;;;; Defined in image.el - -(compat-defun image--set-property (image property value) ;; - "Set PROPERTY in IMAGE to VALUE, internal use only." - :extended "26.1" - :feature image - (if (null value) - (while (cdr image) - (if (eq (cadr image) property) - (setcdr image (cdddr image)) - (setq image (cddr image)))) - (setcdr image (plist-put (cdr image) property value))) - value) - -;; HACK: image--set-property was broken with an off-by-one error on Emacs 26. -;; The bug was fixed in a4ad7bed187493c1c230f223b52c71f5c34f7c89. Therefore we -;; override the gv expander until Emacs 27.1. -(compat-guard ;; - (or (= emacs-major-version 26) (not (get 'image-property 'gv-expander))) - :feature image - (gv-define-setter image-property (value image prop) - `(,(if (< emacs-major-version 26) 'image--set-property 'compat--image--set-property) - ,image ,prop ,value))) - -;;;; Defined in files.el - -(compat-defun file-name-quoted-p (name &optional top) ;; - "Handle optional argument TOP." - :extended "26.1" - (let ((file-name-handler-alist (unless top file-name-handler-alist))) - (string-prefix-p "/:" (file-local-name name)))) - -(compat-defun file-name-quote (name &optional top) ;; - "Handle optional argument TOP." - :extended "26.1" - (let* ((file-name-handler-alist (unless top file-name-handler-alist)) - (localname (file-local-name name))) - (if (string-prefix-p "/:" localname) - name - (concat (file-remote-p name) "/:" localname)))) - -(compat-defun file-name-unquote (name &optional top) ;; - "Handle optional argument TOP." - :extended "26.1" - (let* ((file-name-handler-alist (unless top file-name-handler-alist)) - (localname (file-local-name name))) - (when (string-prefix-p "/:" localname) - (setq localname (if (= (length localname) 2) "/" (substring localname 2)))) - (concat (file-remote-p name) localname))) - -(compat-defun file-size-human-readable (file-size &optional flavor space unit) ;; - "Handle the optional arguments SPACE and UNIT." - :extended t - (let ((power (if (or (null flavor) (eq flavor 'iec)) - 1024.0 - 1000.0)) - (prefixes '("" "k" "M" "G" "T" "P" "E" "Z" "Y"))) - (while (and (>= file-size power) (cdr prefixes)) - (setq file-size (/ file-size power) - prefixes (cdr prefixes))) - (let* ((prefix (car prefixes)) - (prefixed-unit (if (eq flavor 'iec) - (concat - (if (string= prefix "k") "K" prefix) - (if (string= prefix "") "" "i") - (or unit "B")) - (concat prefix unit)))) - (format (if (and (>= (mod file-size 1.0) 0.05) - (< (mod file-size 1.0) 0.95)) - "%.1f%s%s" - "%.0f%s%s") - file-size - (if (string= prefixed-unit "") "" (or space "")) - prefixed-unit)))) - -(compat-defun file-size-human-readable-iec (size) ;; - "Human-readable string for SIZE bytes, using IEC prefixes." - (compat--file-size-human-readable size 'iec " ")) - -(compat-defun exec-path () ;; - "Return list of directories to search programs to run in remote subprocesses. -The remote host is identified by `default-directory'. For remote -hosts that do not support subprocesses, this returns nil. -If `default-directory' is a local directory, this function returns -the value of the variable `exec-path'." - (let ((handler (find-file-name-handler default-directory 'exec-path))) - ;; NOTE: The handler may fail since it was added in 27.1. - (or (and handler (ignore-errors (funcall handler 'exec-path))) - (if (file-remote-p default-directory) - ;; FIXME: Just return some standard path on remote - '("/bin" "/usr/bin" "/sbin" "/usr/sbin" "/usr/local/bin" "/usr/local/sbin") - exec-path)))) - -(compat-defun executable-find (command &optional remote) ;; - "Handle optional argument REMOTE." - :extended t - (if (and remote (file-remote-p default-directory)) - (let ((res (locate-file - command - (mapcar - (apply-partially - #'concat (file-remote-p default-directory)) - (exec-path)) - exec-suffixes 'file-executable-p))) - (when (stringp res) (file-local-name res))) - (executable-find command))) - -(compat-defun make-empty-file (filename &optional parents) ;; - "Create an empty file FILENAME. -Optional arg PARENTS, if non-nil then creates parent dirs as needed." - (when (and (file-exists-p filename) (null parents)) - (signal 'file-already-exists (list "File exists" filename))) - (let ((paren-dir (file-name-directory filename))) - (when (and paren-dir (not (file-exists-p paren-dir))) - (make-directory paren-dir parents))) - (write-region "" nil filename nil 0)) - -;;;; Defined in regexp-opt.el - -(compat-defun regexp-opt (strings &optional paren) ;; - "Handle an empty list of STRINGS." - :extended t - (if (null strings) - (let ((re "\\`a\\`")) - (cond ((null paren) - (concat "\\(?:" re "\\)")) - ((stringp paren) - (concat paren re "\\)")) - ((eq paren 'words) - (concat "\\<\\(" re "\\)\\>")) - ((eq paren 'symbols) - (concat "\\_\\(<" re "\\)\\_>")) - ((concat "\\(" re "\\)")))) - (regexp-opt strings paren))) - -;;;; Defined in package.el - -(declare-function lm-header "lisp-mnt") -(declare-function macroexp-file-name nil) - -(compat-defun package-get-version () ;; - "Return the version number of the package in which this is used. -Assumes it is used from an Elisp file placed inside the top-level directory -of an installed ELPA package. -The return value is a string (or nil in case we can’t find it)." - ;; No :feature since the function is autoloaded. - ;; In a sense, this is a lie, but it does just what we want: precompute - ;; the version at compile time and hardcodes it into the .elc file! - (declare (pure t)) - ;; Hack alert! - (let ((file (or (macroexp-file-name) buffer-file-name))) - (cond - ((null file) nil) - ;; Packages are normally installed into directories named "-", - ;; so get the version number from there. - ((string-match - "/[^/]+-\\([0-9]\\(?:[0-9.]\\|pre\\|beta\\|alpha\\|snapshot\\)+\\)/[^/]+\\'" - file) - (match-string 1 file)) - ;; For packages run straight from the an elpa.git clone, there's no - ;; "-" in the directory name, so we have to fetch the version - ;; the hard way. - ((let* ((pkgdir (file-name-directory file)) - (pkgname (file-name-nondirectory (directory-file-name pkgdir))) - (mainfile (expand-file-name (concat pkgname ".el") pkgdir))) - (when (file-readable-p mainfile) - (require 'lisp-mnt) - (with-temp-buffer - (insert-file-contents mainfile) - (or (lm-header "package-version") - (lm-header "version"))))))))) - -;;;; Defined in time-date.el - -(compat-defun make-decoded-time ;; - (&key second minute hour day month year (dst -1) zone) - "Return a `decoded-time' structure with only the keywords given filled out." - :feature time-date - (list second minute hour day month year nil dst zone)) - -(compat-defun date-days-in-month (year month) ;; - "The number of days in MONTH in YEAR." - :feature time-date - (unless (and (numberp month) - (<= 1 month) - (<= month 12)) - (error "Month %s is invalid" month)) - (if (= month 2) - (if (date-leap-year-p year) - 29 - 28) - (if (memq month '(1 3 5 7 8 10 12)) - 31 - 30))) - -(compat-defun date-ordinal-to-time (year ordinal) ;; - "Convert a YEAR/ORDINAL to the equivalent `decoded-time' structure. -ORDINAL is the number of days since the start of the year, with -January 1st being 1." - (let ((month 1)) - (while (> ordinal (date-days-in-month year month)) - (setq ordinal (- ordinal (date-days-in-month year month)) - month (1+ month))) - (list nil nil nil ordinal month year nil nil nil))) - -;;;; Defined in text-property-search.el - -(declare-function make-prop-match nil) -(compat-guard (not (fboundp 'make-prop-match)) ;; - (cl-defstruct (prop-match) beginning end value)) - -(compat-defun text-property-search-forward ;; - (property &optional value predicate not-current) - "Search for the next region of text where PREDICATE is true. -PREDICATE is used to decide whether a value of PROPERTY should be -considered as matching VALUE. - -If PREDICATE is a function, it will be called with two arguments: -VALUE and the value of PROPERTY. The function should return -non-nil if these two values are to be considered a match. - -Two special values of PREDICATE can also be used: -If PREDICATE is t, that means a value must `equal' VALUE to be -considered a match. -If PREDICATE is nil (which is the default value), a value will -match if is not `equal' to VALUE. Furthermore, a nil PREDICATE -means that the match region is ended if the value changes. For -instance, this means that if you loop with - - (while (setq prop (text-property-search-forward \\='face)) - ...) - -you will get all distinct regions with non-nil `face' values in -the buffer, and the `prop' object will have the details about the -match. See the manual for more details and examples about how -VALUE and PREDICATE interact. - -If NOT-CURRENT is non-nil, the function will search for the first -region that doesn't include point and has a value of PROPERTY -that matches VALUE. - -If no matches can be found, return nil and don't move point. -If found, move point to the end of the region and return a -`prop-match' object describing the match. To access the details -of the match, use `prop-match-beginning' and `prop-match-end' for -the buffer positions that limit the region, and -`prop-match-value' for the value of PROPERTY in the region." - (let* ((match-p - (lambda (prop-value) - (funcall - (cond - ((eq predicate t) - #'equal) - ((eq predicate nil) - (lambda (val p-val) - (not (equal val p-val)))) - (predicate)) - value prop-value))) - (find-end - (lambda (start) - (let (end) - (if (and value - (null predicate)) - ;; This is the normal case: We're looking for areas where the - ;; values aren't, so we aren't interested in sub-areas where the - ;; property has different values, all non-matching value. - (let ((ended nil)) - (while (not ended) - (setq end (next-single-property-change (point) property)) - (if (not end) - (progn - (goto-char (point-max)) - (setq end (point) - ended t)) - (goto-char end) - (unless (funcall match-p (get-text-property (point) property)) - (setq ended t))))) - ;; End this at the first place the property changes value. - (setq end (next-single-property-change (point) property nil (point-max))) - (goto-char end)) - (make-prop-match - :beginning start - :end end - :value (get-text-property start property)))))) - (cond - ;; No matches at the end of the buffer. - ((eobp) - nil) - ;; We're standing in the property we're looking for, so find the - ;; end. - ((and (funcall match-p (get-text-property (point) property)) - (not not-current)) - (funcall find-end (point))) - (t - (let ((origin (point)) - (ended nil) - pos) - ;; Find the next candidate. - (while (not ended) - (setq pos (next-single-property-change (point) property)) - (if (not pos) - (progn - (goto-char origin) - (setq ended t)) - (goto-char pos) - (if (funcall match-p (get-text-property (point) property)) - (setq ended (funcall find-end (point))) - ;; Skip past this section of non-matches. - (setq pos (next-single-property-change (point) property)) - (unless pos - (goto-char origin) - (setq ended t))))) - (and (not (eq ended t)) - ended)))))) - -(compat-defun text-property-search-backward ;; - (property &optional value predicate not-current) - "Search for the previous region of text whose PROPERTY matches VALUE. - -Like `text-property-search-forward', which see, but searches backward, -and if a matching region is found, place point at the start of the region." - (let* ((match-p - (lambda (prop-value) - (funcall - (cond - ((eq predicate t) - #'equal) - ((eq predicate nil) - (lambda (val p-val) - (not (equal val p-val)))) - (predicate)) - value prop-value))) - (find-end - (lambda (start) - (let (end) - (if (and value - (null predicate)) - ;; This is the normal case: We're looking for areas where the - ;; values aren't, so we aren't interested in sub-areas where the - ;; property has different values, all non-matching value. - (let ((ended nil)) - (while (not ended) - (setq end (previous-single-property-change (point) property)) - (if (not end) - (progn - (goto-char (point-min)) - (setq end (point) - ended t)) - (goto-char (1- end)) - (unless (funcall match-p (get-text-property (point) property)) - (goto-char end) - (setq ended t))))) - ;; End this at the first place the property changes value. - (setq end (previous-single-property-change - (point) property nil (point-min))) - (goto-char end)) - (make-prop-match - :beginning end - :end (1+ start) - :value (get-text-property end property)))))) - (cond - ;; We're at the start of the buffer; no previous matches. - ((bobp) - nil) - ;; We're standing in the property we're looking for, so find the - ;; end. - ((funcall match-p (get-text-property (1- (point)) property)) - (let ((origin (point)) - (match (funcall find-end (1- (point)) property value predicate))) - ;; When we want to ignore the current element, then repeat the - ;; search if we haven't moved out of it yet. - (if (and not-current - (equal (get-text-property (point) property) - (get-text-property origin property))) - (text-property-search-backward property value predicate) - match))) - (t - (let ((origin (point)) - (ended nil) - pos) - ;; Find the previous candidate. - (while (not ended) - (setq pos (previous-single-property-change (point) property)) - (if (not pos) - (progn - (goto-char origin) - (setq ended t)) - (goto-char (1- pos)) - (if (funcall match-p (get-text-property (point) property)) - (setq ended - (funcall find-end (point))) - ;; Skip past this section of non-matches. - (setq pos (previous-single-property-change (point) property)) - (unless pos - (goto-char origin) - (setq ended t))))) - (and (not (eq ended t)) - ended)))))) - -;;;; Defined in ring.el - -(compat-defun ring-resize (ring size) ;; - "Set the size of RING to SIZE. -If the new size is smaller, then the oldest items in the ring are -discarded." - :feature ring - (when (integerp size) - (let ((length (ring-length ring)) - (new-vec (make-vector size nil))) - (if (= length 0) - (setcdr ring (cons 0 new-vec)) - (let* ((hd (car ring)) - (old-size (ring-size ring)) - (old-vec (cddr ring)) - (copy-length (min size length)) - (copy-hd (mod (+ hd (- length copy-length)) length))) - (setcdr ring (cons copy-length new-vec)) - ;; If the ring is wrapped, the existing elements must be written - ;; out in the right order. - (dotimes (j copy-length) - (aset new-vec j (aref old-vec (mod (+ copy-hd j) old-size)))) - (setcar ring 0)))))) - -;;;; Defined in map-ynp.el - -(compat-version "26.2") - -(compat-defvar read-answer-short 'auto ;; - "If non-nil, the `read-answer' function accepts single-character answers. -If t, accept short (single key-press) answers to the question. -If nil, require long answers. If `auto', accept short answers if -`use-short-answers' is non-nil, or the function cell of `yes-or-no-p' -is set to `y-or-n-p'. - -Note that this variable does not affect calls to the more -commonly-used `yes-or-no-p' function; it only affects calls to -the `read-answer' function. To control whether `yes-or-no-p' -requires a long or a short answer, see the `use-short-answers' -variable.") - -(compat-defun read-answer (question answers) ;; - "Read an answer either as a complete word or its character abbreviation. -Ask user a question and accept an answer from the list of possible answers. - -QUESTION should end in a space; this function adds a list of answers to it. - -ANSWERS is an alist with elements in the following format: - (LONG-ANSWER SHORT-ANSWER HELP-MESSAGE) -where - LONG-ANSWER is a complete answer, - SHORT-ANSWER is an abbreviated one-character answer, - HELP-MESSAGE is a string describing the meaning of the answer. - -SHORT-ANSWER is not necessarily a single character answer. It can be -also a function key like F1, a character event such as C-M-h, or -a control character like C-h. - -Example: - \\='((\"yes\" ?y \"perform the action\") - (\"no\" ?n \"skip to the next\") - (\"all\" ?! \"accept all remaining without more questions\") - (\"help\" ?h \"show help\") - (\"quit\" ?q \"exit\")) - -When `read-answer-short' is non-nil, accept short answers. - -Return a long answer even in case of accepting short ones. - -When `use-dialog-box' is t, pop up a dialog window to get user input." - ;; NOTE: For simplicity we provide a primitive implementation based on - ;; `read-multiple-choice', which does neither support long answers nor the the - ;; gui dialog box. - (cadr (read-multiple-choice - (string-trim-right question) - (delq nil - (mapcar (lambda (x) (unless (equal "help" (car x)) - (list (cadr x) (car x) (caddr x)))) - answers))))) - -(provide 'compat-27) -;;; compat-27.el ends here blob - b5730b11f4b79c9ddfeadbf3a3ca8bd769e8115e (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/compat-28.el +++ /dev/null @@ -1,852 +0,0 @@ -;;; compat-28.el --- Functionality added in Emacs 28.1 -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Functionality added in Emacs 28.1, needed by older Emacs versions. - -;;; Code: - -(eval-when-compile (load "compat-macs.el" nil t t)) -(compat-require compat-27 "27.1") - -(compat-version "28.1") - -;;;; Defined in comp.c - -(compat-defalias native-comp-available-p ignore) ;; - -;;;; Defined in fns.c - -;; FIXME Should handle multibyte regular expressions -(compat-defun string-search (needle haystack &optional start-pos) ;; - "Search for the string NEEDLE in the string HAYSTACK. - -The return value is the position of the first occurrence of -NEEDLE in HAYSTACK, or nil if no match was found. - -The optional START-POS argument says where to start searching in -HAYSTACK and defaults to zero (start at the beginning). -It must be between zero and the length of HAYSTACK, inclusive. - -Case is always significant and text properties are ignored. - -NOTE: Prior to Emacs 27 `string-match' has issues handling -multibyte regular expressions. As the compatibility function -for `string-search' is implemented via `string-match', these -issues are inherited." - (when (and start-pos (or (< (length haystack) start-pos) - (< start-pos 0))) - (signal 'args-out-of-range (list start-pos))) - (let (case-fold-search) - (string-match-p (regexp-quote needle) haystack start-pos))) - -(compat-defun length= (sequence length) ;; [[compat-tests:length=]] - "Returns non-nil if SEQUENCE has a length equal to LENGTH." - (cond - ((null sequence) (zerop length)) - ((consp sequence) - (and (null (nthcdr length sequence)) - (nthcdr (1- length) sequence) - t)) - ((arrayp sequence) - (= (length sequence) length)) - (t (signal 'wrong-type-argument (list 'sequencep sequence))))) - -(compat-defun length< (sequence length) ;; [[compat-tests:length<]] - "Returns non-nil if SEQUENCE is shorter than LENGTH." - (cond - ((null sequence) (not (zerop length))) - ((listp sequence) - (null (nthcdr (1- length) sequence))) - ((arrayp sequence) - (< (length sequence) length)) - (t (signal 'wrong-type-argument (list 'sequencep sequence))))) - -(compat-defun length> (sequence length) ;; [[compat-tests:length>]] - "Returns non-nil if SEQUENCE is longer than LENGTH." - (cond - ((listp sequence) - (and (nthcdr length sequence) t)) - ((arrayp sequence) - (> (length sequence) length)) - (t (signal 'wrong-type-argument (list 'sequencep sequence))))) - -;;;; Defined in fileio.c - -(compat-defun file-name-concat (directory &rest components) ;; - "Append COMPONENTS to DIRECTORY and return the resulting string. -Elements in COMPONENTS must be a string or nil. -DIRECTORY or the non-final elements in COMPONENTS may or may not end -with a slash -- if they don’t end with a slash, a slash will be -inserted before contatenating." - (let ((separator (eval-when-compile - (if (memq system-type '(ms-dos windows-nt cygwin)) - "\\" "/"))) - (components (delq nil - (mapcar (lambda (x) (and (not (equal "" x)) x)) - (cons directory components)))) - (result "")) - (while components - (let ((c (pop components))) - (setq result (concat result c - (and components - (not (string-suffix-p separator c)) - separator))))) - result)) - -;;;; Defined in alloc.c - -(compat-defalias garbage-collect-maybe ignore) ;; - -;;;; Defined in characters.c - -(compat-defun string-width (string &optional from to) ;; - "Handle optional arguments FROM and TO." - :extended t - (let* ((len (length string)) - (from (or from 0)) - (to (or to len))) - (if (and (= from 0) (= to len)) - (string-width string) - (string-width (substring string from to))))) - -;;;; Defined in dired.c - -(compat-defun directory-files (directory &optional full match nosort count) ;; - "Handle additional optional argument COUNT." - :extended t - (let ((files (directory-files directory full match nosort))) - (when (natnump count) - (setf (nthcdr count files) nil)) - files)) - -(compat-defun directory-files-and-attributes (directory &optional full match nosort id-format count) ;; - "Handle additional optional argument COUNT." - :extended t - (let ((files (directory-files-and-attributes directory full match nosort id-format))) - (when (natnump count) - (setf (nthcdr count files) nil)) - files)) - -;;;; xfaces.c - -(compat-defun color-values-from-color-spec (spec) ;; - "Parse color SPEC as a numeric color and return (RED GREEN BLUE). -This function recognises the following formats for SPEC: - - #RGB, where R, G and B are hex numbers of equal length, 1-4 digits each. - rgb:R/G/B, where R, G, and B are hex numbers, 1-4 digits each. - rgbi:R/G/B, where R, G and B are floating-point numbers in [0,1]. - -If SPEC is not in one of the above forms, return nil. - -Each of the 3 integer members of the resulting list, RED, GREEN, -and BLUE, is normalized to have its value in [0,65535]." - (let ((case-fold-search nil)) - (save-match-data - (cond - ((string-match - ;; (rx bos "#" - ;; (or (: (group-n 1 (= 1 hex)) (group-n 2 (= 1 hex)) (group-n 3 (= 1 hex))) - ;; (: (group-n 1 (= 2 hex)) (group-n 2 (= 2 hex)) (group-n 3 (= 2 hex))) - ;; (: (group-n 1 (= 3 hex)) (group-n 2 (= 3 hex)) (group-n 3 (= 3 hex))) - ;; (: (group-n 1 (= 4 hex)) (group-n 2 (= 4 hex)) (group-n 3 (= 4 hex)))) - ;; eos) - "\\`#\\(?:\\(?1:[[:xdigit:]]\\{1\\}\\)\\(?2:[[:xdigit:]]\\{1\\}\\)\\(?3:[[:xdigit:]]\\{1\\}\\)\\|\\(?1:[[:xdigit:]]\\{2\\}\\)\\(?2:[[:xdigit:]]\\{2\\}\\)\\(?3:[[:xdigit:]]\\{2\\}\\)\\|\\(?1:[[:xdigit:]]\\{3\\}\\)\\(?2:[[:xdigit:]]\\{3\\}\\)\\(?3:[[:xdigit:]]\\{3\\}\\)\\|\\(?1:[[:xdigit:]]\\{4\\}\\)\\(?2:[[:xdigit:]]\\{4\\}\\)\\(?3:[[:xdigit:]]\\{4\\}\\)\\)\\'" - spec) - (let ((max (1- (ash 1 (* (- (match-end 1) (match-beginning 1)) 4))))) - (list (/ (* (string-to-number (match-string 1 spec) 16) 65535) max) - (/ (* (string-to-number (match-string 2 spec) 16) 65535) max) - (/ (* (string-to-number (match-string 3 spec) 16) 65535) max)))) - ((string-match - ;; (rx bos "rgb:" - ;; (group (** 1 4 hex)) "/" - ;; (group (** 1 4 hex)) "/" - ;; (group (** 1 4 hex)) - ;; eos) - "\\`rgb:\\([[:xdigit:]]\\{1,4\\}\\)/\\([[:xdigit:]]\\{1,4\\}\\)/\\([[:xdigit:]]\\{1,4\\}\\)\\'" - spec) - (list (/ (* (string-to-number (match-string 1 spec) 16) 65535) - (1- (ash 1 (* (- (match-end 1) (match-beginning 1)) 4)))) - (/ (* (string-to-number (match-string 2 spec) 16) 65535) - (1- (ash 1 (* (- (match-end 2) (match-beginning 2)) 4)))) - (/ (* (string-to-number (match-string 3 spec) 16) 65535) - (1- (ash 1 (* (- (match-end 3) (match-beginning 3)) 4)))))) - ;; The "RGBi" (RGB Intensity) specification is defined by - ;; XCMS[0], see [1] for the implementation in Xlib. - ;; - ;; [0] https://www.nic.funet.fi/pub/X11/X11R4/DOCS/color/Xcms.text - ;; [1] https://gitlab.freedesktop.org/xorg/lib/libx11/-/blob/master/src/xcms/LRGB.c#L1392 - ((string-match - ;; (rx bos "rgbi:" (* space) - ;; (group (? (or "-" "+")) - ;; (or (: (+ digit) (? "." (* digit))) - ;; (: "." (+ digit))) - ;; (? "e" (? (or "-" "+")) (+ digit))) - ;; "/" (* space) - ;; (group (? (or "-" "+")) - ;; (or (: (+ digit) (? "." (* digit))) - ;; (: "." (+ digit))) - ;; (? "e" (? (or "-" "+")) (+ digit))) - ;; "/" (* space) - ;; (group (? (or "-" "+")) - ;; (or (: (+ digit) (? "." (* digit))) - ;; (: "." (+ digit))) - ;; (? "e" (? (or "-" "+")) (+ digit))) - ;; eos) - "\\`rgbi:[[:space:]]*\\([+-]?\\(?:[[:digit:]]+\\(?:\\.[[:digit:]]*\\)?\\|\\.[[:digit:]]+\\)\\(?:e[+-]?[[:digit:]]+\\)?\\)/[[:space:]]*\\([+-]?\\(?:[[:digit:]]+\\(?:\\.[[:digit:]]*\\)?\\|\\.[[:digit:]]+\\)\\(?:e[+-]?[[:digit:]]+\\)?\\)/[[:space:]]*\\([+-]?\\(?:[[:digit:]]+\\(?:\\.[[:digit:]]*\\)?\\|\\.[[:digit:]]+\\)\\(?:e[+-]?[[:digit:]]+\\)?\\)\\'" - spec) - (let ((r (round (* (string-to-number (match-string 1 spec)) 65535))) - (g (round (* (string-to-number (match-string 2 spec)) 65535))) - (b (round (* (string-to-number (match-string 3 spec)) 65535)))) - (when (and (<= 0 r) (<= r 65535) - (<= 0 g) (<= g 65535) - (<= 0 b) (<= b 65535)) - (list r g b)))))))) - -;;;; Defined in simple.el - -(compat-defun make-separator-line (&optional length) ;; - "Make a string appropriate for usage as a visual separator line. -If LENGTH is nil, use the window width." - (if (display-graphic-p) - (if length - (concat (propertize (make-string length ?\s) 'face '(:underline t)) "\n") - (propertize "\n" 'face '(:extend t :height 0.1 :inverse-video t))) - (concat (make-string (or length (1- (window-width))) ?-) "\n"))) - -;;;; Defined in subr.el - -(compat-defun process-lines-handling-status (program status-handler &rest args) ;; - "Execute PROGRAM with ARGS, returning its output as a list of lines. -If STATUS-HANDLER is non-nil, it must be a function with one -argument, which will be called with the exit status of the -program before the output is collected. If STATUS-HANDLER is -nil, an error is signaled if the program returns with a non-zero -exit status." - (with-temp-buffer - (let ((status (apply #'call-process program nil (current-buffer) nil args))) - (if status-handler - (funcall status-handler status) - (unless (eq status 0) - (error "%s exited with status %s" program status))) - (goto-char (point-min)) - (let (lines) - (while (not (eobp)) - (setq lines (cons (buffer-substring-no-properties - (line-beginning-position) - (line-end-position)) - lines)) - (forward-line 1)) - (nreverse lines))))) - -(compat-defun process-lines-ignore-status (program &rest args) ;; - "Execute PROGRAM with ARGS, returning its output as a list of lines. -The exit status of the program is ignored. -Also see `process-lines'." - (apply 'process-lines-handling-status program #'ignore args)) - -;; FIXME Should handle multibyte regular expressions -(compat-defun string-replace (fromstring tostring instring) ;; - "Replace FROMSTRING with TOSTRING in INSTRING each time it occurs." - (when (equal fromstring "") - (signal 'wrong-length-argument '(0))) - (let ((case-fold-search nil)) - (replace-regexp-in-string - (regexp-quote fromstring) - tostring instring - t t))) - -(compat-defun always (&rest _arguments) ;; - "Do nothing and return t. -This function accepts any number of ARGUMENTS, but ignores them. -Also see `ignore'." - t) - -(compat-defun insert-into-buffer (buffer &optional start end) ;; - "Insert the contents of the current buffer into BUFFER. -If START/END, only insert that region from the current buffer. -Point in BUFFER will be placed after the inserted text." - (let ((current (current-buffer))) - (with-current-buffer buffer - (insert-buffer-substring current start end)))) - -(compat-defun replace-string-in-region (string replacement &optional start end) ;; - "Replace STRING with REPLACEMENT in the region from START to END. -The number of replaced occurrences are returned, or nil if STRING -doesn't exist in the region. - -If START is nil, use the current point. If END is nil, use `point-max'. - -Comparisons and replacements are done with fixed case." - (if start - (when (< start (point-min)) - (error "Start before start of buffer")) - (setq start (point))) - (if end - (when (> end (point-max)) - (error "End after end of buffer")) - (setq end (point-max))) - (save-excursion - (goto-char start) - (save-restriction - (narrow-to-region start end) - (let ((matches 0) - (case-fold-search nil)) - (while (search-forward string nil t) - (delete-region (match-beginning 0) (match-end 0)) - (insert replacement) - (setq matches (1+ matches))) - (and (not (zerop matches)) - matches))))) - -(compat-defun replace-regexp-in-region (regexp replacement &optional start end) ;; - "Replace REGEXP with REPLACEMENT in the region from START to END. -The number of replaced occurrences are returned, or nil if REGEXP -doesn't exist in the region. - -If START is nil, use the current point. If END is nil, use `point-max'. - -Comparisons and replacements are done with fixed case. - -REPLACEMENT can use the following special elements: - - `\\&' in NEWTEXT means substitute original matched text. - `\\N' means substitute what matched the Nth `\\(...\\)'. - If Nth parens didn't match, substitute nothing. - `\\\\' means insert one `\\'. - `\\?' is treated literally." - (if start - (when (< start (point-min)) - (error "Start before start of buffer")) - (setq start (point))) - (if end - (when (> end (point-max)) - (error "End after end of buffer")) - (setq end (point-max))) - (save-excursion - (goto-char start) - (save-restriction - (narrow-to-region start end) - (let ((matches 0) - (case-fold-search nil)) - (while (re-search-forward regexp nil t) - (replace-match replacement t) - (setq matches (1+ matches))) - (and (not (zerop matches)) - matches))))) - -(compat-defun buffer-local-boundp (symbol buffer) ;; - "Return non-nil if SYMBOL is bound in BUFFER. -Also see `local-variable-p'." - (condition-case nil - (progn (buffer-local-value symbol buffer) - t) - (void-variable nil))) - -(compat-defmacro with-existing-directory (&rest body) ;; - "Execute BODY with `default-directory' bound to an existing directory. -If `default-directory' is already an existing directory, it's not changed." - (declare (indent 0) (debug t)) - `(let ((default-directory - (or (catch 'quit - (dolist (dir (list default-directory - (expand-file-name "~/") - temporary-file-directory - (getenv "TMPDIR") - "/tmp/")) - (when (and dir (file-exists-p dir)) - (throw 'quit dir)))) - "/"))) - ,@body)) - -(compat-defmacro dlet (binders &rest body) ;; - "Like `let' but using dynamic scoping." - (declare (indent 1) (debug let)) - `(let (_) - ,@(mapcar (lambda (binder) - `(defvar ,(if (consp binder) (car binder) binder))) - binders) - (let ,binders ,@body))) - -(compat-defun ensure-list (object) ;; - "Return OBJECT as a list. -If OBJECT is already a list, return OBJECT itself. If it's -not a list, return a one-element list containing OBJECT." - (if (listp object) - object - (list object))) - -(compat-defalias subr-primitive-p subrp) ;; - -;;;; Defined in data.c - -(compat-defalias subr-native-elisp-p ignore) ;; - -;;;; Defined in subr-x.el - -(compat-defun string-clean-whitespace (string) ;; - "Clean up whitespace in STRING. -All sequences of whitespaces in STRING are collapsed into a -single space character, and leading/trailing whitespace is -removed." - (let ((blank "[[:blank:]\r\n]+")) - (replace-regexp-in-string - "^[[:blank:]\r\n]+\\|[[:blank:]\r\n]+$" - "" - (replace-regexp-in-string - blank " " string)))) - -(compat-defun string-fill (string length) ;; - "Clean up whitespace in STRING. -All sequences of whitespaces in STRING are collapsed into a -single space character, and leading/trailing whitespace is -removed." - (with-temp-buffer - (insert string) - (goto-char (point-min)) - (let ((fill-column length) - (adaptive-fill-mode nil)) - (fill-region (point-min) (point-max))) - (buffer-string))) - -(compat-defun string-pad (string length &optional padding start) ;; - "Pad STRING to LENGTH using PADDING. -If PADDING is nil, the space character is used. If not nil, it -should be a character. - -If STRING is longer than the absolute value of LENGTH, no padding -is done. - -If START is nil (or not present), the padding is done to the end -of the string, and if non-nil, padding is done to the start of -the string." - (unless (natnump length) - (signal 'wrong-type-argument (list 'natnump length))) - (let ((pad-length (- length (length string)))) - (if (< pad-length 0) - string - (concat (and start - (make-string pad-length (or padding ?\s))) - string - (and (not start) - (make-string pad-length (or padding ?\s))))))) - -(compat-defun string-chop-newline (string) ;; - "Remove the final newline (if any) from STRING." - (if (and (>= (length string) 1) (= (aref string (1- (length string))) ?\n)) - (substring string 0 -1) - string)) - -(compat-defmacro named-let (name bindings &rest body) ;; - "Looping construct taken from Scheme. -Like `let', bind variables in BINDINGS and then evaluate BODY, -but with the twist that BODY can evaluate itself recursively by -calling NAME, where the arguments passed to NAME are used -as the new values of the bound variables in the recursive invocation." - (declare (indent 2) (debug (symbolp (&rest (symbolp form)) body))) - (let ((fargs (mapcar (lambda (b) - (let ((var (if (consp b) (car b) b))) - (make-symbol (symbol-name var)))) - bindings)) - (aargs (mapcar (lambda (b) (if (consp b) (cadr b))) bindings)) - rargs) - (dotimes (i (length bindings)) - (let ((b (nth i bindings))) - (push (list (if (consp b) (car b) b) (nth i fargs)) - rargs) - (setf (if (consp b) (car b) b) - (nth i fargs)))) - (letrec - ((quit (make-symbol "quit")) (self (make-symbol "self")) - (total-tco t) - (macro (lambda (&rest args) - (setq total-tco nil) - `(funcall ,self . ,args))) - ;; Based on `cl--self-tco': - (tco-progn (lambda (exprs) - (append - (butlast exprs) - (list (funcall tco (car (last exprs))))))) - (tco (lambda (expr) - (cond - ((eq (car-safe expr) 'if) - (append (list 'if - (cadr expr) - (funcall tco (nth 2 expr))) - (funcall tco-progn (nthcdr 3 expr)))) - ((eq (car-safe expr) 'cond) - (let ((conds (cdr expr)) body) - (while conds - (let ((branch (pop conds))) - (push (cond - ((cdr branch) ;has tail - (funcall tco-progn branch)) - ((null conds) ;last element - (list t (funcall tco (car branch)))) - ((progn - branch))) - body))) - (cons 'cond (nreverse body)))) - ((eq (car-safe expr) 'or) - (if (cddr expr) - (let ((var (make-symbol "var"))) - `(let ((,var ,(cadr expr))) - (if ,var ,(funcall tco var) - ,(funcall tco (cons 'or (cddr expr)))))) - (funcall tco (cadr expr)))) - ((eq (car-safe expr) 'condition-case) - (append (list 'condition-case (cadr expr) (nth 2 expr)) - (mapcar - (lambda (handler) - (cons (car handler) - (funcall tco-progn (cdr handler)))) - (nthcdr 3 expr)))) - ((memq (car-safe expr) '(and progn)) - (cons (car expr) (funcall tco-progn (cdr expr)))) - ((memq (car-safe expr) '(let let*)) - (append (list (car expr) (cadr expr)) - (funcall tco-progn (cddr expr)))) - ((eq (car-safe expr) name) - (let (sets (args (cdr expr))) - (dolist (farg fargs) - (push (list farg (pop args)) - sets)) - (cons 'setq (apply #'nconc (nreverse sets))))) - (`(throw ',quit ,expr)))))) - (when-let ((tco-body (funcall tco (macroexpand-all (macroexp-progn body))))) - (setq body `((catch ',quit - (while t (let ,rargs ,@(macroexp-unprogn tco-body))))))) - (let ((expand (macroexpand-all (macroexp-progn body) (list (cons name macro))))) - (if total-tco - `(let ,bindings ,expand) - `(funcall - (letrec ((,self (lambda ,fargs ,expand))) ,self) - ,@aargs)))))) - -;;;; Defined in files.el - -(compat-defun file-name-with-extension (filename extension) ;; - "Set the EXTENSION of a FILENAME. -The extension (in a file name) is the part that begins with the last \".\". - -Trims a leading dot from the EXTENSION so that either \"foo\" or -\".foo\" can be given. - -Errors if the FILENAME or EXTENSION are empty, or if the given -FILENAME has the format of a directory. - -See also `file-name-sans-extension'." - (let ((extn (string-remove-prefix "." extension))) - (cond - ((string= filename "") - (error "Empty filename")) - ((string= extn "") - (error "Malformed extension: %s" extension)) - ((directory-name-p filename) - (error "Filename is a directory: %s" filename)) - (t - (concat (file-name-sans-extension filename) "." extn))))) - -(compat-defun directory-empty-p (dir) ;; - "Return t if DIR names an existing directory containing no other files. -Return nil if DIR does not name a directory, or if there was -trouble determining whether DIR is a directory or empty. - -Symbolic links to directories count as directories. -See `file-symlink-p' to distinguish symlinks." - (and (file-directory-p dir) - (null (directory-files dir nil directory-files-no-dot-files-regexp t)))) - -(compat-defun file-modes-number-to-symbolic (mode &optional filetype) ;; - "Return a string describing a file's MODE. -For instance, if MODE is #o700, then it produces `-rwx------'. -FILETYPE if provided should be a character denoting the type of file, -such as `?d' for a directory, or `?l' for a symbolic link and will override -the leading `-' char." - (string - (or filetype - (pcase (ash mode -12) - ;; POSIX specifies that the file type is included in st_mode - ;; and provides names for the file types but values only for - ;; the permissions (e.g., S_IWOTH=2). - - ;; (#o017 ??) ;; #define S_IFMT 00170000 - (#o014 ?s) ;; #define S_IFSOCK 0140000 - (#o012 ?l) ;; #define S_IFLNK 0120000 - ;; (8 ??) ;; #define S_IFREG 0100000 - (#o006 ?b) ;; #define S_IFBLK 0060000 - (#o004 ?d) ;; #define S_IFDIR 0040000 - (#o002 ?c) ;; #define S_IFCHR 0020000 - (#o001 ?p) ;; #define S_IFIFO 0010000 - (_ ?-))) - (if (zerop (logand 256 mode)) ?- ?r) - (if (zerop (logand 128 mode)) ?- ?w) - (if (zerop (logand 64 mode)) - (if (zerop (logand 2048 mode)) ?- ?S) - (if (zerop (logand 2048 mode)) ?x ?s)) - (if (zerop (logand 32 mode)) ?- ?r) - (if (zerop (logand 16 mode)) ?- ?w) - (if (zerop (logand 8 mode)) - (if (zerop (logand 1024 mode)) ?- ?S) - (if (zerop (logand 1024 mode)) ?x ?s)) - (if (zerop (logand 4 mode)) ?- ?r) - (if (zerop (logand 2 mode)) ?- ?w) - (if (zerop (logand 512 mode)) - (if (zerop (logand 1 mode)) ?- ?x) - (if (zerop (logand 1 mode)) ?T ?t)))) - -(compat-defun file-backup-file-names (filename) ;; - "Return a list of backup files for FILENAME. -The list will be sorted by modification time so that the most -recent files are first." - ;; `make-backup-file-name' will get us the right directory for - ;; ordinary or numeric backups. It might create a directory for - ;; backups as a side-effect, according to `backup-directory-alist'. - (let* ((filename (file-name-sans-versions - (make-backup-file-name (expand-file-name filename)))) - (dir (file-name-directory filename)) - files) - (dolist (file (file-name-all-completions - (file-name-nondirectory filename) dir)) - (let ((candidate (concat dir file))) - (when (and (backup-file-name-p candidate) - (string= (file-name-sans-versions candidate) filename)) - (push candidate files)))) - (sort files #'file-newer-than-file-p))) - -(compat-defun make-lock-file-name (filename) ;; - "Make a lock file name for FILENAME. -This prepends \".#\" to the non-directory part of FILENAME, and -doesn't respect `lock-file-name-transforms', as Emacs 28.1 and -onwards does." - (expand-file-name - (concat - ".#" (file-name-nondirectory filename)) - (file-name-directory filename))) - -;;;; Defined in minibuffer.el - -(compat-defun format-prompt (prompt default &rest format-args) ;; - "Format PROMPT with DEFAULT. -If FORMAT-ARGS is nil, PROMPT is used as a plain string. If -FORMAT-ARGS is non-nil, PROMPT is used as a format control -string, and FORMAT-ARGS are the arguments to be substituted into -it. See `format' for details. - -If DEFAULT is a list, the first element is used as the default. -If not, the element is used as is. - -If DEFAULT is nil or an empty string, no \"default value\" string -is included in the return value." - (concat - (if (null format-args) - prompt - (apply #'format prompt format-args)) - (and default - (or (not (stringp default)) - (> (length default) 0)) - (format " (default %s)" - (if (consp default) - (car default) - default))) - ": ")) - -;;;; Defined in faces.el - -(compat-defvar color-luminance-dark-limit 0.325 ;; - "The relative luminance below which a color is considered \"dark\". -A \"dark\" color in this sense provides better contrast with white -than with black; see `color-dark-p'. -This value was determined experimentally." - :constant t) - -(compat-defun color-dark-p (rgb) ;; - "Whether RGB is more readable against white than black. -RGB is a 3-element list (R G B), each component in the range [0,1]. -This predicate can be used both for determining a suitable (black or white) -contrast color with RGB as background and as foreground." - (unless (<= 0 (apply #'min rgb) (apply #'max rgb) 1) - (error "RGB components %S not in [0,1]" rgb)) - ;; Compute the relative luminance after gamma-correcting (assuming sRGB), - ;; and compare to a cut-off value determined experimentally. - ;; See https://en.wikipedia.org/wiki/Relative_luminance for details. - (let* ((sr (nth 0 rgb)) - (sg (nth 1 rgb)) - (sb (nth 2 rgb)) - ;; Gamma-correct the RGB components to linear values. - ;; Use the power 2.2 as an approximation to sRGB gamma; - ;; it should be good enough for the purpose of this function. - (r (expt sr 2.2)) - (g (expt sg 2.2)) - (b (expt sb 2.2)) - (y (+ (* r 0.2126) (* g 0.7152) (* b 0.0722)))) - (< y color-luminance-dark-limit))) - -;;;; Defined in window.el - -(compat-defmacro with-window-non-dedicated (window &rest body) ;; - "Evaluate BODY with WINDOW temporarily made non-dedicated. -If WINDOW is nil, use the selected window. Return the value of -the last form in BODY." - (declare (indent 1) (debug t)) - (let ((window-dedicated-sym (gensym)) - (window-sym (gensym))) - `(let* ((,window-sym (window-normalize-window ,window t)) - (,window-dedicated-sym (window-dedicated-p ,window-sym))) - (set-window-dedicated-p ,window-sym nil) - (unwind-protect - (progn ,@body) - (set-window-dedicated-p ,window-sym ,window-dedicated-sym))))) - -(compat-defun count-windows (&optional minibuf all-frames) ;; - "Handle optional argument ALL-FRAMES." - :extended t - (if all-frames - (let ((sum 0)) - (dolist (frame (frame-list)) - (with-selected-frame frame - (setq sum (+ (count-windows minibuf) sum)))) - sum) - (count-windows minibuf))) - -;;;; Defined in thingatpt.el - -(compat-defun thing-at-mouse (event thing &optional no-properties) ;; - "Return the THING at mouse click. -Like `thing-at-point', but tries to use the event -where the mouse button is clicked to find a thing nearby." - ;; No :feature specified, since the function is autoloaded. - (save-excursion - (mouse-set-point event) - (thing-at-point thing no-properties))) - -(compat-defun bounds-of-thing-at-mouse (event thing) ;; - "Determine start and end locations for THING at mouse click given by EVENT. -Like `bounds-of-thing-at-point', but tries to use the position in EVENT -where the mouse button is clicked to find the thing nearby." - ;; No :feature specified, since the function is autoloaded. - (save-excursion - (mouse-set-point event) - (bounds-of-thing-at-point thing))) - -;;;; Defined in mouse.el - -(compat-defun mark-thing-at-mouse (click thing) ;; - "Activate the region around THING found near the mouse CLICK." - (when-let ((bounds (bounds-of-thing-at-mouse click thing))) - (goto-char (if mouse-select-region-move-to-beginning - (car bounds) (cdr bounds))) - (push-mark (if mouse-select-region-move-to-beginning - (cdr bounds) (car bounds)) - t 'activate))) - -;;;; Defined in macroexp.el - -(compat-defun macroexp-warn-and-return (msg form &optional _category _compile-only _arg) ;; - "Return code equivalent to FORM labeled with warning MSG. -CATEGORY is the category of the warning, like the categories that -can appear in `byte-compile-warnings'. -COMPILE-ONLY non-nil means no warning should be emitted if the code -is executed without being compiled first. -ARG is a symbol (or a form) giving the source code position for the message. -It should normally be a symbol with position and it defaults to FORM." - (macroexp--warn-and-return msg form)) - -(compat-defun macroexp-file-name () ;; - "Return the name of the file from which the code comes. -Returns nil when we do not know. -A non-nil result is expected to be reliable when called from a macro in order -to find the file in which the macro's call was found, and it should be -reliable as well when used at the top-level of a file. -Other uses risk returning non-nil value that point to the wrong file." - (let ((file (car (last current-load-list)))) - (or (if (stringp file) file) - (bound-and-true-p byte-compile-current-file)))) - -;;;; Defined in env.el - -(compat-defmacro with-environment-variables (variables &rest body) ;; - "Set VARIABLES in the environment and execute BODY. -VARIABLES is a list of variable settings of the form (VAR VALUE), -where VAR is the name of the variable (a string) and VALUE -is its value (also a string). - -The previous values will be be restored upon exit." - (declare (indent 1) (debug (sexp body))) - (unless (consp variables) - (error "Invalid VARIABLES: %s" variables)) - `(let ((process-environment (copy-sequence process-environment))) - ,@(mapcar (lambda (elem) - `(setenv ,(car elem) ,(cadr elem))) - variables) - ,@body)) - -;;;; Defined in time-data.el - -(compat-defun decoded-time-period (time) ;; - "Interpret DECODED as a period and return its length in seconds. -For computational purposes, years are 365 days long and months -are 30 days long." - :feature time-date - (+ (if (consp (decoded-time-second time)) - (/ (float (car (decoded-time-second time))) - (cdr (decoded-time-second time))) - (or (decoded-time-second time) 0)) - (* (or (decoded-time-minute time) 0) 60) - (* (or (decoded-time-hour time) 0) 60 60) - (* (or (decoded-time-day time) 0) 60 60 24) - (* (or (decoded-time-month time) 0) 60 60 24 30) - (* (or (decoded-time-year time) 0) 60 60 24 365))) - -;;;; Defined in doc.c - -(compat-defun text-quoting-style () ;; - "Return the current effective text quoting style. -If the variable `text-quoting-style' is `grave', `straight' or -`curve', just return that value. If it is nil (the default), return -`grave' if curved quotes cannot be displayed (for instance, on a -terminal with no support for these characters), otherwise return -`quote'. Any other value is treated as `grave'. - -Note that in contrast to the variable `text-quoting-style', this -function will never return nil." - (cond - ((memq text-quoting-style '(grave straight curve)) - text-quoting-style) - ((not text-quoting-style) 'grave) - (t 'curve))) - -;;;; Defined in button.el - -;; Obsolete Alias since 29 -(compat-defalias button-buttonize buttonize :obsolete t) ;; - -;;;; Defined in wid-edit.el - -(compat-guard t ;; - :feature wid-edit - (define-widget 'natnum 'restricted-sexp - "A nonnegative integer." - :tag "Integer (positive)" - :value 0 - :type-error "This field should contain a nonnegative integer" - :match-alternatives '(natnump))) - -(provide 'compat-28) -;;; compat-28.el ends here blob - 5b0a4dfbb3a82bb0c2a2bbd3ab95e29707e6d3ed (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/compat-29.el +++ /dev/null @@ -1,1599 +0,0 @@ -;;; compat-29.el --- Functionality added in Emacs 29.1 -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Functionality added in Emacs 29.1, needed by older Emacs versions. - -;;; Code: - -(eval-when-compile (load "compat-macs.el" nil t t)) -(compat-require compat-28 "28.1") - -;; Preloaded in loadup.el -(compat-require seq "29.1") ;; - -(compat-version "29.1") - -;;;; Defined in startup.el - -(compat-defvar lisp-directory ;; - (file-truename - (file-name-directory - (locate-file "simple" load-path (get-load-suffixes)))) - "Directory where Emacs's own *.el and *.elc Lisp files are installed.") - -;;;; Defined in window.c - -(compat-defalias window-configuration-equal-p compare-window-configurations) ;; - -;;;; Defined in xdisp.c - -(compat-defun get-display-property (position prop &optional object properties) ;; - "Get the value of the `display' property PROP at POSITION. -If OBJECT, this should be a buffer or string where the property is -fetched from. If omitted, OBJECT defaults to the current buffer. - -If PROPERTIES, look for value of PROP in PROPERTIES instead of -the properties at POSITION." - (if properties - (unless (listp properties) - (signal 'wrong-type-argument (list 'listp properties))) - (setq properties (get-text-property position 'display object))) - (cond - ((vectorp properties) - (catch 'found - (dotimes (i (length properties)) - (let ((ent (aref properties i))) - (when (eq (car ent) prop) - (throw 'found (cadr ent ))))))) - ((consp (car properties)) - (condition-case nil - (cadr (assq prop properties)) - ;; Silently handle improper lists: - (wrong-type-argument nil))) - ((and (consp (cdr properties)) - (eq (car properties) prop)) - (cadr properties)))) - -;;;; Defined in fns.c - -(compat-defun ntake (n list) ;; - "Modify LIST to keep only the first N elements. -If N is zero or negative, return nil. -If N is greater or equal to the length of LIST, return LIST unmodified. -Otherwise, return LIST after truncating it." - (and (> n 0) (let ((cons (nthcdr (1- n) list))) - (when cons (setcdr cons nil)) - list))) - -(compat-defun take (n list) ;; - "Return the first N elements of LIST. -If N is zero or negative, return nil. -If N is greater or equal to the length of LIST, return LIST (or a copy)." - (declare (pure t) (side-effect-free t)) - (let (copy) - (while (and (< 0 n) list) - (push (pop list) copy) - (setq n (1- n))) - (nreverse copy))) - -(compat-defun string-equal-ignore-case (string1 string2) ;; - "Like `string-equal', but case-insensitive. -Upper-case and lower-case letters are treated as equal. -Unibyte strings are converted to multibyte for comparison." - (declare (pure t) (side-effect-free t)) - (eq t (compare-strings string1 0 nil string2 0 nil t))) - -(compat-defun plist-get (plist prop &optional predicate) ;; - "Handle optional argument PREDICATE." - :extended t - (pcase predicate - ((or `nil `eq) (plist-get plist prop)) - (`equal (lax-plist-get plist prop)) - (_ (catch 'found - (while (consp plist) - (when (funcall predicate prop (car plist)) - (throw 'found (cadr plist))) - (setq plist (cddr plist))))))) - -(compat-defun plist-put (plist prop val &optional predicate) ;; - "Handle optional argument PREDICATE." - :extended t - (pcase predicate - ((or `nil `eq) (plist-put plist prop val)) - (`equal (lax-plist-put plist prop val)) - (_ (catch 'found - (let ((tail plist)) - (while (consp tail) - (when (funcall predicate prop (car tail)) - (setcar (cdr tail) val) - (throw 'found plist)) - (setq tail (cddr tail)))) - (nconc plist (list prop val)))))) - -(compat-defun plist-member (plist prop &optional predicate) ;; - "Handle optional argument PREDICATE." - :extended t - (pcase predicate - ((or `nil `eq) (plist-member plist prop)) - (_ (catch 'found - (while (consp plist) - (when (funcall predicate prop (car plist)) - (throw 'found plist)) - (setq plist (cddr plist))))))) - -;;;; Defined in gv.el - -(compat-guard t ;; - (gv-define-expander compat--plist-get - (lambda (do plist prop &optional predicate) - (macroexp-let2 macroexp-copyable-p key prop - (gv-letplace (getter setter) plist - (macroexp-let2 nil p `(cdr (compat--plist-member ,getter ,key ,predicate)) - (funcall do - `(car ,p) - (lambda (val) - `(if ,p - (setcar ,p ,val) - ,(funcall setter - `(cons ,key (cons ,val ,getter))))))))))) - (unless (get 'plist-get 'gv-expander) - (put 'plist-get 'gv-expander (get 'compat--plist-get 'gv-expander)))) - -;;;; Defined in editfns.c - -(compat-defun pos-bol (&optional n) ;; - "Return the position of the first character on the current line. -With optional argument N, scan forward N - 1 lines first. -If the scan reaches the end of the buffer, return that position. - -This function ignores text display directionality; it returns the -position of the first character in logical order, i.e. the smallest -character position on the logical line. See `vertical-motion' for -movement by screen lines. - -This function does not move point. Also see `line-beginning-position'." - (declare (side-effect-free t)) - (let ((inhibit-field-text-motion t)) - (line-beginning-position n))) - -(compat-defun pos-eol (&optional n) ;; - "Return the position of the last character on the current line. -With argument N not nil or 1, move forward N - 1 lines first. -If scan reaches end of buffer, return that position. - -This function ignores text display directionality; it returns the -position of the last character in logical order, i.e. the largest -character position on the line. - -This function does not move point. Also see `line-end-position'." - (declare (side-effect-free t)) - (let ((inhibit-field-text-motion t)) - (line-end-position n))) - -;;;; Defined in subr.el - -(compat-defmacro with-delayed-message (_args &rest body) ;; - "Like `progn', but display MESSAGE if BODY takes longer than TIMEOUT seconds. -The MESSAGE form will be evaluated immediately, but the resulting -string will be displayed only if BODY takes longer than TIMEOUT seconds. - -NOTE: The compatibility function never displays the message, -which is not problematic since the only effect of the function is -to display a progress message to the user. Backporting this -feature is not possible, since the implementation is directly -baked into the Elisp interpreter. - -\(fn (timeout message) &rest body)" - (declare (indent 1)) - (macroexp-progn body)) - -(compat-defun funcall-with-delayed-message (timeout message function) ;; - "Like `funcall', but display MESSAGE if FUNCTION takes longer than TIMEOUT. -TIMEOUT is a number of seconds, and can be an integer or a -floating point number. If FUNCTION takes less time to execute -than TIMEOUT seconds, MESSAGE is not displayed. - -NOTE: The compatibility function never displays the message, -which is not problematic since the only effect of the function is -to display a progress message to the user. Backporting this -feature is not possible, since the implementation is directly -baked into the Elisp interpreter." - (ignore timeout message) - (funcall function)) - -(compat-defun string-lines (string &optional omit-nulls keep-newlines) ;; - "Handle additional KEEP-NEWLINES argument." - :extended "28.1" - (if (equal string "") - (if omit-nulls - nil - (list "")) - (let ((lines nil) - (start 0)) - (while (< start (length string)) - (let ((newline (string-search "\n" string start))) - (if newline - (progn - (when (or (not omit-nulls) - (not (= start newline))) - (let ((line (substring string start - (if keep-newlines - (1+ newline) - newline)))) - (when (not (and keep-newlines omit-nulls - (equal line "\n"))) - (push line lines)))) - (setq start (1+ newline))) - (if (zerop start) - (push string lines) - (push (substring string start) lines)) - (setq start (length string))))) - (nreverse lines)))) - -(compat-defun readablep (object) ;; - "Say whether OBJECT has a readable syntax. -This means that OBJECT can be printed out and then read back -again by the Lisp reader. This function returns nil if OBJECT is -unreadable, and the printed representation (from `prin1') of -OBJECT if it is readable." - (declare (side-effect-free error-free)) - (ignore-errors (equal object (read (prin1-to-string object))))) - -(compat-defun buffer-local-restore-state (states) ;; - "Restore values of buffer-local variables recorded in STATES. -STATES should be an object returned by `buffer-local-set-state'." - (dolist (state states) - (if (cadr state) - (set (car state) (caddr state)) - (kill-local-variable (car state))))) - -(compat-defun buffer-local-set-state--get (pairs) ;; - "Internal helper function." - (let ((states nil)) - (while pairs - (push (list (car pairs) - (and (boundp (car pairs)) - (local-variable-p (car pairs))) - (and (boundp (car pairs)) - (symbol-value (car pairs)))) - states) - (setq pairs (cddr pairs))) - (nreverse states))) - -(compat-defmacro buffer-local-set-state (&rest pairs) ;; - "Like `setq-local', but allow restoring the previous state of locals later. -This macro returns an object that can be passed to `buffer-local-restore-state' -in order to restore the state of the local variables set via this macro. - -\(fn [VARIABLE VALUE]...)" - (declare (debug setq)) - (unless (zerop (mod (length pairs) 2)) - (error "PAIRS must have an even number of variable/value members")) - `(prog1 - (buffer-local-set-state--get ',pairs) - (,(if (fboundp 'compat--setq-local) 'compat--setq-local 'setq-local) - ,@pairs))) - -(compat-defun list-of-strings-p (object) ;; - "Return t if OBJECT is nil or a list of strings." - (declare (pure t) (side-effect-free t)) - (while (and (consp object) (stringp (car object))) - (setq object (cdr object))) - (null object)) - -(compat-defun plistp (object) ;; - "Non-nil if and only if OBJECT is a valid plist." - (let ((len (proper-list-p object))) - (and len (zerop (% len 2))))) - -(compat-defun delete-line () ;; - "Delete the current line." - (delete-region (pos-bol) (pos-bol 2))) - -(compat-defmacro with-restriction (start end &rest rest) ;; - "Execute BODY with restrictions set to START and END. - -The current restrictions, if any, are restored upon return. - -When the optional :label LABEL argument is present, in which -LABEL is a symbol, inside BODY, `narrow-to-region' and `widen' -can be used only within the START and END limits. To gain access -to other portions of the buffer, use `without-restriction' with the -same LABEL argument. - -\(fn START END [:label LABEL] BODY)" - (declare (indent 0) (debug t)) - `(save-restriction - (narrow-to-region ,start ,end) - ;; Locking is ignored - ,@(if (eq (car rest) :label) (cddr rest) rest))) - -(compat-defmacro without-restriction (&rest rest) ;; - "Execute BODY without restrictions. - -The current restrictions, if any, are restored upon return. - -When the optional :label LABEL argument is present, the -restrictions set by `with-restriction' with the same LABEL argument -are lifted. - -\(fn [:label LABEL] BODY)" - (declare (indent 0) (debug t)) - `(save-restriction - (widen) - ;; Locking is ignored - ,@(if (eq (car rest) :label) (cddr rest) rest))) - -(compat-defmacro with-memoization (place &rest code) ;; - "Return the value of CODE and stash it in PLACE. -If PLACE's value is non-nil, then don't bother evaluating CODE -and return the value found in PLACE instead." - (declare (indent 1)) - (gv-letplace (getter setter) place - `(or ,getter - ,(macroexp-let2 nil val (macroexp-progn code) - `(progn - ,(funcall setter val) - ,val))))) - -(compat-defalias string-split split-string) ;; - -(compat-defun compiled-function-p (object) ;; - "Return non-nil if OBJECT is a function that has been compiled. -Does not distinguish between functions implemented in machine code -or byte-code." - (or (subrp object) (byte-code-function-p object))) - -(compat-defun function-alias-p (func &optional noerror) ;; - "Return nil if FUNC is not a function alias. -If FUNC is a function alias, return the function alias chain. - -If the function alias chain contains loops, an error will be -signalled. If NOERROR, the non-loop parts of the chain is returned." - (declare (side-effect-free t)) - (let ((chain nil) - (orig-func func)) - (nreverse - (catch 'loop - (while (and (symbolp func) - (setq func (symbol-function func)) - (symbolp func)) - (when (or (memq func chain) - (eq func orig-func)) - (if noerror - (throw 'loop chain) - (signal 'cyclic-function-indirection (list orig-func)))) - (push func chain)) - chain)))) - -(compat-defun buffer-match-p (condition buffer-or-name &optional arg) ;; - "Return non-nil if BUFFER-OR-NAME matches CONDITION. -CONDITION is either: -- the symbol t, to always match, -- the symbol nil, which never matches, -- a regular expression, to match a buffer name, -- a predicate function that takes a buffer object and ARG as - arguments, and returns non-nil if the buffer matches, -- a cons-cell, where the car describes how to interpret the cdr. - The car can be one of the following: - * `derived-mode': the buffer matches if the buffer's major mode - is derived from the major mode in the cons-cell's cdr. - * `major-mode': the buffer matches if the buffer's major mode - is eq to the cons-cell's cdr. Prefer using `derived-mode' - instead when both can work. - * `not': the cadr is interpreted as a negation of a condition. - * `and': the cdr is a list of recursive conditions, that all have - to be met. - * `or': the cdr is a list of recursive condition, of which at - least one has to be met." - (letrec - ((buffer (get-buffer buffer-or-name)) - (match - (lambda (conditions) - (catch 'match - (dolist (condition conditions) - (when (cond - ((eq condition t)) - ((stringp condition) - (string-match-p condition (buffer-name buffer))) - ((functionp condition) - (condition-case nil - (funcall condition buffer) - (wrong-number-of-arguments - (funcall condition buffer arg)))) - ((eq (car-safe condition) 'major-mode) - (eq - (buffer-local-value 'major-mode buffer) - (cdr condition))) - ((eq (car-safe condition) 'derived-mode) - (provided-mode-derived-p - (buffer-local-value 'major-mode buffer) - (cdr condition))) - ((eq (car-safe condition) 'not) - (not (funcall match (cdr condition)))) - ((eq (car-safe condition) 'or) - (funcall match (cdr condition))) - ((eq (car-safe condition) 'and) - (catch 'fail - (dolist (c (cdr condition)) - (unless (funcall match (list c)) - (throw 'fail nil))) - t))) - (throw 'match t))))))) - (funcall match (list condition)))) - -(compat-defun match-buffers (condition &optional buffers arg) ;; - "Return a list of buffers that match CONDITION. -See `buffer-match' for details on CONDITION. By default all -buffers are checked, this can be restricted by passing an -optional argument BUFFERS, set to a list of buffers to check. -ARG is passed to `buffer-match', for predicate conditions in -CONDITION." - (let (bufs) - (dolist (buf (or buffers (buffer-list))) - (when (buffer-match-p condition (get-buffer buf) arg) - (push buf bufs))) - bufs)) - -(compat-defvar set-transient-map-timeout nil ;; - "Timeout in seconds for deactivation of a transient keymap. -If this is a number, it specifies the amount of idle time -after which to deactivate the keymap set by `set-transient-map', -thus overriding the value of the TIMEOUT argument to that function.") - -(compat-defvar set-transient-map-timer nil ;; - "Timer for `set-transient-map-timeout'.") - -(declare-function format-spec "format-spec") -(compat-defun set-transient-map (map &optional keep-pred on-exit message timeout) ;; - "Handle the optional arguments MESSAGE and TIMEOUT." - :extended t - (unless (fboundp 'format-spec) - (require 'format-spec)) - (let* ((timeout (or set-transient-map-timeout timeout)) - (message - (when message - (let (keys) - (map-keymap (lambda (key cmd) (and cmd (push key keys))) map) - (format-spec (if (stringp message) message "Repeat with %k") - `((?k . ,(mapconcat - (lambda (key) - (substitute-command-keys - (format "\\`%s'" - (key-description (vector key))))) - keys ", "))))))) - (clearfun (make-symbol "clear-transient-map")) - (exitfun - (lambda () - (internal-pop-keymap map 'overriding-terminal-local-map) - (remove-hook 'pre-command-hook clearfun) - (when message (message "")) - (when set-transient-map-timer (cancel-timer set-transient-map-timer)) - (when on-exit (funcall on-exit))))) - (fset clearfun - (lambda () - (with-demoted-errors "set-transient-map PCH: %S" - (if (cond - ((null keep-pred) nil) - ((and (not (eq map (cadr overriding-terminal-local-map))) - (memq map (cddr overriding-terminal-local-map))) - t) - ((eq t keep-pred) - (let ((mc (lookup-key map (this-command-keys-vector)))) - (when (and mc (symbolp mc)) - (setq mc (or (command-remapping mc) mc))) - (and mc (eq this-command mc)))) - (t (funcall keep-pred))) - (when message (message "%s" message)) - (funcall exitfun))))) - (add-hook 'pre-command-hook clearfun) - (internal-push-keymap map 'overriding-terminal-local-map) - (when timeout - (when set-transient-map-timer (cancel-timer set-transient-map-timer)) - (setq set-transient-map-timer (run-with-idle-timer timeout nil exitfun))) - (when message (message "%s" message)) - exitfun)) - -;;;; Defined in simple.el - -(compat-defun char-uppercase-p (char) ;; - "Return non-nil if CHAR is an upper-case character. -If the Unicode tables are not yet available, e.g. during bootstrap, -then gives correct answers only for ASCII characters." - (cond ((unicode-property-table-internal 'lowercase) - (characterp (get-char-code-property char 'lowercase))) - ((and (>= char ?A) (<= char ?Z))))) - -(compat-defun use-region-noncontiguous-p () ;; - "Return non-nil for a non-contiguous region if `use-region-p'." - (and (use-region-p) (region-noncontiguous-p))) - -(compat-defun use-region-beginning () ;; - "Return the start of the region if `use-region-p'." - (and (use-region-p) (region-beginning))) - -(compat-defun use-region-end () ;; - "Return the end of the region if `use-region-p'." - (and (use-region-p) (region-end))) - -(compat-defun get-scratch-buffer-create () ;; - "Return the *scratch* buffer, creating a new one if needed." - (or (get-buffer "*scratch*") - (let ((scratch (get-buffer-create "*scratch*"))) - ;; Don't touch the buffer contents or mode unless we know that - ;; we just created it. - (with-current-buffer scratch - (when initial-scratch-message - (insert (substitute-command-keys initial-scratch-message)) - (set-buffer-modified-p nil)) - (funcall initial-major-mode)) - scratch))) - -;;;; Defined in subr-x.el - -(compat-defmacro with-buffer-unmodified-if-unchanged (&rest body) ;; - "Like `progn', but change buffer-modified status only if buffer text changes. -If the buffer was unmodified before execution of BODY, and -buffer text after execution of BODY is identical to what it was -before, ensure that buffer is still marked unmodified afterwards. -For example, the following won't change the buffer's modification -status: - - (with-buffer-unmodified-if-unchanged - (insert \"a\") - (delete-char -1)) - -Note that only changes in the raw byte sequence of the buffer text, -as stored in the internal representation, are monitored for the -purpose of detecting the lack of changes in buffer text. Any other -changes that are normally perceived as \"buffer modifications\", such -as changes in text properties, `buffer-file-coding-system', buffer -multibyteness, etc. -- will not be noticed, and the buffer will still -be marked unmodified, effectively ignoring those changes." - (declare (debug t) (indent 0)) - (let ((hash (gensym)) - (buffer (gensym))) - `(let ((,hash (and (not (buffer-modified-p)) - (buffer-hash))) - (,buffer (current-buffer))) - (prog1 - (progn - ,@body) - ;; If we didn't change anything in the buffer (and the buffer - ;; was previously unmodified), then flip the modification status - ;; back to "unchanged". - (when (and ,hash (buffer-live-p ,buffer)) - (with-current-buffer ,buffer - (when (and (buffer-modified-p) - (equal ,hash (buffer-hash))) - (restore-buffer-modified-p nil)))))))) - -(compat-defun add-display-text-property (start end prop value ;; - &optional object) - "Add display property PROP with VALUE to the text from START to END. -If any text in the region has a non-nil `display' property, those -properties are retained. - -If OBJECT is non-nil, it should be a string or a buffer. If nil, -this defaults to the current buffer." - (let ((sub-start start) - (sub-end 0) - disp) - (while (< sub-end end) - (setq sub-end (next-single-property-change sub-start 'display object - (if (stringp object) - (min (length object) end) - (min end (point-max))))) - (if (not (setq disp (get-text-property sub-start 'display object))) - ;; No old properties in this range. - (put-text-property sub-start sub-end 'display (list prop value) - object) - ;; We have old properties. - (let ((vector nil)) - ;; Make disp into a list. - (setq disp - (cond - ((vectorp disp) - (setq vector t) - (append disp nil)) - ((not (consp (car disp))) - (list disp)) - (t - disp))) - ;; Remove any old instances. - (when-let ((old (assoc prop disp))) - (setq disp (delete old disp))) - (setq disp (cons (list prop value) disp)) - (when vector - (setq disp (vconcat disp))) - ;; Finally update the range. - (put-text-property sub-start sub-end 'display disp object))) - (setq sub-start sub-end)))) - -(compat-defmacro while-let (spec &rest body) ;; - "Bind variables according to SPEC and conditionally evaluate BODY. -Evaluate each binding in turn, stopping if a binding value is nil. -If all bindings are non-nil, eval BODY and repeat. - -The variable list SPEC is the same as in `if-let*'." - (declare (indent 1) (debug if-let)) - (let ((done (gensym "done"))) - `(catch ',done - (while t - (if-let* ,spec - (progn - ,@body) - (throw ',done nil)))))) - -;;;; Defined in files.el - -(compat-defun directory-abbrev-make-regexp (directory) ;; - "Create a regexp to match DIRECTORY for `directory-abbrev-alist'." - (let ((regexp - ;; We include a slash at the end, to avoid spurious - ;; matches such as `/usr/foobar' when the home dir is - ;; `/usr/foo'. - (concat "\\`" (regexp-quote directory) "\\(/\\|\\'\\)"))) - ;; The value of regexp could be multibyte or unibyte. In the - ;; latter case, we need to decode it. - (if (multibyte-string-p regexp) - regexp - (decode-coding-string regexp - (if (eq system-type 'windows-nt) - 'utf-8 - locale-coding-system))))) - -(compat-defun directory-abbrev-apply (filename) ;; - "Apply the abbreviations in `directory-abbrev-alist' to FILENAME. -Note that when calling this, you should set `case-fold-search' as -appropriate for the filesystem used for FILENAME." - (dolist (dir-abbrev directory-abbrev-alist filename) - (when (string-match (car dir-abbrev) filename) - (setq filename (concat (cdr dir-abbrev) - (substring filename (match-end 0))))))) - -(compat-defun file-name-split (filename) ;; - "Return a list of all the components of FILENAME. -On most systems, this will be true: - - (equal (string-join (file-name-split filename) \"/\") filename)" - (let ((components nil)) - ;; If this is a directory file name, then we have a null file name - ;; at the end. - (when (directory-name-p filename) - (push "" components) - (setq filename (directory-file-name filename))) - ;; Loop, chopping off components. - (while (length> filename 0) - (push (file-name-nondirectory filename) components) - (let ((dir (file-name-directory filename))) - (setq filename (and dir (directory-file-name dir))) - ;; If there's nothing left to peel off, we're at the root and - ;; we can stop. - (when (and dir (equal dir filename)) - (push (if (equal dir "") "" - ;; On Windows, the first component might be "c:" or - ;; the like. - (substring dir 0 -1)) - components) - (setq filename nil)))) - components)) - -(compat-defun file-attribute-file-identifier (attributes) ;; - "The inode and device numbers in ATTRIBUTES returned by `file-attributes'. -The value is a list of the form (INODENUM DEVICE), where DEVICE could be -either a single number or a cons cell of two numbers. -This tuple of numbers uniquely identifies the file." - (nthcdr 10 attributes)) - -(compat-defun file-name-parent-directory (filename) ;; - "Return the directory name of the parent directory of FILENAME. -If FILENAME is at the root of the filesystem, return nil. -If FILENAME is relative, it is interpreted to be relative -to `default-directory', and the result will also be relative." - (let* ((expanded-filename (expand-file-name filename)) - (parent (file-name-directory (directory-file-name expanded-filename)))) - (cond - ;; filename is at top-level, therefore no parent - ((or (null parent) - ;; `equal' is enough, we don't need to resolve symlinks here - ;; with `file-equal-p', also for performance - (equal parent expanded-filename)) - nil) - ;; filename is relative, return relative parent - ((not (file-name-absolute-p filename)) - (file-relative-name parent)) - (t - parent)))) - -(compat-defvar file-has-changed-p--hash-table ;; - (make-hash-table :test #'equal) - "Internal variable used by `file-has-changed-p'.") - -(compat-defun file-has-changed-p (file &optional tag) ;; - "Return non-nil if FILE has changed. -The size and modification time of FILE are compared to the size -and modification time of the same FILE during a previous -invocation of `file-has-changed-p'. Thus, the first invocation -of `file-has-changed-p' always returns non-nil when FILE exists. -The optional argument TAG, which must be a symbol, can be used to -limit the comparison to invocations with identical tags; it can be -the symbol of the calling function, for example." - (let* ((file (directory-file-name (expand-file-name file))) - (remote-file-name-inhibit-cache t) - (fileattr (file-attributes file 'integer)) - (attr (and fileattr - (cons (file-attribute-size fileattr) - (file-attribute-modification-time fileattr)))) - (sym (concat (symbol-name tag) "@" file)) - (cachedattr (gethash sym file-has-changed-p--hash-table))) - (unless (equal attr cachedattr) - (puthash sym attr file-has-changed-p--hash-table)))) - -;;;; Defined in keymap.el - -(compat-defun key-valid-p (keys) ;; - "Say whether KEYS is a valid key. -A key is a string consisting of one or more key strokes. -The key strokes are separated by single space characters. - -Each key stroke is either a single character, or the name of an -event, surrounded by angle brackets. In addition, any key stroke -may be preceded by one or more modifier keys. Finally, a limited -number of characters have a special shorthand syntax. - -Here's some example key sequences. - - \"f\" (the key `f') - \"S o m\" (a three key sequence of the keys `S', `o' and `m') - \"C-c o\" (a two key sequence of the keys `c' with the control modifier - and then the key `o') - \"H-\" (the key named \"left\" with the hyper modifier) - \"M-RET\" (the \"return\" key with a meta modifier) - \"C-M-\" (the \"space\" key with both the control and meta modifiers) - -These are the characters that have shorthand syntax: -NUL, RET, TAB, LFD, ESC, SPC, DEL. - -Modifiers have to be specified in this order: - - A-C-H-M-S-s - -which is - - Alt-Control-Hyper-Meta-Shift-super" - (declare (pure t) (side-effect-free t)) - (let ((case-fold-search nil)) - (and - (stringp keys) - (string-match-p "\\`[^ ]+\\( [^ ]+\\)*\\'" keys) - (save-match-data - (catch 'exit - (let ((prefixes - "\\(A-\\)?\\(C-\\)?\\(H-\\)?\\(M-\\)?\\(S-\\)?\\(s-\\)?")) - (dolist (key (split-string keys " ")) - ;; Every key might have these modifiers, and they should be - ;; in this order. - (when (string-match (concat "\\`" prefixes) key) - (setq key (substring key (match-end 0)))) - (unless (or (and (= (length key) 1) - ;; Don't accept control characters as keys. - (not (< (aref key 0) ?\s)) - ;; Don't accept Meta'd characters as keys. - (or (multibyte-string-p key) - (not (<= 127 (aref key 0) 255)))) - (and (string-match-p "\\`<[-_A-Za-z0-9]+>\\'" key) - ;; Don't allow . - (= (progn - (string-match - (concat "\\`<" prefixes) key) - (match-end 0)) - 1)) - (string-match-p - "\\`\\(NUL\\|RET\\|TAB\\|LFD\\|ESC\\|SPC\\|DEL\\)\\'" - key)) - ;; Invalid. - (throw 'exit nil))) - t)))))) - -(compat-defun keymap--check (key) ;; - "Signal an error if KEY doesn't have a valid syntax." - (unless (key-valid-p key) - (error "%S is not a valid key definition; see `key-valid-p'" key))) - -(compat-defun key-parse (keys) ;; - "Convert KEYS to the internal Emacs key representation. -See `kbd' for a descripion of KEYS." - (declare (pure t) (side-effect-free t)) - ;; A pure function is expected to preserve the match data. - (save-match-data - (let ((case-fold-search nil) - (len (length keys)) ; We won't alter keys in the loop below. - (pos 0) - (res [])) - (while (and (< pos len) - (string-match "[^ \t\n\f]+" keys pos)) - (let* ((word-beg (match-beginning 0)) - (word-end (match-end 0)) - (word (substring keys word-beg len)) - (times 1) - key) - ;; Try to catch events of the form "". - (if (string-match "\\`<[^ <>\t\n\f][^>\t\n\f]*>" word) - (setq word (match-string 0 word) - pos (+ word-beg (match-end 0))) - (setq word (substring keys word-beg word-end) - pos word-end)) - (when (string-match "\\([0-9]+\\)\\*." word) - (setq times (string-to-number (substring word 0 (match-end 1)))) - (setq word (substring word (1+ (match-end 1))))) - (cond ((string-match "^<<.+>>$" word) - (setq key (vconcat (if (eq (key-binding [?\M-x]) - 'execute-extended-command) - [?\M-x] - (or (car (where-is-internal - 'execute-extended-command)) - [?\M-x])) - (substring word 2 -2) "\r"))) - ((and (string-match "^\\(\\([ACHMsS]-\\)*\\)<\\(.+\\)>$" word) - (progn - (setq word (concat (match-string 1 word) - (match-string 3 word))) - (not (string-match - "\\<\\(NUL\\|RET\\|LFD\\|ESC\\|SPC\\|DEL\\)$" - word)))) - (setq key (list (intern word)))) - ((or (equal word "REM") (string-match "^;;" word)) - (setq pos (string-match "$" keys pos))) - (t - (let ((orig-word word) (prefix 0) (bits 0)) - (while (string-match "^[ACHMsS]-." word) - (setq bits (+ bits - (cdr - (assq (aref word 0) - '((?A . ?\A-\0) (?C . ?\C-\0) - (?H . ?\H-\0) (?M . ?\M-\0) - (?s . ?\s-\0) (?S . ?\S-\0)))))) - (setq prefix (+ prefix 2)) - (setq word (substring word 2))) - (when (string-match "^\\^.$" word) - (setq bits (+ bits ?\C-\0)) - (setq prefix (1+ prefix)) - (setq word (substring word 1))) - (let ((found (assoc word '(("NUL" . "\0") ("RET" . "\r") - ("LFD" . "\n") ("TAB" . "\t") - ("ESC" . "\e") ("SPC" . " ") - ("DEL" . "\177"))))) - (when found (setq word (cdr found)))) - (when (string-match "^\\\\[0-7]+$" word) - (let ((n 0)) - (dolist (ch (cdr (string-to-list word))) - (setq n (+ (* n 8) ch -48))) - (setq word (vector n)))) - (cond ((= bits 0) - (setq key word)) - ((and (= bits ?\M-\0) (stringp word) - (string-match "^-?[0-9]+$" word)) - (setq key (mapcar (lambda (x) (+ x bits)) - (append word nil)))) - ((/= (length word) 1) - (error "%s must prefix a single character, not %s" - (substring orig-word 0 prefix) word)) - ((and (/= (logand bits ?\C-\0) 0) (stringp word) - ;; We used to accept . and ? here, - ;; but . is simply wrong, - ;; and C-? is not used (we use DEL instead). - (string-match "[@-_a-z]" word)) - (setq key (list (+ bits (- ?\C-\0) - (logand (aref word 0) 31))))) - (t - (setq key (list (+ bits (aref word 0))))))))) - (when key - (dolist (_ (number-sequence 1 times)) - (setq res (vconcat res key)))))) - res))) - -(compat-defun keymap-set (keymap key definition) ;; - "Set KEY to DEFINITION in KEYMAP. -KEY is a string that satisfies `key-valid-p'. - -DEFINITION is anything that can be a key's definition: - nil (means key is undefined in this keymap), - a command (a Lisp function suitable for interactive calling), - a string (treated as a keyboard macro), - a keymap (to define a prefix key), - a symbol (when the key is looked up, the symbol will stand for its - function definition, which should at that time be one of the above, - or another symbol whose function definition is used, etc.), - a cons (STRING . DEFN), meaning that DEFN is the definition - (DEFN should be a valid definition in its own right) and - STRING is the menu item name (which is used only if the containing - keymap has been created with a menu name, see `make-keymap'), - or a cons (MAP . CHAR), meaning use definition of CHAR in keymap MAP, - or an extended menu item definition. - (See info node `(elisp)Extended Menu Items'.)" - (keymap--check key) - (when (stringp definition) - (keymap--check definition) - (setq definition (key-parse definition))) - (define-key keymap (key-parse key) definition)) - -(compat-defun keymap-unset (keymap key &optional remove) ;; - "Remove key sequence KEY from KEYMAP. -KEY is a string that satisfies `key-valid-p'. - -If REMOVE, remove the binding instead of unsetting it. This only -makes a difference when there's a parent keymap. When unsetting -a key in a child map, it will still shadow the same key in the -parent keymap. Removing the binding will allow the key in the -parent keymap to be used." - (keymap--check key) - (compat--define-key keymap (key-parse key) nil remove)) - -(compat-defun keymap-global-set (key command) ;; - "Give KEY a global binding as COMMAND. -COMMAND is the command definition to use; usually it is -a symbol naming an interactively-callable function. - -KEY is a string that satisfies `key-valid-p'. - -Note that if KEY has a local binding in the current buffer, -that local binding will continue to shadow any global binding -that you make with this function. - -NOTE: The compatibility version is not a command." - (keymap-set (current-global-map) key command)) - -(compat-defun keymap-local-set (key command) ;; - "Give KEY a local binding as COMMAND. -COMMAND is the command definition to use; usually it is -a symbol naming an interactively-callable function. - -KEY is a string that satisfies `key-valid-p'. - -The binding goes in the current buffer's local map, which in most -cases is shared with all other buffers in the same major mode. - -NOTE: The compatibility version is not a command." - (let ((map (current-local-map))) - (unless map - (use-local-map (setq map (make-sparse-keymap)))) - (keymap-set map key command))) - -(compat-defun keymap-global-unset (key &optional remove) ;; - "Remove global binding of KEY (if any). -KEY is a string that satisfies `key-valid-p'. - -If REMOVE (interactively, the prefix arg), remove the binding -instead of unsetting it. See `keymap-unset' for details. - -NOTE: The compatibility version is not a command." - (keymap-unset (current-global-map) key remove)) - -(compat-defun keymap-local-unset (key &optional remove) ;; - "Remove local binding of KEY (if any). -KEY is a string that satisfies `key-valid-p'. - -If REMOVE (interactively, the prefix arg), remove the binding -instead of unsetting it. See `keymap-unset' for details. - -NOTE: The compatibility version is not a command." - (when (current-local-map) - (keymap-unset (current-local-map) key remove))) - -(compat-defun keymap-substitute (keymap olddef newdef &optional oldmap prefix) ;; - "Replace OLDDEF with NEWDEF for any keys in KEYMAP now defined as OLDDEF. -In other words, OLDDEF is replaced with NEWDEF wherever it appears. -Alternatively, if optional fourth argument OLDMAP is specified, we redefine -in KEYMAP as NEWDEF those keys that are defined as OLDDEF in OLDMAP. - -If you don't specify OLDMAP, you can usually get the same results -in a cleaner way with command remapping, like this: - (define-key KEYMAP [remap OLDDEF] NEWDEF) -\n(fn OLDDEF NEWDEF KEYMAP &optional OLDMAP)" - ;; Don't document PREFIX in the doc string because we don't want to - ;; advertise it. It's meant for recursive calls only. Here's its - ;; meaning - - ;; If optional argument PREFIX is specified, it should be a key - ;; prefix, a string. Redefined bindings will then be bound to the - ;; original key, with PREFIX added at the front. - (unless prefix - (setq prefix "")) - (let* ((scan (or oldmap keymap)) - (prefix1 (vconcat prefix [nil])) - (key-substitution-in-progress - (cons scan key-substitution-in-progress))) - ;; Scan OLDMAP, finding each char or event-symbol that - ;; has any definition, and act on it with hack-key. - (map-keymap - (lambda (char defn) - (aset prefix1 (length prefix) char) - (substitute-key-definition-key defn olddef newdef prefix1 keymap)) - scan))) - -(compat-defun keymap-set-after (keymap key definition &optional after) ;; - "Add binding in KEYMAP for KEY => DEFINITION, right after AFTER's binding. -This is like `keymap-set' except that the binding for KEY is placed -just after the binding for the event AFTER, instead of at the beginning -of the map. Note that AFTER must be an event type (like KEY), NOT a command -\(like DEFINITION). - -If AFTER is t or omitted, the new binding goes at the end of the keymap. -AFTER should be a single event type--a symbol or a character, not a sequence. - -Bindings are always added before any inherited map. - -The order of bindings in a keymap matters only when it is used as -a menu, so this function is not useful for non-menu keymaps." - (keymap--check key) - (when (eq after t) (setq after nil)) ; nil and t are treated the same - (when (stringp after) - (keymap--check after) - (setq after (key-parse after))) - ;; If we're binding this key to another key, then parse that other - ;; key, too. - (when (stringp definition) - (keymap--check definition) - (setq definition (key-parse definition))) - (define-key-after keymap (key-parse key) definition - after)) - -(compat-defun keymap-lookup ;; - (keymap key &optional accept-default no-remap position) - "Return the binding for command KEY. -KEY is a string that satisfies `key-valid-p'. - -If KEYMAP is nil, look up in the current keymaps. If non-nil, it -should either be a keymap or a list of keymaps, and only these -keymap(s) will be consulted. - -The binding is probably a symbol with a function definition. - -Normally, `keymap-lookup' ignores bindings for t, which act as -default bindings, used when nothing else in the keymap applies; -this makes it usable as a general function for probing keymaps. -However, if the optional second argument ACCEPT-DEFAULT is -non-nil, `keymap-lookup' does recognize the default bindings, -just as `read-key-sequence' does. - -Like the normal command loop, `keymap-lookup' will remap the -command resulting from looking up KEY by looking up the command -in the current keymaps. However, if the optional third argument -NO-REMAP is non-nil, `keymap-lookup' returns the unmapped -command. - -If KEY is a key sequence initiated with the mouse, the used keymaps -will depend on the clicked mouse position with regard to the buffer -and possible local keymaps on strings. - -If the optional argument POSITION is non-nil, it specifies a mouse -position as returned by `event-start' and `event-end', and the lookup -occurs in the keymaps associated with it instead of KEY. It can also -be a number or marker, in which case the keymap properties at the -specified buffer position instead of point are used." - (keymap--check key) - (when (and keymap position) - (error "Can't pass in both keymap and position")) - (if keymap - (let ((value (lookup-key keymap (key-parse key) accept-default))) - (if (and (not no-remap) - (symbolp value)) - (or (command-remapping value) value) - value)) - (key-binding (key-parse key) accept-default no-remap position))) - -(compat-defun keymap-local-lookup (keys &optional accept-default) ;; - "Return the binding for command KEYS in current local keymap only. -KEY is a string that satisfies `key-valid-p'. - -The binding is probably a symbol with a function definition. - -If optional argument ACCEPT-DEFAULT is non-nil, recognize default -bindings; see the description of `keymap-lookup' for more details -about this." - (when-let ((map (current-local-map))) - (keymap-lookup map keys accept-default))) - -(compat-defun keymap-global-lookup (keys &optional accept-default _message) ;; - "Return the binding for command KEYS in current global keymap only. -KEY is a string that satisfies `key-valid-p'. - -The binding is probably a symbol with a function definition. -This function's return values are the same as those of `keymap-lookup' -\(which see). - -If optional argument ACCEPT-DEFAULT is non-nil, recognize default -bindings; see the description of `keymap-lookup' for more details -about this. - -NOTE: The compatibility version is not a command." - (keymap-lookup (current-global-map) keys accept-default)) - -(compat-defun define-keymap (&rest definitions) ;; - "Create a new keymap and define KEY/DEFINITION pairs as key bindings. -The new keymap is returned. - -Options can be given as keywords before the KEY/DEFINITION -pairs. Available keywords are: - -:full If non-nil, create a chartable alist (see `make-keymap'). - If nil (i.e., the default), create a sparse keymap (see - `make-sparse-keymap'). - -:suppress If non-nil, the keymap will be suppressed (see `suppress-keymap'). - If `nodigits', treat digits like other chars. - -:parent If non-nil, this should be a keymap to use as the parent - (see `set-keymap-parent'). - -:keymap If non-nil, instead of creating a new keymap, the given keymap - will be destructively modified instead. - -:name If non-nil, this should be a string to use as the menu for - the keymap in case you use it as a menu with `x-popup-menu'. - -:prefix If non-nil, this should be a symbol to be used as a prefix - command (see `define-prefix-command'). If this is the case, - this symbol is returned instead of the map itself. - -KEY/DEFINITION pairs are as KEY and DEF in `keymap-set'. KEY can -also be the special symbol `:menu', in which case DEFINITION -should be a MENU form as accepted by `easy-menu-define'. - -\(fn &key FULL PARENT SUPPRESS NAME PREFIX KEYMAP &rest [KEY DEFINITION]...)" - (declare (indent defun)) - (let (full suppress parent name prefix keymap) - ;; Handle keywords. - (while (and definitions - (keywordp (car definitions)) - (not (eq (car definitions) :menu))) - (let ((keyword (pop definitions))) - (unless definitions - (error "Missing keyword value for %s" keyword)) - (let ((value (pop definitions))) - (pcase keyword - (:full (setq full value)) - (:keymap (setq keymap value)) - (:parent (setq parent value)) - (:suppress (setq suppress value)) - (:name (setq name value)) - (:prefix (setq prefix value)) - (_ (error "Invalid keyword: %s" keyword)))))) - - (when (and prefix - (or full parent suppress keymap)) - (error "A prefix keymap can't be defined with :full/:parent/:suppress/:keymap keywords")) - - (when (and keymap full) - (error "Invalid combination: :keymap with :full")) - - (let ((keymap (cond - (keymap keymap) - (prefix (define-prefix-command prefix nil name)) - (full (make-keymap name)) - (t (make-sparse-keymap name)))) - seen-keys) - (when suppress - (suppress-keymap keymap (eq suppress 'nodigits))) - (when parent - (set-keymap-parent keymap parent)) - - ;; Do the bindings. - (while definitions - (let ((key (pop definitions))) - (unless definitions - (error "Uneven number of key/definition pairs")) - (let ((def (pop definitions))) - (if (eq key :menu) - (easy-menu-define nil keymap "" def) - (if (member key seen-keys) - (error "Duplicate definition for key: %S %s" key keymap) - (push key seen-keys)) - (keymap-set keymap key def))))) - keymap))) - -(compat-defmacro defvar-keymap (variable-name &rest defs) ;; - "Define VARIABLE-NAME as a variable with a keymap definition. -See `define-keymap' for an explanation of the keywords and KEY/DEFINITION. - -In addition to the keywords accepted by `define-keymap', this -macro also accepts a `:doc' keyword, which (if present) is used -as the variable documentation string. - -The `:repeat' keyword can also be specified; it controls the -`repeat-mode' behavior of the bindings in the keymap. When it is -non-nil, all commands in the map will have the `repeat-map' -symbol property. - -More control is available over which commands are repeatable; the -value can also be a property list with properties `:enter' and -`:exit', for example: - - :repeat (:enter (commands ...) :exit (commands ...)) - -`:enter' specifies the list of additional commands that only -enter `repeat-mode'. When the list is empty, then only the -commands defined in the map enter `repeat-mode'. Specifying a -list of commands is useful when there are commands that have the -`repeat-map' symbol property, but don't exist in this specific -map. - -`:exit' is a list of commands that exit `repeat-mode'. When the -list is empty, no commands in the map exit `repeat-mode'. -Specifying a list of commands is useful when those commands exist -in this specific map, but should not have the `repeat-map' symbol -property. - -\(fn VARIABLE-NAME &key DOC FULL PARENT SUPPRESS NAME PREFIX KEYMAP REPEAT &rest [KEY DEFINITION]...)" - (declare (indent 1)) - (let ((opts nil) - doc repeat props) - (while (and defs - (keywordp (car defs)) - (not (eq (car defs) :menu))) - (let ((keyword (pop defs))) - (unless defs - (error "Uneven number of keywords")) - (cond - ((eq keyword :doc) (setq doc (pop defs))) - ((eq keyword :repeat) (setq repeat (pop defs))) - (t (push keyword opts) - (push (pop defs) opts))))) - (unless (zerop (% (length defs) 2)) - (error "Uneven number of key/definition pairs: %s" defs)) - - (let ((defs defs) - key seen-keys) - (while defs - (setq key (pop defs)) - (pop defs) - (unless (eq key :menu) - (if (member key seen-keys) - (error "Duplicate definition for key '%s' in keymap '%s'" - key variable-name) - (push key seen-keys))))) - - (when repeat - (let ((defs defs) - def) - (dolist (def (plist-get repeat :enter)) - (push `(put ',def 'repeat-map ',variable-name) props)) - (while defs - (pop defs) - (setq def (pop defs)) - (when (and (memq (car def) '(function quote)) - (not (memq (cadr def) (plist-get repeat :exit)))) - (push `(put ,def 'repeat-map ',variable-name) props))))) - - (let ((defvar-form - `(defvar ,variable-name - (define-keymap ,@(nreverse opts) ,@defs) - ,@(and doc (list doc))))) - (if props - `(progn - ,defvar-form - ,@(nreverse props)) - defvar-form)))) - -;;;; Defined in keymap.c - -(compat-defun define-key (keymap key def &optional remove) ;; - "Handle optional argument REMOVE." - :extended t - (if (not remove) - (define-key keymap key def) - ;; Canonicalize key - (setq key (key-parse (key-description key))) - (define-key keymap key nil) - ;; Split M-key in ESC key - (setq key (mapcan (lambda (k) - (if (and (integerp k) (/= (logand k ?\M-\0) 0)) - (list ?\e (logxor k ?\M-\0)) - (list k))) - key)) - ;; Delete single keys directly - (if (length= key 1) - (delete key keymap) - ;; Lookup submap and delete key from there - (let ((submap (lookup-key keymap (vconcat (butlast key))))) - (unless (keymapp submap) - (error "Not a keymap for %s" key)) - (when (symbolp submap) - (setq submap (symbol-function submap))) - (delete (last key) submap))) - def)) - -;;;; Defined in help.el - -(compat-defun substitute-quotes (string) ;; - "Substitute quote characters for display. -Each grave accent \\=` is replaced by left quote, and each -apostrophe \\=' is replaced by right quote. Left and right quote -characters are specified by `text-quoting-style'." - (cond ((eq (text-quoting-style) 'curve) - (string-replace "`" "‘" - (string-replace "'" "’" string))) - ((eq (text-quoting-style) 'straight) - (string-replace "`" "'" string)) - (t string))) - -;;;; Defined in button.el - -(compat-defun button--properties (callback data help-echo) ;; - "Helper function." - (list 'font-lock-face 'button - 'mouse-face 'highlight - 'help-echo help-echo - 'button t - 'follow-link t - 'category t - 'button-data data - 'keymap button-map - 'action callback)) - -(compat-defun buttonize (string callback &optional data help-echo) ;; - "Make STRING into a button and return it. -When clicked, CALLBACK will be called with the DATA as the -function argument. If DATA isn't present (or is nil), the button -itself will be used instead as the function argument. - -If HELP-ECHO, use that as the `help-echo' property. - -Also see `buttonize-region'." - (let ((string - (apply #'propertize string - (button--properties callback data help-echo)))) - ;; Add the face to the end so that it can be overridden. - (add-face-text-property 0 (length string) 'button t string) - string)) - -(compat-defun buttonize-region (start end callback &optional data help-echo) ;; - "Make the region between START and END into a button. -When clicked, CALLBACK will be called with the DATA as the -function argument. If DATA isn't present (or is nil), the button -itself will be used instead as the function argument. - -If HELP-ECHO, use that as the `help-echo' property. - -Also see `buttonize'." - (add-text-properties start end (button--properties callback data help-echo)) - (add-face-text-property start end 'button t)) - -;;;; Defined in rmc.el - -(compat-defun read-multiple-choice ;; - (prompt choices &optional _help-str _show-help long-form) - "Handle LONG-FORM argument." - :extended t - (if (not long-form) - (read-multiple-choice prompt choices) - (let ((answer - (completing-read - (concat prompt " (" - (mapconcat #'identity (mapcar #'cadr choices) "/") - ") ") - (mapcar #'cadr choices) nil t))) - (catch 'found - (dolist (c choices) - (when (equal answer (cadr c)) - (throw 'found c))))))) - -;;;; Defined in paragraphs.el - -(compat-defun count-sentences (start end) ;; - "Count sentences in current buffer from START to END." - (let ((sentences 0) - (inhibit-field-text-motion t)) - (save-excursion - (save-restriction - (narrow-to-region start end) - (goto-char (point-min)) - (while (ignore-errors (forward-sentence)) - (setq sentences (1+ sentences))) - (when (/= (skip-chars-backward " \t\n") 0) - (setq sentences (1- sentences))) - sentences)))) - -;;;; Defined in cl-lib.el - -(compat-defun cl-constantly (value) ;; - "Return a function that takes any number of arguments, but returns VALUE." - :feature cl-lib - (lambda (&rest _) value)) - -;;;; Defined in cl-macs.el - -(compat-defmacro cl-with-gensyms (names &rest body) ;; - "Bind each of NAMES to an uninterned symbol and evaluate BODY." - ;; No :feature since macro is autoloaded - (declare (debug (sexp body)) (indent 1)) - `(let ,(cl-loop for name in names collect - `(,name (gensym (symbol-name ',name)))) - ,@body)) - -(compat-defmacro cl-once-only (names &rest body) ;; - "Generate code to evaluate each of NAMES just once in BODY. - -This macro helps with writing other macros. Each of names is -either (NAME FORM) or NAME, which latter means (NAME NAME). -During macroexpansion, each NAME is bound to an uninterned -symbol. The expansion evaluates each FORM and binds it to the -corresponding uninterned symbol. - -For example, consider this macro: - - (defmacro my-cons (x) - (cl-once-only (x) - \\=`(cons ,x ,x))) - -The call (my-cons (pop y)) will expand to something like this: - - (let ((g1 (pop y))) - (cons g1 g1)) - -The use of `cl-once-only' ensures that the pop is performed only -once, as intended. - -See also `macroexp-let2'." - ;; No :feature since macro is autoloaded - (declare (debug (sexp body)) (indent 1)) - (setq names (mapcar #'ensure-list names)) - (let ((our-gensyms (cl-loop for _ in names collect (gensym)))) - `(let ,(cl-loop for sym in our-gensyms collect `(,sym (gensym))) - `(let ,(list - ,@(cl-loop for name in names for gensym in our-gensyms - for to-eval = (or (cadr name) (car name)) - collect ``(,,gensym ,,to-eval))) - ,(let ,(cl-loop for name in names for gensym in our-gensyms - collect `(,(car name) ,gensym)) - ,@body))))) - -;;;; Defined in ert-x.el - -(compat-defmacro ert-with-temp-file (name &rest body) ;; - "Bind NAME to the name of a new temporary file and evaluate BODY. -Delete the temporary file after BODY exits normally or -non-locally. NAME will be bound to the file name of the temporary -file. - -The following keyword arguments are supported: - -:prefix STRING If non-nil, pass STRING to `make-temp-file' as - the PREFIX argument. Otherwise, use the value of - `ert-temp-file-prefix'. - -:suffix STRING If non-nil, pass STRING to `make-temp-file' as the - SUFFIX argument. Otherwise, use the value of - `ert-temp-file-suffix'; if the value of that - variable is nil, generate a suffix based on the - name of the file that `ert-with-temp-file' is - called from. - -:text STRING If non-nil, pass STRING to `make-temp-file' as - the TEXT argument. - -:buffer SYMBOL Open the temporary file using `find-file-noselect' - and bind SYMBOL to the buffer. Kill the buffer - after BODY exits normally or non-locally. - -:coding CODING If non-nil, bind `coding-system-for-write' to CODING - when executing BODY. This is handy when STRING includes - non-ASCII characters or the temporary file must have a - specific encoding or end-of-line format. - -See also `ert-with-temp-directory'." - :feature ert-x - (declare (indent 1) (debug (symbolp body))) - (cl-check-type name symbol) - (let (keyw prefix suffix directory text extra-keywords buffer coding) - (while (keywordp (setq keyw (car body))) - (setq body (cdr body)) - (pcase keyw - (:prefix (setq prefix (pop body))) - (:suffix (setq suffix (pop body))) - ;; This is only for internal use by `ert-with-temp-directory' - ;; and is therefore not documented. - (:directory (setq directory (pop body))) - (:text (setq text (pop body))) - (:buffer (setq buffer (pop body))) - (:coding (setq coding (pop body))) - (_ (push keyw extra-keywords) (pop body)))) - (when extra-keywords - (error "Invalid keywords: %s" (mapconcat #'symbol-name extra-keywords " "))) - (let ((temp-file (make-symbol "temp-file")) - (prefix (or prefix "emacs-test-")) - (suffix (or suffix - (thread-last - (file-name-base (or (macroexp-file-name) buffer-file-name)) - (replace-regexp-in-string (rx string-start - (group (+? not-newline)) - (regexp "-?tests?") - string-end) - "\\1") - (concat "-"))))) - `(let* ((coding-system-for-write ,(or coding coding-system-for-write)) - (,temp-file (,(if directory 'file-name-as-directory 'identity) - (,(if (fboundp 'compat--make-temp-file) - 'compat--make-temp-file 'make-temp-file) - ,prefix ,directory ,suffix ,text))) - (,name ,(if directory - `(file-name-as-directory ,temp-file) - temp-file)) - ,@(when buffer - (list `(,buffer (find-file-literally ,temp-file))))) - (unwind-protect - (progn ,@body) - (ignore-errors - ,@(when buffer - (list `(with-current-buffer ,buffer - (set-buffer-modified-p nil)) - `(kill-buffer ,buffer)))) - (ignore-errors - ,(if directory - `(delete-directory ,temp-file :recursive) - `(delete-file ,temp-file)))))))) - -(compat-defmacro ert-with-temp-directory (name &rest body) ;; - "Bind NAME to the name of a new temporary directory and evaluate BODY. -Delete the temporary directory after BODY exits normally or -non-locally. - -NAME is bound to the directory name, not the directory file -name. (In other words, it will end with the directory delimiter; -on Unix-like systems, it will end with \"/\".) - -The same keyword arguments are supported as in -`ert-with-temp-file' (which see), except for :text." - :feature ert-x - (declare (indent 1) (debug (symbolp body))) - (let ((tail body) keyw) - (while (keywordp (setq keyw (car tail))) - (setq tail (cddr tail)) - (pcase keyw (:text (error "Invalid keyword for directory: :text"))))) - `(ert-with-temp-file ,name - :directory t - ,@body)) - -;;;; Defined in wid-edit.el - -(compat-guard (not (fboundp 'widget-key-validate)) ;; - :feature wid-edit - (defvar widget-key-prompt-value-history nil - "History of input to `widget-key-prompt-value'.") - (define-widget 'key 'editable-field - "A key sequence." - :prompt-value 'widget-field-prompt-value - :match 'widget-key-valid-p - :format "%{%t%}: %v" - :validate 'widget-key-validate - :keymap widget-key-sequence-map - :help-echo "C-q: insert KEY, EVENT, or CODE; RET: enter value" - :tag "Key") - (defun widget-key-valid-p (_widget value) - (key-valid-p value)) - (defun widget-key-validate (widget) - (unless (and (stringp (widget-value widget)) - (key-valid-p (widget-value widget))) - (widget-put widget :error (format "Invalid key: %S" - (widget-value widget))) - widget))) - -(provide 'compat-29) -;;; compat-29.el ends here blob - 676fd32e08dcbd44a12761d76b44d5f4487195ac (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/compat-autoloads.el +++ /dev/null @@ -1,33 +0,0 @@ -;;; compat-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from compat.el - -(register-definition-prefixes "compat" '("compat-")) - - -;;; Generated autoloads from compat-macs.el - -(register-definition-prefixes "compat-macs" '("compat-")) - -;;; End of scraped data - -(provide 'compat-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; compat-autoloads.el ends here blob - ffd5223305343d89bf9f75ff9ef8d146d4488116 (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/compat-macs.el +++ /dev/null @@ -1,267 +0,0 @@ -;;; compat-macs.el --- Compatibility Macros -*- lexical-binding: t; no-byte-compile: t; -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; WARNING: This file provides *internal* macros. The macros are used -;; by Compat to facilitate the definition of compatibility functions, -;; compatibility macros and compatibility variables. The -;; `compat-macs' feature should never be loaded at runtime in your -;; Emacs and will only be used during byte compilation. Every -;; definition provided here is internal, may change any time between -;; Compat releases and must not be used by other packages. - -;;; Code: - -;; We always require subr-x at compile time for the fboundp check -;; since definitions have been moved around. The cl-lib macros are -;; needed by compatibility definitions. -(require 'subr-x) -(require 'cl-lib) - -(defvar compat-macs--version nil - "Version of the currently defined compatibility definitions.") - -(defun compat-macs--strict (cond &rest error) - "Assert strict COND, otherwise fail with ERROR." - (when (bound-and-true-p compat-strict) - (apply #'compat-macs--assert cond error))) - -(defun compat-macs--assert (cond &rest error) - "Assert COND, otherwise fail with ERROR." - (unless cond (apply #'error error))) - -(defun compat-macs--docstring (type name docstring) - "Format DOCSTRING for NAME of TYPE. -Prepend compatibility notice to the actual documentation string." - (with-temp-buffer - (insert - (format - "[Compatibility %s for `%s', defined in Emacs %s. \ -See (compat) Emacs %s' for more details.]\n\n%s" - type name compat-macs--version compat-macs--version docstring)) - (let ((fill-column 80)) - (fill-region (point-min) (point-max))) - (buffer-string))) - -(defun compat-macs--check-attributes (attrs preds) - "Check ATTRS given PREDS predicate plist and return rest." - (while (keywordp (car attrs)) - (compat-macs--assert (cdr attrs) "Attribute list length is odd") - (compat-macs--assert (let ((p (plist-get preds (car attrs)))) - (and p (or (eq p t) (funcall p (cadr attrs))))) - "Invalid attribute %s" (car attrs)) - (setq attrs (cddr attrs))) - attrs) - -(defun compat-macs--guard (attrs preds fun) - "Guard compatibility definition generation. -The version constraints specified by ATTRS are checked. PREDS is -a plist of predicates for arguments which are passed to FUN." - (declare (indent 2)) - (compat-macs--assert compat-macs--version "No `compat-version' was declared") - (let* ((body (compat-macs--check-attributes - attrs `(,@preds :feature symbolp))) - (feature (plist-get attrs :feature)) - (attrs `(:body ,body ,@attrs)) - args) - ;; Require feature at compile time - (when feature - (compat-macs--assert (not (eq feature 'subr-x)) "Invalid feature subr-x") - (require feature)) - ;; The current Emacs must be older than the currently declared version. - (when (version< emacs-version compat-macs--version) - (while preds - (push (plist-get attrs (car preds)) args) - (setq preds (cddr preds))) - (setq body (apply fun (nreverse args))) - (if (and feature body) - `(with-eval-after-load ',feature ,@body) - (macroexp-progn body))))) - -(defun compat-macs--defun (type name arglist docstring rest) - "Define function NAME of TYPE with ARGLIST and DOCSTRING. -REST are attributes and the function BODY." - (compat-macs--guard - rest (list :extended (lambda (x) (or (booleanp x) (version-to-list x))) - :obsolete (lambda (x) (or (booleanp x) (stringp x))) - :body t) - (lambda (extended obsolete body) - (when (stringp extended) - (compat-macs--assert - (and (version< extended compat-macs--version) (version< "24.4" extended)) - "Invalid :extended version %s for %s %s" extended type name) - (setq extended (version<= extended emacs-version))) - (compat-macs--strict (eq extended (fboundp name)) - "Wrong :extended flag for %s %s" type name) - ;; Remove unsupported declares. It might be possible to set these - ;; properties otherwise. That should be looked into and implemented - ;; if it is the case. - (when (and (listp (car-safe body)) (eq (caar body) 'declare) (<= emacs-major-version 25)) - (setcar body (assq-delete-all 'pure (assq-delete-all - 'side-effect-free (car body))))) - ;; Use `:extended' name if the function is already defined. - (let* ((defname (if (and extended (fboundp name)) - (intern (format "compat--%s" name)) - name)) - (def `(,(if (memq '&key arglist) - (if (eq type 'macro) 'cl-defmacro 'cl-defun) - (if (eq type 'macro) 'defmacro 'defun)) - ,defname ,arglist - ,(compat-macs--docstring type name docstring) - ,@body))) - `(,@(if (eq defname name) - ;; An additional fboundp check is performed at runtime to make - ;; sure that we never redefine an existing definition if Compat - ;; is loaded on a newer Emacs version. Declare the function, - ;; such that the byte compiler does not complain about possibly - ;; missing functions at runtime. The warnings are generated due - ;; to the fboundp check. - `((declare-function ,name nil) - (unless (fboundp ',name) ,def)) - (list def)) - ,@(when obsolete - `((make-obsolete - ',defname ,(if (stringp obsolete) obsolete "No substitute") - ,compat-macs--version)))))))) - -(defmacro compat-guard (cond &rest rest) - "Guard definition with a runtime COND and a version check. -The runtime condition must make sure that no definition is -overridden. REST is an attribute plist followed by the definition -body. The attributes specify the conditions under which the -definition is generated. - -- :feature :: Wrap the definition with `with-eval-after-load' for - the given feature." - (declare (debug ([&rest keywordp sexp] def-body)) - (indent 1)) - (compat-macs--guard rest '(:body t) - (lambda (body) - (compat-macs--assert body "The guarded body is empty") - (if (eq cond t) - body - (compat-macs--strict (eval cond t) "Guard %S failed" cond) - `((when ,cond ,@body)))))) - -(defmacro compat-defalias (name def &rest attrs) - "Define compatibility alias NAME as DEF. -ATTRS is a plist of attributes, which specify the conditions -under which the definition is generated. - -- :obsolete :: Mark the alias as obsolete if t. - -- :feature :: See `compat-guard'." - (declare (debug (name symbolp [&rest keywordp sexp]))) - (compat-macs--guard attrs '(:obsolete booleanp) - (lambda (obsolete) - (compat-macs--strict (not (fboundp name)) "%s already defined" name) - ;; The fboundp check is performed at runtime to make sure that we never - ;; redefine an existing definition if Compat is loaded on a newer Emacs - ;; version. - `((unless (fboundp ',name) - (defalias ',name ',def - ,(compat-macs--docstring 'function name - (get name 'function-documentation))) - ,@(when obsolete - `((make-obsolete ',name ',def ,compat-macs--version)))))))) - -(defmacro compat-defun (name arglist docstring &rest rest) - "Define compatibility function NAME with arguments ARGLIST. -The function must be documented in DOCSTRING. REST is an -attribute plist followed by the function body. The attributes -specify the conditions under which the definition is generated. - -- :extended :: Mark the function as extended if t. The function - must be called explicitly via `compat-call'. This attribute - should be used for functions which extend already existing - functions, e.g., functions which changed their calling - convention or their behavior. The value can also be a version - string, which specifies the Emacs version when the original - version of the function was introduced. - -- :obsolete :: Mark the function as obsolete if t, can be a - string describing the obsoletion. - -- :feature :: See `compat-guard'." - (declare (debug (&define name (&rest symbolp) - stringp - [&rest keywordp sexp] - def-body)) - (doc-string 3) (indent 2)) - (compat-macs--defun 'function name arglist docstring rest)) - -(defmacro compat-defmacro (name arglist docstring &rest rest) - "Define compatibility macro NAME with arguments ARGLIST. -The macro must be documented in DOCSTRING. REST is an attribute -plist followed by the macro body. See `compat-defun' for -details." - (declare (debug compat-defun) (doc-string 3) (indent 2)) - (compat-macs--defun 'macro name arglist docstring rest)) - -(defmacro compat-defvar (name initval docstring &rest attrs) - "Define compatibility variable NAME with initial value INITVAL. -The variable must be documented in DOCSTRING. ATTRS is a plist -of attributes, which specify the conditions under which the -definition is generated. - -- :constant :: Mark the variable as constant if t. - -- :local :: Make the variable buffer-local if t. If the value is - `permanent' make the variable additionally permanently local. - -- :obsolete :: Mark the variable as obsolete if t, can be a - string describing the obsoletion. - -- :feature :: See `compat-guard'." - (declare (debug (name form stringp [&rest keywordp sexp])) - (doc-string 3) (indent 2)) - (compat-macs--guard - attrs (list :constant #'booleanp - :local (lambda (x) (memq x '(nil t permanent))) - :obsolete (lambda (x) (or (booleanp x) (stringp x)))) - (lambda (constant local obsolete) - (compat-macs--strict (not (boundp name)) "%s already defined" name) - (compat-macs--assert (not (and constant local)) "Both :constant and :local") - ;; The boundp check is performed at runtime to make sure that we never - ;; redefine an existing definition if Compat is loaded on a newer Emacs - ;; version. - `((defvar ,name) - (unless (boundp ',name) - (,(if constant 'defconst 'defvar) - ,name ,initval - ,(compat-macs--docstring 'variable name docstring)) - ,@(when obsolete - `((make-obsolete-variable - ',name ,(if (stringp obsolete) obsolete "No substitute") - ,compat-macs--version)))) - ,@(and local `((make-variable-buffer-local ',name))) - ,@(and (eq local 'permanent) `((put ',name 'permanent-local t))))))) - -(defmacro compat-version (version) - "Set the Emacs version that is currently being handled to VERSION." - (setq compat-macs--version version) - nil) - -(defmacro compat-require (feature version) - "Require FEATURE if the Emacs version is less than VERSION." - (when (version< emacs-version version) - (require feature) - `(require ',feature))) - -(provide 'compat-macs) -;;; compat-macs.el ends here blob - 6b1726446ae7b460d72a40359072cec48b9afc18 (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/compat-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from compat.el -*- no-byte-compile: t -*- -(define-package "compat" "29.1.4.5" "Emacs Lisp Compatibility Library" '((emacs "24.4") (seq "2.23")) :commit "8d4e8a366681def88751f5e9975738ecd3180deb" :authors '(("Philip Kaludercic" . "philipk@posteo.net") ("Daniel Mendler" . "mail@daniel-mendler.de")) :maintainer '("Compat Development" . "~pkal/compat-devel@lists.sr.ht") :keywords '("lisp" "maint") :url "https://github.com/emacs-compat/compat") blob - b2eb9fef7d5594392c59628fbfe959b537f67fe4 (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/compat.el +++ /dev/null @@ -1,93 +0,0 @@ -;;; compat.el --- Emacs Lisp Compatibility Library -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; Author: Philip Kaludercic , Daniel Mendler -;; Maintainer: Compat Development <~pkal/compat-devel@lists.sr.ht> -;; Version: 29.1.4.5 -;; URL: https://github.com/emacs-compat/compat -;; Package-Requires: ((emacs "24.4") (seq "2.23")) -;; Keywords: lisp, maint - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Compat is the Elisp forwards compatibility library, which provides -;; definitions introduced in newer Emacs versions. The definitions -;; are only installed if necessary for your current Emacs version. If -;; Compat is compiled on a recent version of Emacs, all of the -;; definitions are disabled at compile time, such that no negative -;; performance impact is incurred. The provided compatibility -;; implementations of functions and macros are at least subsets of the -;; actual implementations. Be sure to read the documentation string -;; and the Compat manual. -;; -;; Not every function provided in newer versions of Emacs is provided -;; here. Some depend on new features from the C core, others cannot -;; be implemented to a meaningful degree. Please consult the Compat -;; manual for details regarding the usage of the Compat library and -;; the provided functionality. - -;; The main audience for this library are not regular users, but -;; package maintainers. Therefore no commands, user-facing modes or -;; user options are implemented here. - -;;; Code: - -;; Ensure that the newest compatibility layer is required at compile -;; time and runtime, but only if needed. -(eval-when-compile - (defmacro compat--maybe-require () - (when (version< emacs-version "29.1") - (require 'compat-29) - '(require 'compat-29)))) -(compat--maybe-require) - -;;;; Macros for extended compatibility function calls - -(defmacro compat-function (fun) - "Return compatibility function symbol for FUN. - -If the Emacs version provides a sufficiently recent version of -FUN, the symbol FUN is returned itself. Otherwise the macro -returns the symbol of a compatibility function which supports the -behavior and calling convention of the current stable Emacs -version. For example Compat 29.1 will provide compatibility -functions which implement the behavior and calling convention of -Emacs 29.1. - -See also `compat-call' to directly call compatibility functions." - (let ((compat (intern (format "compat--%s" fun)))) - `#',(if (fboundp compat) compat fun))) - -(defmacro compat-call (fun &rest args) - "Call compatibility function or macro FUN with ARGS. - -A good example function is `plist-get' which was extended with an -additional predicate argument in Emacs 29.1. The compatibility -function, which supports this additional argument, can be -obtained via (compat-function plist-get) and called -via (compat-call plist-get plist prop predicate). It is not -possible to directly call (plist-get plist prop predicate) on -Emacs older than 29.1, since the original `plist-get' function -does not yet support the predicate argument. Note that the -Compat library never overrides existing functions. - -See also `compat-function' to lookup compatibility functions." - (let ((compat (intern (format "compat--%s" fun)))) - `(,(if (fboundp compat) compat fun) ,@args))) - -(provide 'compat) -;;; compat.el ends here blob - c0a04905f8886b5dcd400ffa45e277810876a1f0 (mode 644) blob + /dev/null Binary files elpa/compat-29.1.4.5/compat.info and /dev/null differ blob - de02c6eadb212b1217e57dc19d8b47900da923af (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5/dir +++ /dev/null @@ -1,18 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs -* Compat: (compat). Compatibility Library for Emacs Lisp. blob - d21a9481862a73a5fb471904f946ae32838b30ef (mode 644) blob + /dev/null --- elpa/compat-29.1.4.5.signed +++ /dev/null @@ -1,2 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2024-03-16T22:05:02+0100 using RSA -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2024-03-16T22:05:03+0100 using EDDSA \ No newline at end of file blob - 3b2af73102bd78274045b7544b50ede3c1f23bed (mode 644) blob + /dev/null --- elpa/consult-1.5/CHANGELOG.org +++ /dev/null @@ -1,417 +0,0 @@ -#+title: consult.el - Changelog -#+author: Daniel Mendler -#+language: en - -* Version 1.5 (2024-04-19) - -- Bugfix ~consult-buffer~: Handle buffer renaming during minibuffer completion - gracefully, by attaching the actual buffer objects to the completion candidate - strings. -- Bugfix ~consult-register~: Ignore marker registers pointing to dead buffers. - -* Version 1.4 (2024-03-08) - -- Bugfix: File preview: Ensure that binary files are not previewed partially. - Otherwise ~pdf-view-mode~ may observe corrupted PDF files. -- ~consult--async-refresh-timer~: Optimize timer reuse and efficiency. This change - improves the performance of commands like ~consult-ripgrep~ for small values of - ~consult-async-refresh-delay~. -- ~consult-completion-in-region~: Remove ~:cycle-threshold~ and ~:completion-styles~ - customization options. - -* Version 1.3 (2024-02-23) - -- ~consult-bookmark-narrow~: More flexible grouping which supports multiple - bookmark handlers per group. -- Bugfix: Ensure that preview is always executed in a non-minibuffer window. -- Bugfix: File preview: Do not preview ~hexl-mode~ buffers. -- Bugfix: File preview: use ~error-message-string~ to access error string. -- Bugfix: Buffer preview: Retrieve original window correctly. -- Bugfix: Fix ~consult-global-mark~ for ~embark-export~. - -* Version 1.2 (2024-01-23) - -- =consult-buffer=: Bugfix. Ensure that null completion works properly. -- File preview: Add indication if previewed file got truncated. - -* Version 1.1 (2023-12-27) - -- Bugfixes: - + ~consult-xref~: Do not error for an empty location list. - + ~consult--read~: Catch null completion if require-match is non-nil. - + ~consult--multi~: Ensure that :new action is invoked on visible source. -- File preview: Check for long lines when previewing files partially. -- Use ~minibuffer-local-filename-syntax~ and ~read-file-name-completion-ignore-case~ - for directory prompt of the ~consult-grep~ and ~consult--find~ family of commands. -- Remove obsolete variables ~consult-preview-max-size~ and - ~consult-preview-raw-size~. - -* Version 1.0 (2023-12-01) - -- Bugfixes. -- Preview large files partially. Add new customization variables - =consult-preview-partial-chunk= and =consult-preview-partial-limit=. This new - feature is experimental. Please report any issues you observe. -- Obsoleted =consult-preview-max-size= and =consult-preview-raw-size=. -- =consult-buffer-other-tab=: New command. -- =consult-fd=: New command based on the fast =fd/fdfind= search utility. -- =consult-outline=: New prefix argument to specify initial narrowing level. -- =consult-org-heading=: Specify category =org-heading= such that Embark provides - appropriate Org heading actions. -- =consult-org-heading=: Add annotation. -- =consult-locate=: Split input into multiple words. -- Remove unreliable =consult--maybe-recenter=. -- Save input history even when using =embark-export= or when aborting from a - command via C-g. This change affects commands like =consult-line= and - =consult-grep=. -- Unify history of =consult-line=, =consult-keep-lines= and =consult-focus-lines=. - -* Version 0.35 (2023-07-02) - -- Bugfixes. -- =consult--read= now accepts programmable completion tables as argument, e.g., - =completion-table-dynamic= or =completion--file-name-table=. This allows you to - reuse existing completion tables to write completion commands enhanced with - Consult candidate preview. -- Replace =consult-preview-cursor= face with =cursor-highlight-mark=. -- Change calling convention of =consult-focus-lines= and =consult-keep-lines=. -- The regexps in =consult-buffer-filter= are matched case sensitively now. - Similarly, the =INCLUDE= and =EXCLUDE= arguments of =consult--buffer-query= are also - case sensitive. -- Do not preview remote files by default, see =consult-preview-excluded-files=. -- Use =consult--maybe-recenter= instead of =recenter= in =consult-after-jump-hook=. -- =consult-goto-line=: Support =line:column= input. - -* Version 0.34 (2023-04-21) - -- Bugfixes. -- =consult-org-heading=: Support tag inheritance. -- Use pure =consult--fast-abbreviate-file-name= function to abbreviate file names - in =consult-buffer= and =consult-recent-file=. This ensures that abbreviation does - not access the file system (or worse remote hosts via Tramp) and is always - fast. The downside is that some paths may not get abbreviated. -- Introduce buffer sources =consult--source-project-buffer-hidden= and - =consult--source-project-recent-file-hidden=. Set the buffer sources of - =consult-project= to =consult--source-project-buffer= and - =consult--source-project-recent-file= to ease customization. -- =consult-buffer=: Explicitly save =window-next-buffers= and =window-prev-buffers=. -- When previewing files literally (=consult-preview-raw-size=), set the multi byte - flag of the previewed buffer, such that UTF-8 buffers are not garbled. -- Do not create preview cursor overlay. Instead display the actual point by - ensuring that =cursor-in-non-selected-windows= is set. - -* Version 0.33 (2023-03-11) - -- BREAKING: The key convention has been updated. The old key convention is not - supported anymore. Keys must now be strings valid according to =key-valid-p=. - This changes affects the keys =consult-narrow-key=, =consult-widen-key=, - =consult-preview-key= and the =:preview-key= of sources and passed as keyword - argument to =consult--read=. See the example configurations in the manual. -- BREAKING: Remove the "." argument from =consult-grep-args= and - =consult-ripgrep-args=, since directories or files to search are appended by the - command line builder. Take this change into account, when you use a customized - version of those variables. -- =consult-grep=: Add support for grep and find over multiple files or directory. - If the prefix argument DIR is a single C-u, prompt for comma separated - directories or files to search recursively via =completing-read-multiple=. -- =consult-buffer= and =consult-isearch-history=: Align annotations dynamically - depending on candidate width, instead of computing the alignment beforehand. -- Add the full path as =help-echo= property to abbreviated directory paths and - project names. Enable =tooltip-mode= and hover with the mouse over the - abbreviated directory path to see the full path. -- =consult-grep/find/etc=: Print first line of stderr output if command failed. - -* Version 0.32 (2023-02-06) - -- Bugfixes -- Deprecate the old key convention. Keys must now be strings valid according to - =key-valid-p=. This changes affects the keys =consult-narrow-key=, - =consult-widen-key=, =consult-preview-key= and the =:preview-key= of sources and - passed as keyword argument to =consult--read=. See the example configurations in - the manual. -- Add =consult-info= command (#634, #727). -- =consult-buffer=: Always select the first candidate when narrowing (#714). -- =consult-locate-args=: Remove =--existing=, which is not supported by =plocate= on - Debian stable. -- =consult-ripgrep-args=: Add =--search-zip= option to automatically search through - compressed files. This will allow you to search Elisp files bundled with your - Emacs installation. Move to an Elisp library via =find-library=, then invoke - =consult-ripgrep=. -- Drop obsolete =consult-apropos=. Alternatives: =describe-symbol= in combination - with =embark-export=. See also =consult-info= and =consult-ripgrep= to search - through info manuals and Elisp source code. -- Drop obsolete =consult-multi-occur=. Alternative: Built-in =multi-occur=, - =multi-occur-in-matching-buffers= or =consult-line-multi=. -- Drop obsolete =consult-file-externally=. The command has been moved to Embark - under the name =embark-open-externally=. - -* Version 0.31 (2023-01-06) - -- Version bump to update the Compat package dependency (29.1.0.1) - -* Version 0.30 (2023-01-02) - -- Bugfixes -- Drop Selectrum support -- Deprecate =consult-file-externally= in favor of =embark-open-externally= -- Deprecate =consult-multi-occur=. The =multi-occur= command should be improved - upstream to take advantage of =completing-read-multiple=. Consult provides the - command =consult-line-multi= as an alternative. -- =consult-history=: Use input as initial completion input - -* Version 0.29 (2022-12-03) - -- Bugfixes -- =consult-line-multi= has been rewritten completely. The candidates are computed - on demand based on the input. This reduces startup speed greatly. The command - behaves like =consult-grep=, but operates on buffers instead of files. -- Add =consult--source-file-register=, and make the registers available in - =consult-buffer=. Registers are often used as quick access keys for files, e.g., - =(add-to-list 'register-alist '(?i file . "~/.emacs.d/init.el")))=. -- Remove obsolete =consult-line-point-placement= -- =consult-grep/find=: Always show directory in the prompt -- Add variable =consult-yank-rotate=, =consult-yank-from-kill-ring= rotates kill ring -- Emacs 29: =consult-register= supports =buffer= register type -- Emacs 29: Support =outline-search-function= -- Org 9.6: Support new =org-fold-core= API (both overlays and text-properties) -- Support abbreviated file names in =recentf-list=, see =recentf-filename-handler=. -- Deprecate =consult-apropos= - -* Version 0.20 (2022-10-16) - -- Bugfixes -- Allow =consult-*-args= to be a string, or a list of strings or expressions. -- Introduce face =consult-highlight-match= to highlight grep matches in the - completion buffer. -- Highlight full matches in =consult-line=, =consult-outline=, =consult-*grep= and - =consult-flymake=. -- Remove face =consult-preview-error=. -- Deprecate =consult-line-point-placement= in favor of more general - =consult-point-placement=, which is also used by the =consult-*grep= commands. -- =consult-imenu=: Support imenu-after-jump-hook and non-default - =imenu-default-goto-function= -- =consult-history=: Add support for history index variables, which are updated - after selection. -- Deprecate support for Selectrum in favor of Vertico. If you use Selectrum - consider switching to Vertico, Icomplete, Mct or default completion. - -* Version 0.19 (2022-09-09) - -- Bugfixes -- Allow =consult-flymake= to work across all buffers in a project -- Remove deprecated =consult-completing-read-multiple= -- =consult-grep/git-grep/ripgrep=: Add =--fixed-strings= support -- =consult-grep=: Respect =grep-find-ignored-directories/files= -- =consult-org-heading=: Add tags to completion candidates -- Add =consult-preview-excluded-files= -- =consult-themes=: Support regexps - -* Version 0.18 (2022-05-25) - -- Bugfixes -- Removed obsolete =consult-recent-file-filter= and =consult-preview-excluded-hooks= -- Deprecate =consult-completing-read-multiple=. See #567 for details. -- Add =consult--source-modified-buffer= - -* Version 0.17 (2022-04-22) - -- Bugfixes -- Drop Emacs 26 support. -- =consult-goto-line=: Use =goto-line-history= on Emacs 28. -- =consult-customize=: Evaluate settings at runtime. This change makes it possible - to use =thing-at-point= to overwrite the =:initial= and =:add-history= settings. -- Rename =consult--read-config= to =consult--customize-alist= and change the format. - The configuration is an alist. The car must be a command symbol. The cdr must - be a plist of keys and expressions, where the expressions evaluate to the - actual configuration values. -- Mode hooks in previewed file buffers are delayed. The buffer is only fully - initialized when leaving the minibuffer for recursive editing. -- Increase =consult-preview-raw-size=. -- Replace =consult-preview-excluded-hooks= by =consult-preview-allowed-hooks=. -- Add =consult-preview-variables= to bind variables for file preview. -- BREAKING API CHANGE of =consult--read=, =consult--prompt=, =consult--multi=: The - state function protocol changed. The function gets notified of more completion - state changes. See the docstring of =consult--with-preview= for details. -- BREAKING API CHANGE of =consult--read=: The lookup function protocol changed. - The function must now accept four or more arguments. -- Remove unused =consult-preview-map=. -- Remove unnecessary =consult-recent-file-filter=. Use =recentf-exclude= instead. -- =consult--multi= sources can have a =:new= function to create candidates. - When narrowed to a source, new candidates will be created by calling the - respective =:new= function. -- =consult--multi= returns =:match= information. =:match= can be nil, t, or new, - depending on if the candidate does not exist, exists or has been created. -- =consult-locate= treats the input literally to take advantage of the db index. - -* Version 0.16 (2022-03-08) - -- Bugfixes -- Deprecate =consult-project-root-function= in favor of =consult-project-function=. -- Preconfigure =consult-project-function= with a default function based - on project.el. -- Add =consult-project-buffer=, a variant of =consult-buffer= restricted to the - current project. -- Add =consult-register-prefix= option. -- Introduced a generic and extensible =consult-register= implementation. -- Lazy marker creation in =consult-line/outline= (performance improvements) - -* Version 0.15 (2022-01-31) - -- Bugfixes -- =consult-xref=: Prettify the group titles, use =xref--group-name-for-display= - if available. -- =consult-focus-lines=: Thanks to @jdtsmith, the command is much faster and - actually useable in large files. -- Added Mct integration, auto refreshing of asynchronous Consult commands. - -* Version 0.14 (2021-12-31) - -- Bugfixes -- Add =consult-recent-file-filter= -- Rename =consult--source-(project-)file= to =consult-source-(project-)recent-file= -- =consult-keep-lines= makes read-only buffers temporarily writable if confirmed - -* Version 0.13 (2021-11-12) - -- Bugfixes -- =consult-register=: Add support for file register values. -- Rename =consult-isearch= to =consult-isearch-history=. The command is a history - browsing command and not a replacement for Isearch. -- =consult-grep= support -[ABC] grep options -- Add =consult-grep-context= face - -* Version 0.12 (2021-10-11) - -- Bugfixes -- Removed obsolete =consult-project-imenu= and =consult-x-command= variables -- =consult-grep=: Use ~--null~ argument to support file names with colons - -* Version 0.11 (2021-08-18) - -- Bugfixes only - -* Version 0.10 (2021-08-11) - -- =consult-mark=, =consult-global-mark=: Add optional marker list argument -- =consult-completing-read-multiple=: New function -- Rename =consult-project-imenu= to =consult-imenu-multi= -- Add =consult-line-multi= to search multiple buffers -- Removed obsolete =consult-yank=, =consult-async-default-split=, =consult-config= -- =consult-ripgrep=: Use =--smart-case= -- =consult-grep/git-grep=: Use =--ignore-case= -- Deprecate =consult--command= in favor of =consult--config.= -- =consult-find=: Use regular expressions instead of globbing/wildcards by default. - Due to the changes to =consult-find= it is not possible anymore to configure - =fd= as backend for =consult-find=. A replacement is documented in the wiki. -- =consult-find/locate/man=: Add highlighting to the matching file/man page names. -- =consult-grep/git-grep/ripgrep/find/locate=: Add support for multiple unordered - patterns. Each of the input patterns must be matched. For example, - =consult-find= transforms the input "first second third" to "first -and second - -and third". -- =consult-grep/git-grep/ripgrep=: Compute the highlighting based on the input, - instead of relying on the ANSI-escaped output. This works better with multiple - patterns, but may occasionally produce false highlighting. -- Deprecate =consult-x-command= configuration variables in favor of =consult-x-args=. - The variables have been renamed since the configuration format changed. -- =consult-async-split-styles-alist=: Remove the =space= splitting style, since - it has been obsoleted by the support for multiple unordered patterns. - -* Version 0.9 (2021-06-22) - -- Add =consult-preview-excluded-hooks= -- =consult--read/consult--prompt=: Add =:inherit-input-method= argument -- Add debouncing support for preview - -* Version 0.8 (2021-05-30) - -- Async commands: Do not fix vertical height in Selectrum. -- =consult-imenu=: Deduplicate items (some imenu backends generate duplicates). -- =consult-org-heading=: Deduplicate items. -- =consult-buffer-filter=: Hide more buffers. -- =consult-line=: Matching line preview overlay only in the selected window. -- =consult-yank/completion-in-region=: Insertion preview only in selected window. -- =consult-yank=: Rename to =consult-yank-from-kill-ring= (Emacs 28 naming). -- =consult-yank= commands: =delete-selection-mode= support, added properties. -- =consult-preview-at-point=, =consult-preview-at-point-mode=: New command and - minor mode to preview candidate at point in =*Completions*= buffer. -- Add =consult-async-split-style= and =consult-async-split-styles-alist=. -- =consult-async-default-split=: Obsoleted in favor of =consult-async-split-style=. -- Deprecate =consult-config= in favor of new =consult-customize= macro. -- =consult-buffer=: Enable previews for files and bookmarks by default. -- =consult-buffer=/=consult--multi=: Add support for =:preview-key= per source. -- =consult-buffer=: Push visible buffers down in the buffer list. -- =consult-flycheck=: Moved to separate repository prior to ELPA submission. -- Submitted Consult to ELPA. - -* Version 0.7 (2021-04-29) - -- Bugfixes -- =consult-buffer=: Respect =confirm-nonexistent-file-or-buffer= -- =consult-widen-key=: Change default setting to twice the =consult-narrow-key= -- =consult-flycheck=: Sort errors first -- Added support for the Vertico completion system -- Consult adds disambiguation suffixes as suffix instead of as prefix now - for the commands =consult-line=, =consult-buffer=, etc. - This enables support for the =basic= completion style and TAB completion. -- =consult--read=: The =:title= function must accept two arguments now, - the candidate string and a flag. If the flag is nil, the function should - return the title of the candidate, otherwise the function should return the - transformed candidate. -- =consult-grep= and related commands: Strip the file name if grouping is used. -- =consult-find/grep=: Ensure that the commands work with Tramp -- =consult-outline=: Add narrowing -- Added =consult-org-heading= and =consult-org-agenda= -- =consult-line=: Highlight visual line during jump preview -- =consult-line=: Start search at current line, add configuration variable - =consult-start-from-top=. The starting point can be toggled by the prefix - argument =C-u=. - -* Version 0.6 (2021-03-02) - -- Bugfixes -- =consult-keep/focus-lines=: Align behavior on regions with built-in =keep-lines=. -- =consult-buffer=: Enable file sources only when =recentf-mode= is enabled -- =consult--multi=: Add =:default= flag, use flag for =consult--source-buffer= -- Add =consult-grep-max-columns= to prevent performance issues for long lines -- Add =consult-fontify-preserve= customization variable -- =consult-line=: Quits Isearch, when started from an Isearch session -- =consult-register-load=: Align prefix argument handling with =insert-register= -- Rename =consult-error= to =consult-compile-error= -- =consult-compile-error=: Allow calling the command from any buffer, - use the errors from all compilation buffers related to the current buffer. -- =consult-man=: Handle aggregated entries returned by mandoc -- =consult-completion-in-region=: Added preview and =consult-preview-region= face -- Added =consult-completion-in-region-styles= customization variable -- Added =consult-xref=. The function can be set as =xref-show-xrefs-function= - and =xref-show-definitions-function=. -- Added support for the candidate grouping function =x-group-function= - -* Version 0.5 (2021-02-09) - -- Bugfixes -- =consult-keep/focus-lines=: If region is active, operate only on the region. -- =consult-register-format=: Do not truncate register strings. -- =consult-buffer= multi sources: Ensure that original buffer is - shown, when the currently selected source does not perform preview. -- Add =consult-preview-raw-size= -- Expose preview functionality for multi-source bookmarks/files -- Multi sources: Add =:enabled=, =:state= and =:action= fields -- =consult-imenu=: Add faces depending on item types - -* Version 0.4 (2021-02-01) - -- Bugfixes -- Introduce multi sources, reimplement =consult-buffer= with multi sources -- =consult-isearch=: Add preview highlighting -- =consult-line=: Use =isearch-string= when invoked from running isearch - -* Version 0.3 (2021-01-28) - -- Bugfixes -- New command =consult-isearch= -- New functions =consult-register-format=, =consult-register-window=, - removed =consult-register-preview= - -* Version 0.2 (2021-01-16) - -- Initial stable release blob - 086f993074dc1f9bc5f978b28586a1bd203cf3e6 (mode 644) blob + /dev/null --- elpa/consult-1.5/README-elpa +++ /dev/null @@ -1,1332 +0,0 @@ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - CONSULT.EL - CONSULTING COMPLETING-READ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - -Consult provides search and navigation commands based on the Emacs -completion function [completing-read]. Completion allows you to quickly -select an item from a list of candidates. Consult offers asynchronous -and interactive `consult-grep' and `consult-ripgrep' commands, and the -line-based search command `consult-line'. Furthermore Consult provides -an advanced buffer switching command `consult-buffer' to switch between -buffers, recently opened files, bookmarks and buffer-like candidates -from other sources. Some of the Consult commands are enhanced versions -of built-in Emacs commands. For example the command `consult-imenu' -presents a flat list of the Imenu with [live preview], [grouping and -narrowing]. Please take a look at the [full list of commands]. - -Consult is fully compatible with completion systems centered around the -standard Emacs `completing-read' API, notably the default completion -system, [Vertico], [Mct], and [Icomplete]. - -This package keeps the completion system specifics to a minimum. The -ability of the Consult commands to work well with arbitrary completion -systems is one of the main advantages of the package. Consult fits well -into existing setups and it helps you to create a full completion -environment out of small and independent components. - -You can combine the complementary packages [Marginalia], [Embark] and -[Orderless] with Consult. Marginalia enriches the completion display -with annotations, e.g., documentation strings or file information. The -versatile Embark package provides local actions, comparable to a context -menu. These actions operate on the selected candidate in the minibuffer -or at point in normal buffers. For example, when selecting from a list -of files, Embark offers an action to delete the file. Additionally -Embark offers a facility to collect completion candidates in a collect -buffer. The section [Embark integration] documents in detail how Consult -and Embark work together. - -Table of Contents -───────────────── - -1. Available commands -.. 1. Virtual Buffers -.. 2. Editing -.. 3. Register -.. 4. Navigation -.. 5. Search -.. 6. Grep and Find -.. 7. Compilation -.. 8. Histories -.. 9. Modes -.. 10. Org Mode -.. 11. Help -.. 12. Miscellaneous -2. Special features -.. 1. Live previews -.. 2. Narrowing and grouping -.. 3. Asynchronous search -.. 4. Multiple sources -.. 5. Embark integration -3. Configuration -.. 1. Use-package example -.. 2. Custom variables -.. 3. Fine-tuning -4. Recommended packages -5. Bug reports -6. Contributions -7. Acknowledgments -8. Indices -.. 1. Function index -.. 2. Concept index - - -[completing-read] - - -[live preview] See section 2.1 - -[grouping and narrowing] See section 2.2 - -[full list of commands] See section 1 - -[Vertico] - -[Mct] - -[Icomplete] - - -[Marginalia] - -[Embark] - -[Orderless] - -[Embark integration] See section 2.5 - - -1 Available commands -════════════════════ - - Most Consult commands follow the meaningful naming scheme - `consult-'. Many commands implement a little known but - convenient Emacs feature called "future history", which guesses what - input the user wants. At a command prompt type `M-n' and typically - Consult will insert the symbol or thing at point into the input. - - *TIP:* If you have [Marginalia] annotators activated, type `M-x - ^consult' to see all Consult commands with their abbreviated - description. Alternatively, type `C-h a ^consult' to get an overview - of all Consult variables and functions with their descriptions. - - -[Marginalia] - -1.1 Virtual Buffers -─────────────────── - - • `consult-buffer': Enhanced version of `switch-to-buffer' with - support for virtual buffers. Supports live preview of buffers and - narrowing to the virtual buffer types. You can type `f SPC' in order - to narrow to recent files. Press `SPC' to show ephemeral - buffers. Supported narrowing keys: - • b Buffers - • SPC Hidden buffers - • * Modified buffers - • f Files (Requires `recentf-mode') - • r File registers - • m Bookmarks - • p Project - • Custom [other sources] configured in `consult-buffer-sources'. - • `consult-buffer-other-window', `consult-buffer-other-frame', - `consult-buffer-other-tab': Variants of `consult-buffer'. - • `consult-project-buffer': Variant of `consult-buffer' restricted to - buffers and recent files of the current project. You can add custom - sources to `consult-project-buffer-sources'. The command may prompt - you for a project if you invoke it from outside a project. - • `consult-bookmark': Select or create bookmark. To select bookmarks - you might use the `consult-buffer' as an alternative, which can - include a bookmark virtual buffer source. Note that - `consult-bookmark' supports preview of bookmarks and narrowing. - • `consult-recent-file': Select from recent files with preview. You - might prefer the powerful `consult-buffer' instead, which can - include recent files as a virtual buffer source. The `recentf-mode' - enables tracking of recent files. - - -[other sources] See section 2.4 - - -1.2 Editing -─────────── - - • `consult-yank-from-kill-ring': Enhanced version of `yank' to select - an item from the `kill-ring'. The selected text previewed as overlay - in the buffer. - • `consult-yank-pop': Enhanced version of `yank-pop' with - DWIM-behavior, which either replaces the last `yank' by cycling - through the `kill-ring', or if there has not been a last `yank' - consults the `kill-ring'. The selected text previewed as overlay in - the buffer. - • `consult-yank-replace': Like `consult-yank-pop', but always replaces - the last `yank' with an item from the `kill-ring'. - • `consult-kmacro': Select macro from the macro ring and execute it. - - -1.3 Register -──────────── - - • `consult-register': Select from list of registers. The command - supports narrowing to register types and preview of marker - positions. This command is useful to search the register - contents. For quick access use the commands `consult-register-load', - `consult-register-store' or the built-in Emacs register commands. - • `consult-register-format': Set `register-preview-function' to this - function for an enhanced register formatting. See the [example - configuration]. - • `consult-register-window': Replace `register-preview' with this - function for a better register window. See the [example - configuration]. - • `consult-register-load': Utility command to quickly load a register. - The command either jumps to the register value or inserts it. - • `consult-register-store': Improved UI to store registers depending - on the current context with an action menu. With an active region, - store/append/prepend the contents, optionally deleting the region - when a prefix argument is given. With a numeric prefix argument, - store/add the number. Otherwise store point, frameset, window or - kmacro. Usage examples: - ‣ `M-' x': If no region is active, store point in register `x'. If - a region is active, store the region in register `x'. - ‣ `M-' M-w x': Store window configuration in register `x'. - ‣ `C-u 100 M-' x': Store number in register `x'. - - -[example configuration] See section 3.1 - - -1.4 Navigation -────────────── - - • `consult-goto-line': Jump to line number enhanced with live - preview. This is a drop-in replacement for `goto-line'. Enter a line - number to jump to the first column of the given line. Alternatively - enter `line:column' in order to jump to a specific column. - • `consult-mark': Jump to a marker in the `mark-ring'. Supports live - preview and recursive editing. - • `consult-global-mark': Jump to a marker in the `global-mark-ring'. - Supports live preview and recursive editing. - • `consult-outline': Jump to a heading of the outline. Supports - narrowing to a heading level, live preview and recursive editing. - • `consult-imenu': Jump to imenu item in the current buffer. Supports - live preview, recursive editing and narrowing. - • `consult-imenu-multi': Jump to imenu item in project buffers, with - the same major mode as the current buffer. Supports live preview, - recursive editing and narrowing. This feature has been inspired by - [imenu-anywhere]. - - -[imenu-anywhere] - - -1.5 Search -────────── - - • `consult-line': Enter search string and select from matching lines. - Supports live preview and recursive editing. The symbol at point and - the recent Isearch string are added to the "future history" and can - be accessed by pressing `M-n'. When `consult-line' is bound to the - `isearch-mode-map' and is invoked during a running Isearch, it will - use the current Isearch string. - • `consult-line-multi': Search dynamically across multiple buffers. By - default search across project buffers. If invoked with a prefix - argument search across all buffers. The candidates are computed on - demand based on the input. The command behaves like `consult-grep', - but operates on buffers instead of files. - • `consult-keep-lines': Replacement for `keep/flush-lines' which uses - the current completion style for filtering the buffer. The function - updates the buffer while typing. In particular `consult-keep-lines' - can narrow down an exported Embark collect buffer further, relying - on the same completion filtering as `completing-read'. If the input - begins with the negation operator, i.e., `! SPC', the filter matches - the complement. If a region is active, the region restricts the - filtering. - • `consult-focus-lines': Temporarily hide lines by filtering them - using the current completion style. Call with `C-u' prefix argument - in order to show the hidden lines again. If the input begins with - the negation operator, i.e., `! SPC', the filter matches the - complement. In contrast to `consult-keep-lines' this function does - not edit the buffer. If a region is active, the region restricts the - filtering. - - -1.6 Grep and Find -───────────────── - - • `consult-grep', `consult-ripgrep', `consult-git-grep': Search for - regular expression in files. Consult invokes Grep asynchronously, - while you enter the search term. After at least - `consult-async-min-input' characters, the search gets - started. Consult splits the input string into two parts, if the - first character is a punctuation character, like `#'. For example - `#regexps#filter-string', is split at the second `#'. The string - `regexps' is passed to Grep. Note that Consult transforms Emacs - regular expressions to expressions understand by the search - program. Always use Emacs regular expressions at the prompt. If you - enter multiple regular expressions separated by space only lines - matching all regular expressions are shown. In order to match space - literally, escape the space with a backslash. The `filter-string' is - passed to the /fast/ Emacs filtering to further narrow down the list - of matches. This is particularly useful if you are using an advanced - completion style like orderless. `consult-grep' supports preview. If - the `consult-project-function' returns non-nil, `consult-grep' - searches the current project directory. Otherwise the - `default-directory' is searched. If `consult-grep' is invoked with - prefix argument `C-u M-s g', you can specify one or more - comma-separated files and directories manually. - • `consult-find', `consult-fd', `consult-locate': Find file by - matching the path against a regexp. Like for `consult-grep', either - the project root or the current directory is the root directory for - the search. The input string is treated similarly to `consult-grep', - where the first part is passed to find, and the second part is used - for Emacs filtering. Prefix arguments to `consult-find' work just - like those for the consult grep commands. - - -1.7 Compilation -─────────────── - - • `consult-compile-error': Jump to a compilation error. Supports live - preview narrowing and recursive editing. - • `consult-flymake': Jump to Flymake diagnostic. Supports live preview - and recursive editing. The command supports narrowing. Press `e - SPC', `w SPC', `n SPC' to only show errors, warnings and notes - respectively. - • `consult-xref': Integration with xref. This function can be set as - `xref-show-xrefs-function' and `xref-show-definitions-function'. - - -1.8 Histories -───────────── - - • `consult-complex-command': Select a command from the - `command-history'. This command is a `completing-read' version of - `repeat-complex-command' and is also a replacement for the - `command-history' command from chistory.el. - • `consult-history': Insert a string from the current buffer history, - for example the Eshell or Comint history. You can also invoke this - command from the minibuffer. In that case `consult-history' uses the - history stored in the `minibuffer-history-variable'. If you prefer - `completion-at-point', take a look at `cape-history' from the [Cape] - package. - • `consult-isearch-history': During an Isearch session, this command - picks a search string from history and continues the search with the - newly selected string. Outside of Isearch, the command allows you to - pick a string from the history and starts a new - Isearch. `consult-isearch-history' acts as a drop-in replacement for - `isearch-edit-string'. - - -[Cape] - - -1.9 Modes -───────── - - • `consult-minor-mode-menu': Enable/disable minor mode. Supports - narrowing to on/off/local/global modes by pressing `i/o/l/g SPC' - respectively. - • `consult-mode-command': Run a command from the currently active - minor or major modes. Supports narrowing to - local-minor/global-minor/major mode via the keys `l/g/m'. - - -1.10 Org Mode -───────────── - - • `consult-org-heading': Variant of `consult-imenu' or - `consult-outline' for Org buffers. The headline and its ancestors - headlines are separated by slashes. Supports narrowing by heading - level, priority and TODO keyword, as well as live preview and - recursive editing. - • `consult-org-agenda': Jump to an Org agenda heading. Supports - narrowing by heading level, priority and TODO keyword, as well as - live preview and recursive editing. - - -1.11 Help -───────── - - • `consult-man': Find Unix man page, via Unix `apropos' or `man - -k'. `consult-man' opens the selected man page using the Emacs `man' - command. - • `consult-info': Full text search through info pages. If the command - is invoked from within an `*info*' buffer, it will search through - the current manual. You may want to create your own commands which - search through a predefined set of info pages, for example: - ┌──── - │ (defun consult-info-emacs () - │ "Search through Emacs info pages." - │ (interactive) - │ (consult-info "emacs" "efaq" "elisp" "cl" "compat")) - │ - │ (defun consult-info-org () - │ "Search through the Org info page." - │ (interactive) - │ (consult-info "org")) - │ - │ (defun consult-info-completion () - │ "Search through completion info pages." - │ (interactive) - │ (consult-info "vertico" "consult" "marginalia" "orderless" "embark" - │ "corfu" "cape" "tempel")) - └──── - - -1.12 Miscellaneous -────────────────── - - • `consult-theme': Select a theme and disable all currently enabled - themes. Supports live preview of the theme while scrolling through - the candidates. - • `consult-preview-at-point' and `consult-preview-at-point-mode': - Command and minor mode which previews the candidate at point in the - `*Completions*' buffer. This mode is relevant if you use [Mct] or - the default `*Completions*' UI. - • `consult-completion-in-region': In case you don't use [Corfu] as - your in-buffer completion UI, this function can be set as - `completion-in-region-function'. Then your minibuffer completion UI - (e.g., Vertico or Icomplete) will be used for `completion-at-point'. - ┌──── - │ ;; Use `consult-completion-in-region' if Vertico is enabled. - │ ;; Otherwise use the default `completion--in-region' function. - │ (setq completion-in-region-function - │ (lambda (&rest args) - │ (apply (if vertico-mode - │ #'consult-completion-in-region - │ #'completion--in-region) - │ args))) - └──── - Instead of `consult-completion-in-region', you may prefer to see the - completions directly in the buffer as a small popup. In that case, I - recommend the [Corfu] package. There is a technical limitation of - `consult-completion-in-region' in combination with the Lsp - modes. The Lsp server relies on the input at point, in order to - generate refined candidate strings. Since the completion is - transferred from the original buffer to the minibuffer, the server - does not receive the updated input. In contrast, in-buffer Lsp - completion for example via Corfu works properly since the completion - takes place directly in the original buffer. - - -[Mct] - -[Corfu] - - -2 Special features -══════════════════ - - Consult enhances `completing-read' with live previews of candidates, - additional narrowing capabilities to candidate groups and - asynchronously generated candidate lists. The internal `consult--read' - function, which is used by most Consult commands, is a thin wrapper - around `completing-read' and provides the special functionality. In - order to support multiple candidate sources there exists the - high-level function `consult--multi'. The architecture of Consult - allows it to work with different completion systems in the backend, - while still offering advanced features. - - -2.1 Live previews -───────────────── - - Some Consult commands support live previews. For example when you - scroll through the items of `consult-line', the buffer will scroll to - the corresponding position. It is possible to jump back and forth - between the minibuffer and the buffer to perform recursive editing - while the search is ongoing. - - Consult enables previews by default. You can disable them by adjusting - the `consult-preview-key' variable. Furthermore it is possible to - specify keybindings which trigger the preview manually as shown in the - [example configuration]. The default setting of `consult-preview-key' - is `any' which means that Consult triggers the preview /immediately/ - on any key press when the selected candidate changes. You can - configure each command individually with its own `:preview-key'. The - following settings are possible: - - • Automatic and immediate `'any' - • Automatic and delayed `(list :debounce 0.5 'any)' - • Manual and immediate `"M-."' - • Manual and delayed `(list :debounce 0.5 "M-.")' - • Disabled `nil' - - A safe recommendation is to leave automatic immediate previews enabled - in general and disable the automatic preview only for commands where - the preview may be expensive due to file loading. Internally, Consult - uses the value of `this-command' to determine the `:preview-key' - customized. This means that if you wrap a `consult-*' command within - your own function or command, you will also need to add the name of - /your custom command/ to the `consult-customize' call in order for it - to be considered. - - ┌──── - │ (consult-customize - │ consult-ripgrep consult-git-grep consult-grep - │ consult-bookmark consult-recent-file consult-xref - │ consult--source-bookmark consult--source-file-register - │ consult--source-recent-file consult--source-project-recent-file - │ ;; my/command-wrapping-consult ;; disable auto previews inside my command - │ :preview-key '(:debounce 0.4 any) ;; Option 1: Delay preview - │ ;; :preview-key "M-.") ;; Option 2: Manual preview - └──── - - In this case one may wonder what the difference is between using an - Embark action on the current candidate in comparison to a manually - triggered preview. The main difference is that the files opened by - manual preview are closed again after the completion session. During - preview some functionality is disabled to improve the performance, see - for example the customization variables `consult-preview-variables' - and `consult-preview-allowed-hooks'. Only the hooks listed in - `consult-preview-allowed-hooks' are executed when a file is opened - (`find-file-hook'). In order to enable additional font locking during - preview, add the corresponding hooks to the allow list. The following - code demonstrates this for [org-modern] and [hl-todo]. - - ┌──── - │ (add-to-list 'consult-preview-allowed-hooks 'global-org-modern-mode-check-buffers) - │ (add-to-list 'consult-preview-allowed-hooks 'global-hl-todo-mode-check-buffers) - └──── - - Files larger than `consult-preview-partial-size' are previewed - partially. Delaying the preview is also useful for `consult-theme', - since the theme preview is slow. The delay results in a smoother UI - experience. - - ┌──── - │ ;; Preview on any key press, but delay 0.5s - │ (consult-customize consult-theme :preview-key '(:debounce 0.5 any)) - │ ;; Preview immediately on M-., on up/down after 0.5s, on any other key after 1s - │ (consult-customize consult-theme - │ :preview-key - │ '("M-." - │ :debounce 0.5 "" "" - │ :debounce 1 any)) - └──── - - -[example configuration] See section 3.1 - -[org-modern] - -[hl-todo] - - -2.2 Narrowing and grouping -────────────────────────── - - Consult has special support for candidate groups. If the completion UI - supports the grouping functionality, the UI separates the groups with - thin lines and shows group titles. Grouping is useful if the list of - candidates consists of candidates of multiple types or candidates from - [multiple sources], like the `consult-buffer' command, which shows - both buffers and recently opened files. Note that you can disable the - group titles by setting the `:group' property of the corresponding - command to nil using the `consult-customize' macro. - - By entering a narrowing prefix or by pressing a narrowing key it is - possible to restrict the completion candidates to a certain candidate - group. When you use the `consult-buffer' command, you can enter the - prefix `b SPC' to restrict list of candidates to buffers only. If you - press `DEL' afterwards, the full candidate list will be shown - again. Furthermore a narrowing prefix key and a widening key can be - configured which can be pressed to achieve the same effect, see the - configuration variables `consult-narrow-key' and `consult-widen-key'. - - After pressing `consult-narrow-key', the possible narrowing keys can - be shown by pressing `C-h'. When pressing `C-h' after some prefix key, - the `prefix-help-command' is invoked, which shows the keybinding help - window by default. As a more compact alternative, there is the - `consult-narrow-help' command which can be bound to a key, for example - `?' or `C-h' in the `consult-narrow-map', as shown in the [example - configuration]. If [which-key] is installed, the narrowing keys are - automatically shown in the which-key window after pressing the - `consult-narrow-key'. - - -[multiple sources] See section 2.4 - -[example configuration] See section 3.1 - -[which-key] - - -2.3 Asynchronous search -─────────────────────── - - Consult has support for asynchronous generation of candidate - lists. This feature is used for search commands like `consult-grep', - where the list of matches is generated dynamically while the user is - typing a regular expression. The grep process is executed in the - background. When modifying the regular expression, the background - process is terminated and a new process is started with the modified - regular expression. - - The matches, which have been found, can then be narrowed using the - installed Emacs completion-style. This can be powerful if you are - using for example the `orderless' completion style. - - This two-level filtering is possible by splitting the input - string. Part of the input string is treated as input to grep and part - of the input is used for filtering. There are multiple splitting - styles available, configured in `consult-async-split-styles-alist': - `nil', `comma', `semicolon' and `perl'. The default splitting style is - configured with the variable `consult-async-split-style'. - - With the `comma' and `semicolon' splitting styles, the first word - before the comma or semicolon is passed to grep, the remaining string - is used for filtering. The `nil' splitting style does not perform any - splitting, the whole input is passed to grep. - - The `perl' splitting style splits the input string at a punctuation - character, using a similar syntax as Perl regular expressions. - - Examples: - - • `#defun': Search for "defun" using grep. - • `#consult embark': Search for both "consult" and "embark" using grep - in any order. - • `#first.*second': Search for "first" followed by "second" using - grep. - • `#\(consult\|embark\)': Search for "consult" or "embark" using - grep. Note the usage of Emacs-style regular expressions. - • `#defun#consult': Search for "defun" using grep, filter with the - word "consult". - • `/defun/consult': It is also possible to use other punctuation - characters. - • `#to#': Force searching for "to" using grep, since the grep pattern - must be longer than `consult-async-min-input' characters by default. - • `#defun -- --invert-match#': Pass argument `--invert-match' to grep. - - Asynchronous processes like `find' and `grep' create an error log - buffer `_*consult-async*' (note the leading space), which is useful - for troubleshooting. The prompt has a small indicator showing the - process status: - - • `:' the usual prompt colon, before input is provided. - • `*' with warning face, the process is running. - • `:' with success face, success, process exited with an error code of - zero. - • `!' with error face, failure, process exited with a nonzero error - code. - • `;' with error face, interrupted, for example if more input is - provided. - - -2.4 Multiple sources -──────────────────── - - Multiple synchronous candidate sources can be combined. This feature - is used by the `consult-buffer' command to present buffer-like - candidates in a single menu for quick access. By default - `consult-buffer' includes buffers, bookmarks, recent files and - project-specific buffers and files. It is possible to configure the - list of sources via the `consult-buffer-sources' variable. Arbitrary - custom sources can be defined. - - As an example, the bookmark source is defined as follows: - - ┌──── - │ (defvar consult--source-bookmark - │ `(:name "Bookmark" - │ :narrow ?m - │ :category bookmark - │ :face consult-bookmark - │ :history bookmark-history - │ :items ,#'bookmark-all-names - │ :action ,#'consult--bookmark-action)) - └──── - - Required source fields: - • `:category' Completion category. - • `:items' List of strings to select from or function returning list - of strings. A list of cons cells is not supported. - - Optional source fields: - • `:name' Name of the source, used for narrowing, group titles and - annotations. - • `:narrow' Narrowing character or `(character . string)' pair. - • `:preview-key' Preview key or keys which trigger preview. - • `:enabled' Function which must return t if the source is enabled. - • `:hidden' When t candidates of this source are hidden by default. - • `:face' Face used for highlighting the candidates. - • `:annotate' Annotation function called for each candidate, returns - string. - • `:history' Name of history variable to add selected candidate. - • `:default' Must be t if the first item of the source is the default - value. - • `:action' Function called with the selected candidate. - • `:new' Function called with new candidate name, only if - `:require-match' is nil. - • `:state' State constructor for the source, must return the state - function. - • Other source fields can be added specifically to the use case. - - The `:state' and `:action' fields of the sources deserve a longer - explanation. The `:action' function takes a single argument and is - only called after selection with the selected candidate, if the - selection has not been aborted. This functionality is provided for - convenience and easy definition of sources. The `:state' field is more - general. The `:state' function is a constructor function without - arguments, which can perform some setup necessary for the preview. It - must return a closure which takes an ACTION and a CANDIDATE - argument. See the docstring of `consult--with-preview' for more - details about the ACTION argument. - - By default, `consult-buffer' previews buffers, bookmarks and - files. Loading recent files or bookmarks can result in expensive - operations. However it is possible to configure a manual preview as - follows. - - ┌──── - │ (consult-customize - │ consult--source-bookmark consult--source-file-register - │ consult--source-recent-file consult--source-project-recent-file - │ :preview-key "M-.") - └──── - - Sources can be added directly to the `consult-buffer-source' list for - convenience. For example, the following source lists all Org buffers - and lets you create new ones. - - ┌──── - │ (defvar org-source - │ (list :name "Org Buffer" - │ :category 'buffer - │ :narrow ?o - │ :face 'consult-buffer - │ :history 'buffer-name-history - │ :state #'consult--buffer-state - │ :new - │ (lambda (name) - │ (with-current-buffer (get-buffer-create name) - │ (insert "#+title: " name "\n\n") - │ (org-mode) - │ (consult--buffer-action (current-buffer)))) - │ :items - │ (lambda () - │ (consult--buffer-query :mode 'org-mode :as #'consult--buffer-pair)))) - │ - │ (add-to-list 'consult-buffer-sources 'org-source 'append) - └──── - - One can create similar sources for other major modes. See the [Consult - wiki] for many additional source examples. See also the documentation - of `consult-buffer' and of the internal `consult--multi' API. The - function `consult--multi' can be used to create new multi-source - commands. - - -[Consult wiki] - - -2.5 Embark integration -────────────────────── - - *NOTE*: Install the `embark-consult' package from MELPA, which - provides Consult-specific Embark actions and the Occur buffer export. - - Embark is a versatile package which offers context dependent actions, - comparable to a context menu. See the [Embark manual] for an extensive - description of its capabilities. - - Actions are commands which can operate on the currently selected - candidate (or target in Embark terminology). When completing files, - for example the `delete-file' command is offered. With Embark you can - execute arbitrary commands on the currently selected candidate via - `M-x'. - - Furthermore Embark provides the `embark-collect' command, which - collects candidates and presents them in an Embark collect buffer, - where further actions can be applied to them. A related feature is the - `embark-export' command, which exports candidate lists to a buffer of - a special type. For example in the case of file completion, a Dired - buffer is opened. - - In the context of Consult, particularly exciting is the possibility to - export the matching lines from `consult-line', `consult-outline', - `consult-mark' and `consult-global-mark'. The matching lines are - exported to an Occur buffer where they can be edited via the - `occur-edit-mode' (press key `e'). Similarly, Embark supports - exporting the matches found by `consult-grep', `consult-ripgrep' and - `consult-git-grep' to a Grep buffer, where the matches across files - can be edited, if the [wgrep] package is installed. These three - workflows are symmetric. - - ⁃ `consult-line' -> `embark-export' to `occur-mode' buffer -> - `occur-edit-mode' for editing of matches in buffer. - ⁃ `consult-grep' -> `embark-export' to `grep-mode' buffer -> `wgrep' - for editing of all matches. - ⁃ `consult-find' -> `embark-export' to `dired-mode' buffer -> - `wdired-change-to-wdired-mode' for editing. - - -[Embark manual] - -[wgrep] - - -3 Configuration -═══════════════ - - Consult can be installed from [ELPA] or [MELPA] via the Emacs built-in - package manager. Alternatively it can be directly installed from the - development repository via other non-standard package managers. - - There is the [Consult wiki], where additional configuration examples - can be contributed. - - *IMPORTANT:* It is recommended that you enable [lexical binding] in - your configuration. Many Consult-related code snippets require lexical - binding, since they use lambdas and closures. - - -[ELPA] - -[MELPA] - -[Consult wiki] - -[lexical binding] - - -3.1 Use-package example -─────────────────────── - - The Consult package only provides commands and does not add any - keybindings or modes. Therefore the package is non-intrusive but - requires a little setup effort. In order to use the Consult commands, - it is advised to add keybindings for commands which are accessed - often. Rarely used commands can be invoked via `M-x'. Feel free to - only bind the commands you consider useful to your workflow. The - configuration shown here relies on the `use-package' macro, which is a - convenient tool to manage package configurations. - - *NOTE:* There is the [Consult wiki], where you can contribute - additional configuration examples. - - ┌──── - │ ;; Example configuration for Consult - │ (use-package consult - │ ;; Replace bindings. Lazily loaded due by `use-package'. - │ :bind (;; C-c bindings in `mode-specific-map' - │ ("C-c M-x" . consult-mode-command) - │ ("C-c h" . consult-history) - │ ("C-c k" . consult-kmacro) - │ ("C-c m" . consult-man) - │ ("C-c i" . consult-info) - │ ([remap Info-search] . consult-info) - │ ;; C-x bindings in `ctl-x-map' - │ ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command - │ ("C-x b" . consult-buffer) ;; orig. switch-to-buffer - │ ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window - │ ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame - │ ("C-x t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab - │ ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump - │ ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer - │ ;; Custom M-# bindings for fast register access - │ ("M-#" . consult-register-load) - │ ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated) - │ ("C-M-#" . consult-register) - │ ;; Other custom bindings - │ ("M-y" . consult-yank-pop) ;; orig. yank-pop - │ ;; M-g bindings in `goto-map' - │ ("M-g e" . consult-compile-error) - │ ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck - │ ("M-g g" . consult-goto-line) ;; orig. goto-line - │ ("M-g M-g" . consult-goto-line) ;; orig. goto-line - │ ("M-g o" . consult-outline) ;; Alternative: consult-org-heading - │ ("M-g m" . consult-mark) - │ ("M-g k" . consult-global-mark) - │ ("M-g i" . consult-imenu) - │ ("M-g I" . consult-imenu-multi) - │ ;; M-s bindings in `search-map' - │ ("M-s d" . consult-find) ;; Alternative: consult-fd - │ ("M-s c" . consult-locate) - │ ("M-s g" . consult-grep) - │ ("M-s G" . consult-git-grep) - │ ("M-s r" . consult-ripgrep) - │ ("M-s l" . consult-line) - │ ("M-s L" . consult-line-multi) - │ ("M-s k" . consult-keep-lines) - │ ("M-s u" . consult-focus-lines) - │ ;; Isearch integration - │ ("M-s e" . consult-isearch-history) - │ :map isearch-mode-map - │ ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string - │ ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string - │ ("M-s l" . consult-line) ;; needed by consult-line to detect isearch - │ ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch - │ ;; Minibuffer history - │ :map minibuffer-local-map - │ ("M-s" . consult-history) ;; orig. next-matching-history-element - │ ("M-r" . consult-history)) ;; orig. previous-matching-history-element - │ - │ ;; Enable automatic preview at point in the *Completions* buffer. This is - │ ;; relevant when you use the default completion UI. - │ :hook (completion-list-mode . consult-preview-at-point-mode) - │ - │ ;; The :init configuration is always executed (Not lazy) - │ :init - │ - │ ;; Optionally configure the register formatting. This improves the register - │ ;; preview for `consult-register', `consult-register-load', - │ ;; `consult-register-store' and the Emacs built-ins. - │ (setq register-preview-delay 0.5 - │ register-preview-function #'consult-register-format) - │ - │ ;; Optionally tweak the register preview window. - │ ;; This adds thin lines, sorting and hides the mode line of the window. - │ (advice-add #'register-preview :override #'consult-register-window) - │ - │ ;; Use Consult to select xref locations with preview - │ (setq xref-show-xrefs-function #'consult-xref - │ xref-show-definitions-function #'consult-xref) - │ - │ ;; Configure other variables and modes in the :config section, - │ ;; after lazily loading the package. - │ :config - │ - │ ;; Optionally configure preview. The default value - │ ;; is 'any, such that any key triggers the preview. - │ ;; (setq consult-preview-key 'any) - │ ;; (setq consult-preview-key "M-.") - │ ;; (setq consult-preview-key '("S-" "S-")) - │ ;; For some commands and buffer sources it is useful to configure the - │ ;; :preview-key on a per-command basis using the `consult-customize' macro. - │ (consult-customize - │ consult-theme :preview-key '(:debounce 0.2 any) - │ consult-ripgrep consult-git-grep consult-grep - │ consult-bookmark consult-recent-file consult-xref - │ consult--source-bookmark consult--source-file-register - │ consult--source-recent-file consult--source-project-recent-file - │ ;; :preview-key "M-." - │ :preview-key '(:debounce 0.4 any)) - │ - │ ;; Optionally configure the narrowing key. - │ ;; Both < and C-+ work reasonably well. - │ (setq consult-narrow-key "<") ;; "C-+" - │ - │ ;; Optionally make narrowing help available in the minibuffer. - │ ;; You may want to use `embark-prefix-help-command' or which-key instead. - │ ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help) - │ - │ ;; By default `consult-project-function' uses `project-root' from project.el. - │ ;; Optionally configure a different project root function. - │ ;;;; 1. project.el (the default) - │ ;; (setq consult-project-function #'consult--default-project--function) - │ ;;;; 2. vc.el (vc-root-dir) - │ ;; (setq consult-project-function (lambda (_) (vc-root-dir))) - │ ;;;; 3. locate-dominating-file - │ ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git"))) - │ ;;;; 4. projectile.el (projectile-project-root) - │ ;; (autoload 'projectile-project-root "projectile") - │ ;; (setq consult-project-function (lambda (_) (projectile-project-root))) - │ ;;;; 5. No project support - │ ;; (setq consult-project-function nil) - │ ) - └──── - - -[Consult wiki] - - -3.2 Custom variables -──────────────────── - - *TIP:* If you have [Marginalia] installed, type `M-x - customize-variable RET ^consult' to see all Consult-specific - customizable variables with their current values and abbreviated - description. Alternatively, type `C-h a ^consult' to get an overview - of all Consult variables and functions with their descriptions. - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - Variable Description - ─────────────────────────────────────────────────────────────────────────────────────── - consult-after-jump-hook Functions to call after jumping to a location - consult-async-input-debounce Input debounce for asynchronous commands - consult-async-input-throttle Input throttle for asynchronous commands - consult-async-min-input Minimum numbers of input characters - consult-async-refresh-delay Refresh delay for asynchronous commands - consult-async-split-style Splitting style used for async commands - consult-async-split-styles-alist Available splitting styles used for async commands - consult-bookmark-narrow Narrowing configuration for `consult-bookmark' - consult-buffer-filter Filter for `consult-buffer' - consult-buffer-sources List of virtual buffer sources - consult-fd-args Command line arguments for fd - consult-find-args Command line arguments for find - consult-fontify-max-size Buffers larger than this limit are not fontified - consult-fontify-preserve Preserve fontification for line-based commands. - consult-git-grep-args Command line arguments for git-grep - consult-goto-line-numbers Show line numbers for `consult-goto-line' - consult-grep-max-columns Maximal number of columns of the matching lines - consult-grep-args Command line arguments for grep - consult-imenu-config Mode-specific configuration for `consult-imenu' - consult-line-numbers-widen Show absolute line numbers when narrowing is active - consult-line-start-from-top Start the `consult-line' search from the top - consult-locate-args Command line arguments for locate - consult-man-args Command line arguments for man - consult-mode-command-filter Filter for `consult-mode-command' - consult-mode-histories Mode-specific history variables - consult-narrow-key Narrowing prefix key during completion - consult-point-placement Placement of the point when jumping to matches - consult-preview-key Keys which triggers preview - consult-preview-allowed-hooks List of `find-file' hooks to enable during preview - consult-preview-excluded-files Regexps matched against file names during preview - consult-preview-max-count Maximum number of files to keep open during preview - consult-preview-partial-size Files larger than this size are previewed partially - consult-preview-partial-chunk Size of the file chunk which is previewed partially - consult-preview-variables Alist of variables to bind during preview - consult-project-buffer-sources List of virtual project buffer sources - consult-project-function Function which returns current project root - consult-register-prefix Prefix string for register keys during completion - consult-ripgrep-args Command line arguments for ripgrep - consult-themes List of themes to be presented for selection - consult-widen-key Widening key during completion - consult-yank-rotate Rotate kill ring - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - -[Marginalia] - - -3.3 Fine-tuning of individual commands -────────────────────────────────────── - - *NOTE:* Consult supports fine-grained customization of individual - commands. This configuration feature exists for experienced users with - special requirements. There is the [Consult wiki], where we collect - further configuration examples. - - Commands and buffer sources allow flexible, individual customization - by using the `consult-customize' macro. You can override any option - passed to the internal `consult--read' API. Note that since - `consult--read' is part of the internal API, options could be removed, - replaced or renamed in future versions of the package. - - Useful options are: - • `:prompt' set the prompt string - • `:preview-key' set the preview key, default is `consult-preview-key' - • `:initial' set the initial input - • `:default' set the default value - • `:history' set the history variable symbol - • `:add-history' add items to the future history, for example symbol - at point - • `:sort' enable or disable sorting - • `:group' set to nil to disable candidate grouping and titles. - • `:inherit-input-method' set to non-nil to inherit the input method. - - ┌──── - │ (consult-customize - │ ;; Disable preview for `consult-theme' completely. - │ consult-theme :preview-key nil - │ ;; Set preview for `consult-buffer' to key `M-.' - │ consult-buffer :preview-key "M-." - │ ;; For `consult-line' change the prompt and specify multiple preview - │ ;; keybindings. Note that you should bind and in the - │ ;; `minibuffer-local-completion-map' or `vertico-map' to the commands which - │ ;; select the previous or next candidate. - │ consult-line :prompt "Search: " - │ :preview-key '("S-" "S-")) - └──── - - The configuration values are evaluated at runtime, just before the - completion session is started. Therefore you can use for example - `thing-at-point' to adjust the initial input or the future history. - - ┌──── - │ (consult-customize - │ consult-line - │ :add-history (seq-some #'thing-at-point '(region symbol))) - │ - │ (defalias 'consult-line-thing-at-point 'consult-line) - │ - │ (consult-customize - │ consult-line-thing-at-point - │ :initial (thing-at-point 'symbol)) - └──── - - Generally it is possible to modify commands for your individual needs - by the following techniques: - - 1. Use `consult-customize' in order to change the command or source - settings. - 2. Create your own wrapper function which passes modified arguments to - the Consult functions. - 3. Create your own buffer [multi sources] for `consult-buffer'. - 4. Create advices to modify some internal behavior. - 5. Write or propose a patch. - - -[Consult wiki] - -[multi sources] See section 2.4 - - -4 Recommended packages -══════════════════════ - - I use and recommend this combination of packages: - - • consult: This package - • [vertico]: Fast and minimal vertical completion system - • [marginalia]: Annotations for the completion candidates - • [embark and embark-consult]: Action commands, which can act on the - completion candidates - • [orderless]: Completion style which offers flexible candidate - filtering - • [wgrep]: Editing of grep buffers. Use with `consult-grep' via - `embark-export'. - - There exist multiple fine completion UIs beside Vertico, which are - supported by Consult. Give them a try and find out which interaction - model fits best for you. - - • The builtin completion UI, which pops up the `*Completions*' buffer. - • The builtin `icomplete-vertical-mode' in Emacs 28 or newer. - • [mct by Protesilaos Stavrou]: Minibuffer and Completions in Tandem, - which builds on the default completion UI. - - Note that all packages are independent and can be exchanged with - alternative components, since there exist no hard - dependencies. Furthermore it is possible to get started with only - default completion and Consult and add more components later to the - mix. For example you can omit Marginalia if you don't need - annotations. I highly recommend the Embark package, but in order to - familiarize yourself with the other components, you can first start - without it - or you could use with Embark right away and add the other - components later on. - - We document a [list of auxiliary packages] in the Consult wiki. These - packages integrate Consult with special programs or with other - packages in the wider Emacs ecosystem. - - -[vertico] - -[marginalia] - -[embark and embark-consult] - -[orderless] - -[wgrep] - -[mct by Protesilaos Stavrou] - -[list of auxiliary packages] - - - -5 Bug reports -═════════════ - - If you find a bug or suspect that there is a problem with Consult, - please carry out the following steps: - - 1. *Search through the issue tracker* if your issue has been reported - before (and has been resolved eventually) in the meantime. - 2. *Remove all packages involved in the suspected bug from your - installation.* - 3. *Reinstall the newest version of all relevant packages*. Updating - alone is not sufficient, since package.el sometimes causes - miscompilation. The list of packages includes Consult, Compat, - Vertico or other completion UIs, Marginalia, Embark and Orderless. - 4. Either use the default completion UI or ensure that exactly one of - `vertico-mode', `mct-mode', or `icomplete-mode' is enabled. The - unsupported modes `selectrum-mode', `ivy-mode', `helm-mode', - `ido-mode' and `ido-ubiquitous-mode' must be disabled. - 5. Ensure that the `completion-styles' variable is properly - configured. Try to set `completion-styles' to a list including - `substring' or `orderless'. - 6. Try to reproduce the issue with the newest stable Emacs - version. Start a bare bone Emacs instance with `emacs -Q' on the - command line. Execute the following minimal code snippets in the - scratch buffer. This way we can exclude side effects due to - configuration settings. If other packages are relevant to reproduce - the issue, include them in the minimal configuration snippet. - - Minimal setup with Vertico for `emacs -Q': - ┌──── - │ (package-initialize) - │ (require 'consult) - │ (require 'vertico) - │ (vertico-mode) - │ (setq completion-styles '(substring basic)) - └──── - - Minimal setup with the default completion system for `emacs -Q': - ┌──── - │ (package-initialize) - │ (require 'consult) - │ (setq completion-styles '(substring basic)) - └──── - - Please provide the necessary important information with your bug - report: - - • The minimal configuration snippet used to reproduce the issue. - • Your completion UI (Default completion, Vertico, Mct or Icomplete). - • A stack trace in case the bug triggers an exception. - • Your Emacs version, since bugs may be fixed or introduced in newer - versions. - • Your operating system, since Emacs behavior varies subtly between - Linux, Mac and Windows. - • The package manager, e.g., straight.el or package.el, used to - install the Emacs packages, in order to exclude update issues. Did - you install Consult as part of the Doom Emacs distribution? - • Do you use Evil? Consult does not provide Evil integration out of - the box, but there is some support in [evil-collection]. - - When evaluating Consult-related code snippets you should enable - [lexical binding]. Consult often relies on lambdas and lexical - closures. - - -[evil-collection] - -[lexical binding] - - - -6 Contributions -═══════════════ - - Consult is a community effort, please participate in the discussions. - Contributions are welcome, but you may want to discuss potential - contributions first. Since this package is part of [GNU ELPA] - contributions require a copyright assignment to the FSF. - - If you have a proposal, take a look at the [Consult issue tracker] and - the [Consult wishlist]. There have been many prior feature - discussions. Please search through the issue tracker, maybe your issue - or feature request has already been discussed. You can contribute to - the [Consult wiki], in case you want to share small configuration or - command snippets. - - -[GNU ELPA] - -[Consult issue tracker] - -[Consult wishlist] - -[Consult wiki] - - -7 Acknowledgments -═════════════════ - - This package took inspiration from [Counsel] by Oleh Krehel. Some of - the Consult commands originated in the Counsel package or the wiki of - the Selectrum package. This package exists only thanks to the help of - these great contributors and thanks to the feedback of many - users. Thank you! - - Code contributions: [Aymeric Agon-Rambosson], [Amos Bird], [Ashton - Wiersdorf], [Adam Spiers], [Augusto Stoffel], [Clemens Radermacher], - [Zhengyi], [Geoffrey Lessel], [Illia Ostapyshyn], [jakanakaevangeli], - [JD Smith], [Jean-Philippe Bernardy], [mattiasdrp], [Mohamed - Abdelnour], [Mohsin Kaleem], [Fox Kiester], [Omar Antolín Camarena], - [Earl Hyatt], [Omar Polo], [Piotr Kwiecinski], [Robert Weiner], - [Sergey Kostyaev], [Alexandru Scvorțov], [Tecosaur], [Sylvain - Rousseau], [Tom Fitzhenry], [Iñigo Serna] and [Alex Kreisher]. - - Advice and useful discussions: [Enrique Kessler Martínez], [Adam - Porter], [Bruce d'Arcus], [Clemens Radermacher], [Dmitry Gutov], - [Howard Melman], [Itai Y. Efrat], [JD Smith], [Manuel Uberti], [Stefan - Monnier], [Omar Antolín Camarena], [Steve Purcell], [Radon - Rosborough], [Tom Fitzhenry] and [Protesilaos Stavrou]. - - -[Counsel] - -[Aymeric Agon-Rambosson] - -[Amos Bird] - -[Ashton Wiersdorf] - -[Adam Spiers] - -[Augusto Stoffel] - -[Clemens Radermacher] - -[Zhengyi] - -[Geoffrey Lessel] - -[Illia Ostapyshyn] - -[jakanakaevangeli] - -[JD Smith] - -[Jean-Philippe Bernardy] - -[mattiasdrp] - -[Mohamed Abdelnour] - -[Mohsin Kaleem] - -[Fox Kiester] - -[Omar Antolín Camarena] - -[Earl Hyatt] - -[Omar Polo] - -[Piotr Kwiecinski] - -[Robert Weiner] - -[Sergey Kostyaev] - -[Alexandru Scvorțov] - -[Tecosaur] - -[Sylvain Rousseau] - -[Tom Fitzhenry] - -[Iñigo Serna] - -[Alex Kreisher] - -[Enrique Kessler Martínez] - -[Adam Porter] - -[Bruce d'Arcus] - -[Dmitry Gutov] - -[Howard Melman] - -[Itai Y. Efrat] - -[Manuel Uberti] - -[Stefan Monnier] - -[Steve Purcell] - -[Radon Rosborough] - -[Protesilaos Stavrou] - - -8 Indices -═════════ - -8.1 Function index -────────────────── - - -8.2 Concept index -───────────────── blob - e4b2ee63932a248b8a00f99b4c276a8cae02f1a8 (mode 644) blob + /dev/null --- elpa/consult-1.5/README.org +++ /dev/null @@ -1,1153 +0,0 @@ -#+title: consult.el - Consulting completing-read -#+author: Daniel Mendler -#+language: en -#+export_file_name: consult.texi -#+texinfo_dir_category: Emacs misc features -#+texinfo_dir_title: Consult: (consult). -#+texinfo_dir_desc: Useful commands built on completing-read. - -#+html: GNU Emacs -#+html: GNU ELPA -#+html: GNU-devel ELPA -#+html: MELPA -#+html: MELPA Stable - -Consult provides search and navigation commands based on the Emacs completion -function [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Minibuffer-Completion.html][completing-read]]. Completion allows you to quickly select an item from a -list of candidates. Consult offers asynchronous and interactive =consult-grep= and -=consult-ripgrep= commands, and the line-based search command =consult-line=. -Furthermore Consult provides an advanced buffer switching command =consult-buffer= -to switch between buffers, recently opened files, bookmarks and buffer-like -candidates from other sources. Some of the Consult commands are enhanced -versions of built-in Emacs commands. For example the command =consult-imenu= -presents a flat list of the Imenu with [[#live-previews][live preview]], [[#narrowing-and-grouping][grouping and narrowing]]. -Please take a look at the [[#available-commands][full list of commands]]. - -Consult is fully compatible with completion systems centered around the standard -Emacs =completing-read= API, notably the default completion system, [[https://github.com/minad/vertico][Vertico]], [[https://github.com/protesilaos/mct][Mct]], -and [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Icomplete.html][Icomplete]]. - -This package keeps the completion system specifics to a minimum. The ability of -the Consult commands to work well with arbitrary completion systems is one of -the main advantages of the package. Consult fits well into existing setups and -it helps you to create a full completion environment out of small and -independent components. - -You can combine the complementary packages [[https://github.com/minad/marginalia/][Marginalia]], [[https://github.com/oantolin/embark/][Embark]] and [[https://github.com/oantolin/orderless][Orderless]] with -Consult. Marginalia enriches the completion display with annotations, e.g., -documentation strings or file information. The versatile Embark package provides -local actions, comparable to a context menu. These actions operate on the -selected candidate in the minibuffer or at point in normal buffers. For example, -when selecting from a list of files, Embark offers an action to delete the file. -Additionally Embark offers a facility to collect completion candidates in a -collect buffer. The section [[#embark-integration][Embark integration]] documents in detail how Consult -and Embark work together. - -#+toc: headlines 8 - -* Screenshots :noexport: - -#+caption: consult-grep -[[https://github.com/minad/consult/blob/screenshots/consult-grep.gif?raw=true]] -Fig. 1: Command =consult-git-grep= - -#+caption: consult-imenu -[[https://github.com/minad/consult/blob/screenshots/consult-imenu.png?raw=true]] -Fig. 2: Command =consult-imenu= - -#+caption: consult-line -[[https://github.com/minad/consult/blob/screenshots/consult-line.png?raw=true]] -Fig. 3: Command =consult-line= - -* Available commands -:properties: -:custom_id: available-commands -:description: Navigation, search, editing commands and more -:end: -#+cindex: commands - -Most Consult commands follow the meaningful naming scheme =consult-=. -Many commands implement a little known but convenient Emacs feature called -"future history", which guesses what input the user wants. At a command prompt -type =M-n= and typically Consult will insert the symbol or thing at point into -the input. - -*TIP:* If you have [[https://github.com/minad/marginalia][Marginalia]] annotators activated, type =M-x ^consult= to see -all Consult commands with their abbreviated description. Alternatively, type -=C-h a ^consult= to get an overview of all Consult variables and functions with -their descriptions. - -** Virtual Buffers -:properties: -:description: Buffers, bookmarks and recent files -:end: -#+cindex: virtual buffers - -#+findex: consult-buffer -#+findex: consult-buffer-other-window -#+findex: consult-buffer-other-frame -#+findex: consult-buffer-other-tab -#+findex: consult-project-buffer -#+findex: consult-recent-file -#+findex: consult-bookmark -- =consult-buffer=: Enhanced version of =switch-to-buffer= with support for virtual - buffers. Supports live preview of buffers and narrowing to the virtual buffer - types. You can type =f SPC= in order to narrow to recent files. Press =SPC= to - show ephemeral buffers. Supported narrowing keys: - - b Buffers - - SPC Hidden buffers - - * Modified buffers - - f Files (Requires =recentf-mode=) - - r File registers - - m Bookmarks - - p Project - - Custom [[#multiple-sources][other sources]] configured in =consult-buffer-sources=. -- =consult-buffer-other-window=, =consult-buffer-other-frame=, - =consult-buffer-other-tab=: Variants of =consult-buffer=. -- =consult-project-buffer=: Variant of =consult-buffer= restricted to buffers and - recent files of the current project. You can add custom sources to - =consult-project-buffer-sources=. The command may prompt you for a project if - you invoke it from outside a project. -- =consult-bookmark=: Select or create bookmark. To select bookmarks you might use the - =consult-buffer= as an alternative, which can include a bookmark virtual buffer - source. Note that =consult-bookmark= supports preview of bookmarks and - narrowing. -- =consult-recent-file=: Select from recent files with preview. - You might prefer the powerful =consult-buffer= instead, which can include - recent files as a virtual buffer source. The =recentf-mode= enables tracking of - recent files. - -** Editing -:properties: -:description: Commands useful for editing -:end: -#+cindex: editing - -#+findex: consult-yank-pop -#+findex: consult-yank-from-kill-ring -#+findex: consult-yank-replace -#+findex: consult-kmacro -- =consult-yank-from-kill-ring=: Enhanced version of =yank= to select an item - from the =kill-ring=. The selected text previewed as overlay in the buffer. -- =consult-yank-pop=: Enhanced version of =yank-pop= with DWIM-behavior, which - either replaces the last =yank= by cycling through the =kill-ring=, or if there - has not been a last =yank= consults the =kill-ring=. The selected text previewed - as overlay in the buffer. -- =consult-yank-replace=: Like =consult-yank-pop=, but always replaces the last - =yank= with an item from the =kill-ring=. -- =consult-kmacro=: Select macro from the macro ring and execute it. - -** Register -:properties: -:description: Searching through registers and fast access -:end: -#+cindex: register - -#+findex: consult-register -#+findex: consult-register-load -#+findex: consult-register-store -#+findex: consult-register-format -#+findex: consult-register-window -- =consult-register=: Select from list of registers. The command - supports narrowing to register types and preview of marker positions. This - command is useful to search the register contents. For quick access use the - commands =consult-register-load=, =consult-register-store= or the built-in Emacs - register commands. -- =consult-register-format=: Set =register-preview-function= to this function for - an enhanced register formatting. See the [[#use-package-example][example configuration]]. -- =consult-register-window=: Replace =register-preview= with this function for a - better register window. See the [[#use-package-example][example configuration]]. -- =consult-register-load=: Utility command to quickly load a register. - The command either jumps to the register value or inserts it. -- =consult-register-store=: Improved UI to store registers depending on the current - context with an action menu. With an active region, store/append/prepend the - contents, optionally deleting the region when a prefix argument is given. - With a numeric prefix argument, store/add the number. Otherwise store point, - frameset, window or kmacro. Usage examples: - * =M-' x=: If no region is active, store point in register =x=. - If a region is active, store the region in register =x=. - * =M-' M-w x=: Store window configuration in register =x=. - * =C-u 100 M-' x=: Store number in register =x=. - -** Navigation -:properties: -:description: Mark rings, outlines and imenu -:end: -#+cindex: navigation - -#+findex: consult-goto-line -#+findex: consult-mark -#+findex: consult-global-mark -#+findex: consult-outline -#+findex: consult-imenu -#+findex: consult-imenu-multi -- =consult-goto-line=: Jump to line number enhanced with live preview. This is a - drop-in replacement for =goto-line=. Enter a line number to jump to the first - column of the given line. Alternatively enter =line:column= in order to jump to - a specific column. -- =consult-mark=: Jump to a marker in the =mark-ring=. Supports live - preview and recursive editing. -- =consult-global-mark=: Jump to a marker in the =global-mark-ring=. - Supports live preview and recursive editing. -- =consult-outline=: Jump to a heading of the outline. Supports narrowing - to a heading level, live preview and recursive editing. -- =consult-imenu=: Jump to imenu item in the current buffer. Supports - live preview, recursive editing and narrowing. -- =consult-imenu-multi=: Jump to imenu item in project buffers, with - the same major mode as the current buffer. Supports live preview, - recursive editing and narrowing. This feature has been inspired by - [[https://github.com/vspinu/imenu-anywhere][imenu-anywhere]]. - -** Search -:properties: -:description: Line search, grep and file search -:end: -#+cindex: search - -#+findex: consult-line -#+findex: consult-line-multi -#+findex: consult-keep-lines -#+findex: consult-focus-lines -- =consult-line=: Enter search string and select from matching lines. - Supports live preview and recursive editing. The symbol at point and the - recent Isearch string are added to the "future history" and can be accessed - by pressing =M-n=. When =consult-line= is bound to the =isearch-mode-map= and - is invoked during a running Isearch, it will use the current Isearch string. -- =consult-line-multi=: Search dynamically across multiple buffers. By default - search across project buffers. If invoked with a prefix argument search across - all buffers. The candidates are computed on demand based on the input. The - command behaves like =consult-grep=, but operates on buffers instead of files. -- =consult-keep-lines=: Replacement for =keep/flush-lines= which uses the current - completion style for filtering the buffer. The function updates the buffer - while typing. In particular =consult-keep-lines= can narrow down an exported - Embark collect buffer further, relying on the same completion filtering as - ~completing-read~. If the input begins with the negation operator, i.e., ~! SPC~, - the filter matches the complement. If a region is active, the region restricts - the filtering. -- =consult-focus-lines=: Temporarily hide lines by filtering them using the - current completion style. Call with =C-u= prefix argument in order to show the - hidden lines again. If the input begins with the negation operator, i.e., ~! - SPC~, the filter matches the complement. In contrast to =consult-keep-lines= this - function does not edit the buffer. If a region is active, the region restricts - the filtering. - -** Grep and Find -:properties: -:description: Searching through the filesystem -:end: -#+cindex: grep -#+cindex: find -#+cindex: locate - -#+findex: consult-grep -#+findex: consult-ripgrep -#+findex: consult-git-grep -#+findex: consult-find -#+findex: consult-fd -#+findex: consult-locate -- =consult-grep=, =consult-ripgrep=, =consult-git-grep=: Search for regular expression - in files. Consult invokes Grep asynchronously, while you enter the search - term. After at least =consult-async-min-input= characters, the search gets - started. Consult splits the input string into two parts, if the first - character is a punctuation character, like =#=. For example - =#regexps#filter-string=, is split at the second =#=. The string =regexps= is passed - to Grep. Note that Consult transforms Emacs regular expressions to expressions - understand by the search program. Always use Emacs regular expressions at the - prompt. If you enter multiple regular expressions separated by space only - lines matching all regular expressions are shown. In order to match space - literally, escape the space with a backslash. The =filter-string= is passed to - the /fast/ Emacs filtering to further narrow down the list of matches. This is - particularly useful if you are using an advanced completion style like - orderless. =consult-grep= supports preview. If the =consult-project-function= - returns non-nil, =consult-grep= searches the current project directory. - Otherwise the =default-directory= is searched. If =consult-grep= is invoked - with prefix argument =C-u M-s g=, you can specify one or more comma-separated files - and directories manually. -- =consult-find=, =consult-fd=, =consult-locate=: Find file by matching the path - against a regexp. Like for =consult-grep=, either the project root or the - current directory is the root directory for the search. The input string is - treated similarly to =consult-grep=, where the first part is passed to find, and - the second part is used for Emacs filtering. Prefix arguments to =consult-find= - work just like those for the consult grep commands. - -** Compilation -:properties: -:description: Jumping to references and compilation errors -:end: -#+cindex: compilation errors - -#+findex: consult-compile-error -#+findex: consult-flymake -#+findex: consult-xref -- =consult-compile-error=: Jump to a compilation error. Supports live preview - narrowing and recursive editing. -- =consult-flymake=: Jump to Flymake diagnostic. Supports live preview and - recursive editing. The command supports narrowing. Press =e SPC=, =w SPC=, =n SPC= - to only show errors, warnings and notes respectively. -- =consult-xref=: Integration with xref. This function can be set as - =xref-show-xrefs-function= and =xref-show-definitions-function=. - -** Histories -:properties: -:description: Navigating histories -:end: -#+cindex: history - -#+findex: consult-complex-command -#+findex: consult-history -#+findex: consult-isearch-history -- =consult-complex-command=: Select a command from the - =command-history=. This command is a =completing-read= version of - =repeat-complex-command= and is also a replacement for the =command-history= - command from chistory.el. -- =consult-history=: Insert a string from the current buffer history, for example - the Eshell or Comint history. You can also invoke this command from the - minibuffer. In that case =consult-history= uses the history stored in the - =minibuffer-history-variable=. If you prefer =completion-at-point=, take a look at - =cape-history= from the [[https://github.com/minad/cape][Cape]] package. -- =consult-isearch-history=: During an Isearch session, this command picks a - search string from history and continues the search with the newly selected - string. Outside of Isearch, the command allows you to pick a string from the - history and starts a new Isearch. =consult-isearch-history= acts as a drop-in - replacement for =isearch-edit-string=. - -** Modes -:properties: -:description: Toggling minor modes and executing commands -:end: -#+cindex: minor mode -#+cindex: major mode - -#+findex: consult-minor-mode-menu -#+findex: consult-mode-command -- =consult-minor-mode-menu=: Enable/disable minor mode. Supports - narrowing to on/off/local/global modes by pressing =i/o/l/g SPC= - respectively. -- =consult-mode-command=: Run a command from the currently active minor or major - modes. Supports narrowing to local-minor/global-minor/major mode via the keys - =l/g/m=. - -** Org Mode -:properties: -:description: Org-specific commands -:end: - -#+findex: consult-org-heading -#+findex: consult-org-agenda -- =consult-org-heading=: Variant of =consult-imenu= or =consult-outline= for Org - buffers. The headline and its ancestors headlines are separated by slashes. - Supports narrowing by heading level, priority and TODO keyword, as well as live - preview and recursive editing. -- =consult-org-agenda=: Jump to an Org agenda heading. Supports narrowing by - heading level, priority and TODO keyword, as well as live preview and - recursive editing. -** Help -:properties: -:description: Searching through help -:end: - -#+findex: consult-info -#+findex: consult-man -- =consult-man=: Find Unix man page, via Unix =apropos= or =man -k=. =consult-man= opens - the selected man page using the Emacs =man= command. -- =consult-info=: Full text search through info pages. If the command is invoked - from within an ~*info*~ buffer, it will search through the current manual. You - may want to create your own commands which search through a predefined set of - info pages, for example: -#+begin_src emacs-lisp -(defun consult-info-emacs () - "Search through Emacs info pages." - (interactive) - (consult-info "emacs" "efaq" "elisp" "cl" "compat")) - -(defun consult-info-org () - "Search through the Org info page." - (interactive) - (consult-info "org")) - -(defun consult-info-completion () - "Search through completion info pages." - (interactive) - (consult-info "vertico" "consult" "marginalia" "orderless" "embark" - "corfu" "cape" "tempel")) -#+end_src - -** Miscellaneous -:properties: -:description: Various other useful commands -:end: - -#+findex: consult-completion-in-region -#+findex: consult-theme -#+findex: consult-preview-at-point -#+findex: consult-preview-at-point-mode -- =consult-theme=: Select a theme and disable all currently enabled themes. - Supports live preview of the theme while scrolling through the candidates. -- =consult-preview-at-point= and =consult-preview-at-point-mode=: Command and minor - mode which previews the candidate at point in the =*Completions*= buffer. This - mode is relevant if you use [[https://git.sr.ht/~protesilaos/mct][Mct]] or the default =*Completions*= UI. -- =consult-completion-in-region=: In case you don't use [[https://github.com/minad/corfu][Corfu]] as your in-buffer - completion UI, this function can be set as =completion-in-region-function=. Then - your minibuffer completion UI (e.g., Vertico or Icomplete) will be used for - =completion-at-point=. - #+begin_src emacs-lisp - ;; Use `consult-completion-in-region' if Vertico is enabled. - ;; Otherwise use the default `completion--in-region' function. - (setq completion-in-region-function - (lambda (&rest args) - (apply (if vertico-mode - #'consult-completion-in-region - #'completion--in-region) - args))) - #+end_src - Instead of =consult-completion-in-region=, you may prefer to see the completions - directly in the buffer as a small popup. In that case, I recommend the [[https://github.com/minad/corfu][Corfu]] - package. There is a technical limitation of =consult-completion-in-region= in - combination with the Lsp modes. The Lsp server relies on the input at point, - in order to generate refined candidate strings. Since the completion is - transferred from the original buffer to the minibuffer, the server does not - receive the updated input. In contrast, in-buffer Lsp completion for example - via Corfu works properly since the completion takes place directly in the - original buffer. - -* Special features -:properties: -:description: Enhancements over built-in `completing-read' -:end: - -Consult enhances =completing-read= with live previews of candidates, additional -narrowing capabilities to candidate groups and asynchronously generated -candidate lists. The internal =consult--read= function, which is used by most -Consult commands, is a thin wrapper around =completing-read= and provides the -special functionality. In order to support multiple candidate sources there -exists the high-level function =consult--multi=. The architecture of Consult -allows it to work with different completion systems in the backend, while still -offering advanced features. - -** Live previews -:properties: -:description: Preview the currently selected candidate -:custom_id: live-previews -:end: -#+cindex: preview - -Some Consult commands support live previews. For example when you scroll through -the items of =consult-line=, the buffer will scroll to the corresponding position. -It is possible to jump back and forth between the minibuffer and the buffer to -perform recursive editing while the search is ongoing. - -Consult enables previews by default. You can disable them by adjusting the -=consult-preview-key= variable. Furthermore it is possible to specify keybindings -which trigger the preview manually as shown in the [[#use-package-example][example configuration]]. The -default setting of =consult-preview-key= is =any= which means that Consult triggers -the preview /immediately/ on any key press when the selected candidate changes. -You can configure each command individually with its own =:preview-key=. The -following settings are possible: - -- Automatic and immediate ='any= -- Automatic and delayed =(list :debounce 0.5 'any)= -- Manual and immediate ="M-."= -- Manual and delayed =(list :debounce 0.5 "M-.")= -- Disabled =nil= - -A safe recommendation is to leave automatic immediate previews enabled in -general and disable the automatic preview only for commands where the preview -may be expensive due to file loading. Internally, Consult uses the -value of =this-command= to determine the =:preview-key= -customized. This means that if you wrap a =consult-*= command within -your own function or command, you will also need to add the name of -/your custom command/ to the =consult-customize= call in order for it -to be considered. - -#+begin_src emacs-lisp -(consult-customize - consult-ripgrep consult-git-grep consult-grep - consult-bookmark consult-recent-file consult-xref - consult--source-bookmark consult--source-file-register - consult--source-recent-file consult--source-project-recent-file - ;; my/command-wrapping-consult ;; disable auto previews inside my command - :preview-key '(:debounce 0.4 any) ;; Option 1: Delay preview - ;; :preview-key "M-.") ;; Option 2: Manual preview -#+end_src - -In this case one may wonder what the difference is between using an Embark -action on the current candidate in comparison to a manually triggered preview. -The main difference is that the files opened by manual preview are closed again -after the completion session. During preview some functionality is disabled to -improve the performance, see for example the customization variables -=consult-preview-variables= and =consult-preview-allowed-hooks=. Only the hooks -listed in =consult-preview-allowed-hooks= are executed when a file is opened -(=find-file-hook=). In order to enable additional font locking during preview, add -the corresponding hooks to the allow list. The following code demonstrates this -for [[https://github.com/minad/org-modern][org-modern]] and [[https://github.com/tarsius/hl-todo][hl-todo]]. - -#+begin_src emacs-lisp -(add-to-list 'consult-preview-allowed-hooks 'global-org-modern-mode-check-buffers) -(add-to-list 'consult-preview-allowed-hooks 'global-hl-todo-mode-check-buffers) -#+end_src - -Files larger than =consult-preview-partial-size= are previewed partially. Delaying -the preview is also useful for =consult-theme=, since the theme preview is slow. -The delay results in a smoother UI experience. - -#+begin_src emacs-lisp -;; Preview on any key press, but delay 0.5s -(consult-customize consult-theme :preview-key '(:debounce 0.5 any)) -;; Preview immediately on M-., on up/down after 0.5s, on any other key after 1s -(consult-customize consult-theme - :preview-key - '("M-." - :debounce 0.5 "" "" - :debounce 1 any)) -#+end_src - -** Narrowing and grouping -:properties: -:description: Restricting the completion to a candidate group -:custom_id: narrowing-and-grouping -:end: -#+cindex: narrowing - -Consult has special support for candidate groups. If the completion UI supports -the grouping functionality, the UI separates the groups with thin lines and -shows group titles. Grouping is useful if the list of candidates consists of -candidates of multiple types or candidates from [[#multiple-sources][multiple sources]], like the -=consult-buffer= command, which shows both buffers and recently opened files. Note -that you can disable the group titles by setting the =:group= property of the -corresponding command to nil using the =consult-customize= macro. - -By entering a narrowing prefix or by pressing a narrowing key it is possible to -restrict the completion candidates to a certain candidate group. When you use -the =consult-buffer= command, you can enter the prefix =b SPC= to restrict list of -candidates to buffers only. If you press =DEL= afterwards, the full candidate list -will be shown again. Furthermore a narrowing prefix key and a widening key can -be configured which can be pressed to achieve the same effect, see the -configuration variables =consult-narrow-key= and =consult-widen-key=. - -After pressing =consult-narrow-key=, the possible narrowing keys can be shown by -pressing =C-h=. When pressing =C-h= after some prefix key, the =prefix-help-command= -is invoked, which shows the keybinding help window by default. As a more compact -alternative, there is the =consult-narrow-help= command which can be bound to a -key, for example =?= or =C-h= in the =consult-narrow-map=, as shown in the [[#use-package-example][example -configuration]]. If [[https://github.com/justbur/emacs-which-key][which-key]] is installed, the narrowing keys are automatically -shown in the which-key window after pressing the =consult-narrow-key=. - -** Asynchronous search -:properties: -:description: Filtering asynchronously generated candidate lists -:end: -#+cindex: asynchronous search - -Consult has support for asynchronous generation of candidate lists. This feature -is used for search commands like =consult-grep=, where the list of matches is -generated dynamically while the user is typing a regular expression. The grep -process is executed in the background. When modifying the regular expression, -the background process is terminated and a new process is started with the -modified regular expression. - -The matches, which have been found, can then be narrowed using the installed -Emacs completion-style. This can be powerful if you are using for example the -=orderless= completion style. - -This two-level filtering is possible by splitting the input string. Part of the -input string is treated as input to grep and part of the input is used for -filtering. There are multiple splitting styles available, configured in -~consult-async-split-styles-alist~: =nil=, =comma=, =semicolon= and =perl=. The default -splitting style is configured with the variable ~consult-async-split-style~. - -With the =comma= and =semicolon= splitting styles, the first word before the comma -or semicolon is passed to grep, the remaining string is used for filtering. The -=nil= splitting style does not perform any splitting, the whole input is passed to -grep. - -The =perl= splitting style splits the input string at a punctuation character, -using a similar syntax as Perl regular expressions. - -Examples: - -- =#defun=: Search for "defun" using grep. -- =#consult embark=: Search for both "consult" and "embark" using grep in any order. -- =#first.*second=: Search for "first" followed by "second" using grep. -- =#\(consult\|embark\)=: Search for "consult" or "embark" using grep. Note the - usage of Emacs-style regular expressions. -- =#defun#consult=: Search for "defun" using grep, filter with the word - "consult". -- =/defun/consult=: It is also possible to use other punctuation - characters. -- =#to#=: Force searching for "to" using grep, since the grep pattern - must be longer than =consult-async-min-input= characters by default. -- =#defun -- --invert-match#=: Pass argument =--invert-match= to grep. - -Asynchronous processes like =find= and =grep= create an error log buffer -=_*consult-async*= (note the leading space), which is useful for -troubleshooting. The prompt has a small indicator showing the process status: - -- =:= the usual prompt colon, before input is provided. -- =*= with warning face, the process is running. -- =:= with success face, success, process exited with an error code of zero. -- =!= with error face, failure, process exited with a nonzero error code. -- =;= with error face, interrupted, for example if more input is provided. - -** Multiple sources -:properties: -:description: Combining candidates from different sources -:custom_id: multiple-sources -:end: -#+cindex: multiple sources - -Multiple synchronous candidate sources can be combined. This feature is used by -the =consult-buffer= command to present buffer-like candidates in a single menu -for quick access. By default =consult-buffer= includes buffers, bookmarks, recent -files and project-specific buffers and files. It is possible to configure the -list of sources via the =consult-buffer-sources= variable. Arbitrary custom -sources can be defined. - -As an example, the bookmark source is defined as follows: - -#+begin_src emacs-lisp -(defvar consult--source-bookmark - `(:name "Bookmark" - :narrow ?m - :category bookmark - :face consult-bookmark - :history bookmark-history - :items ,#'bookmark-all-names - :action ,#'consult--bookmark-action)) -#+end_src - -Required source fields: -- =:category= Completion category. -- =:items= List of strings to select from or function returning list of strings. - A list of cons cells is not supported. - -Optional source fields: -- =:name= Name of the source, used for narrowing, group titles and annotations. -- =:narrow= Narrowing character or =(character . string)= pair. -- =:preview-key= Preview key or keys which trigger preview. -- =:enabled= Function which must return t if the source is enabled. -- =:hidden= When t candidates of this source are hidden by default. -- =:face= Face used for highlighting the candidates. -- =:annotate= Annotation function called for each candidate, returns string. -- =:history= Name of history variable to add selected candidate. -- =:default= Must be t if the first item of the source is the default value. -- =:action= Function called with the selected candidate. -- =:new= Function called with new candidate name, only if =:require-match= is nil. -- =:state= State constructor for the source, must return the state function. -- Other source fields can be added specifically to the use case. - -The =:state= and =:action= fields of the sources deserve a longer explanation. The -=:action= function takes a single argument and is only called after selection with -the selected candidate, if the selection has not been aborted. This -functionality is provided for convenience and easy definition of sources. The -=:state= field is more general. The =:state= function is a constructor function -without arguments, which can perform some setup necessary for the preview. It -must return a closure which takes an ACTION and a CANDIDATE argument. See the -docstring of =consult--with-preview= for more details about the ACTION argument. - -By default, =consult-buffer= previews buffers, bookmarks and files. Loading recent -files or bookmarks can result in expensive operations. However it is possible to -configure a manual preview as follows. - -#+begin_src emacs-lisp -(consult-customize - consult--source-bookmark consult--source-file-register - consult--source-recent-file consult--source-project-recent-file - :preview-key "M-.") -#+end_src - -Sources can be added directly to the =consult-buffer-source= list for convenience. -For example, the following source lists all Org buffers and lets you create new -ones. - -#+begin_src emacs-lisp -(defvar org-source - (list :name "Org Buffer" - :category 'buffer - :narrow ?o - :face 'consult-buffer - :history 'buffer-name-history - :state #'consult--buffer-state - :new - (lambda (name) - (with-current-buffer (get-buffer-create name) - (insert "#+title: " name "\n\n") - (org-mode) - (consult--buffer-action (current-buffer)))) - :items - (lambda () - (consult--buffer-query :mode 'org-mode :as #'consult--buffer-pair)))) - -(add-to-list 'consult-buffer-sources 'org-source 'append) -#+end_src - -One can create similar sources for other major modes. See the [[https://github.com/minad/consult/wiki][Consult wiki]] for -many additional source examples. See also the documentation of =consult-buffer= -and of the internal =consult--multi= API. The function =consult--multi= can be used -to create new multi-source commands. - -** Embark integration -:properties: -:description: Actions, Grep/Occur-buffer export -:custom_id: embark-integration -:end: -#+cindex: embark - -*NOTE*: Install the =embark-consult= package from MELPA, which provides -Consult-specific Embark actions and the Occur buffer export. - -Embark is a versatile package which offers context dependent actions, comparable -to a context menu. See the [[https://github.com/oantolin/embark][Embark manual]] for an extensive description of its -capabilities. - -Actions are commands which can operate on the currently selected candidate (or -target in Embark terminology). When completing files, for example the -=delete-file= command is offered. With Embark you can execute arbitrary commands -on the currently selected candidate via =M-x=. - -Furthermore Embark provides the =embark-collect= command, which collects -candidates and presents them in an Embark collect buffer, where further actions -can be applied to them. A related feature is the =embark-export= command, which -exports candidate lists to a buffer of a special type. For example in the case -of file completion, a Dired buffer is opened. - -In the context of Consult, particularly exciting is the possibility to export -the matching lines from =consult-line=, =consult-outline=, =consult-mark= and -=consult-global-mark=. The matching lines are exported to an Occur buffer where -they can be edited via the =occur-edit-mode= (press key =e=). Similarly, Embark -supports exporting the matches found by =consult-grep=, =consult-ripgrep= and -=consult-git-grep= to a Grep buffer, where the matches across files can be edited, -if the [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep]] package is installed. These three workflows are symmetric. - -+ =consult-line= -> =embark-export= to =occur-mode= buffer -> =occur-edit-mode= for editing of matches in buffer. -+ =consult-grep= -> =embark-export= to =grep-mode= buffer -> =wgrep= for editing of all matches. -+ =consult-find= -> =embark-export= to =dired-mode= buffer -> =wdired-change-to-wdired-mode= for editing. - -* Configuration -:properties: -:description: Example configuration and customization variables -:end: - -Consult can be installed from [[https://elpa.gnu.org/packages/consult.html][ELPA]] or [[https://melpa.org/#/consult][MELPA]] via the Emacs built-in package -manager. Alternatively it can be directly installed from the development -repository via other non-standard package managers. - -There is the [[https://github.com/minad/consult/wiki][Consult wiki]], where additional configuration examples can be -contributed. - -*IMPORTANT:* It is recommended that you enable [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html][lexical binding]] in your -configuration. Many Consult-related code snippets require lexical binding, since -they use lambdas and closures. - -** Use-package example -:properties: -:description: Configuration example based on use-package -:custom_id: use-package-example -:end: -#+cindex: use-package - -The Consult package only provides commands and does not add any keybindings or -modes. Therefore the package is non-intrusive but requires a little setup -effort. In order to use the Consult commands, it is advised to add keybindings -for commands which are accessed often. Rarely used commands can be invoked via -=M-x=. Feel free to only bind the commands you consider useful to your workflow. -The configuration shown here relies on the =use-package= macro, which is a -convenient tool to manage package configurations. - -*NOTE:* There is the [[https://github.com/minad/consult/wiki][Consult wiki]], where you can contribute additional -configuration examples. - -#+begin_src emacs-lisp -;; Example configuration for Consult -(use-package consult - ;; Replace bindings. Lazily loaded due by `use-package'. - :bind (;; C-c bindings in `mode-specific-map' - ("C-c M-x" . consult-mode-command) - ("C-c h" . consult-history) - ("C-c k" . consult-kmacro) - ("C-c m" . consult-man) - ("C-c i" . consult-info) - ([remap Info-search] . consult-info) - ;; C-x bindings in `ctl-x-map' - ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command - ("C-x b" . consult-buffer) ;; orig. switch-to-buffer - ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window - ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame - ("C-x t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab - ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump - ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer - ;; Custom M-# bindings for fast register access - ("M-#" . consult-register-load) - ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated) - ("C-M-#" . consult-register) - ;; Other custom bindings - ("M-y" . consult-yank-pop) ;; orig. yank-pop - ;; M-g bindings in `goto-map' - ("M-g e" . consult-compile-error) - ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck - ("M-g g" . consult-goto-line) ;; orig. goto-line - ("M-g M-g" . consult-goto-line) ;; orig. goto-line - ("M-g o" . consult-outline) ;; Alternative: consult-org-heading - ("M-g m" . consult-mark) - ("M-g k" . consult-global-mark) - ("M-g i" . consult-imenu) - ("M-g I" . consult-imenu-multi) - ;; M-s bindings in `search-map' - ("M-s d" . consult-find) ;; Alternative: consult-fd - ("M-s c" . consult-locate) - ("M-s g" . consult-grep) - ("M-s G" . consult-git-grep) - ("M-s r" . consult-ripgrep) - ("M-s l" . consult-line) - ("M-s L" . consult-line-multi) - ("M-s k" . consult-keep-lines) - ("M-s u" . consult-focus-lines) - ;; Isearch integration - ("M-s e" . consult-isearch-history) - :map isearch-mode-map - ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string - ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string - ("M-s l" . consult-line) ;; needed by consult-line to detect isearch - ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch - ;; Minibuffer history - :map minibuffer-local-map - ("M-s" . consult-history) ;; orig. next-matching-history-element - ("M-r" . consult-history)) ;; orig. previous-matching-history-element - - ;; Enable automatic preview at point in the *Completions* buffer. This is - ;; relevant when you use the default completion UI. - :hook (completion-list-mode . consult-preview-at-point-mode) - - ;; The :init configuration is always executed (Not lazy) - :init - - ;; Optionally configure the register formatting. This improves the register - ;; preview for `consult-register', `consult-register-load', - ;; `consult-register-store' and the Emacs built-ins. - (setq register-preview-delay 0.5 - register-preview-function #'consult-register-format) - - ;; Optionally tweak the register preview window. - ;; This adds thin lines, sorting and hides the mode line of the window. - (advice-add #'register-preview :override #'consult-register-window) - - ;; Use Consult to select xref locations with preview - (setq xref-show-xrefs-function #'consult-xref - xref-show-definitions-function #'consult-xref) - - ;; Configure other variables and modes in the :config section, - ;; after lazily loading the package. - :config - - ;; Optionally configure preview. The default value - ;; is 'any, such that any key triggers the preview. - ;; (setq consult-preview-key 'any) - ;; (setq consult-preview-key "M-.") - ;; (setq consult-preview-key '("S-" "S-")) - ;; For some commands and buffer sources it is useful to configure the - ;; :preview-key on a per-command basis using the `consult-customize' macro. - (consult-customize - consult-theme :preview-key '(:debounce 0.2 any) - consult-ripgrep consult-git-grep consult-grep - consult-bookmark consult-recent-file consult-xref - consult--source-bookmark consult--source-file-register - consult--source-recent-file consult--source-project-recent-file - ;; :preview-key "M-." - :preview-key '(:debounce 0.4 any)) - - ;; Optionally configure the narrowing key. - ;; Both < and C-+ work reasonably well. - (setq consult-narrow-key "<") ;; "C-+" - - ;; Optionally make narrowing help available in the minibuffer. - ;; You may want to use `embark-prefix-help-command' or which-key instead. - ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help) - - ;; By default `consult-project-function' uses `project-root' from project.el. - ;; Optionally configure a different project root function. - ;;;; 1. project.el (the default) - ;; (setq consult-project-function #'consult--default-project--function) - ;;;; 2. vc.el (vc-root-dir) - ;; (setq consult-project-function (lambda (_) (vc-root-dir))) - ;;;; 3. locate-dominating-file - ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git"))) - ;;;; 4. projectile.el (projectile-project-root) - ;; (autoload 'projectile-project-root "projectile") - ;; (setq consult-project-function (lambda (_) (projectile-project-root))) - ;;;; 5. No project support - ;; (setq consult-project-function nil) -) -#+end_src - -** Custom variables -:properties: -:description: Short description of all customization settings -:end: -#+cindex: customization - -*TIP:* If you have [[https://github.com/minad/marginalia][Marginalia]] installed, type =M-x customize-variable RET -^consult= to see all Consult-specific customizable variables with their current -values and abbreviated description. Alternatively, type =C-h a ^consult= to get -an overview of all Consult variables and functions with their descriptions. - -| Variable | Description | -|----------------------------------+-----------------------------------------------------| -| consult-after-jump-hook | Functions to call after jumping to a location | -| consult-async-input-debounce | Input debounce for asynchronous commands | -| consult-async-input-throttle | Input throttle for asynchronous commands | -| consult-async-min-input | Minimum numbers of input characters | -| consult-async-refresh-delay | Refresh delay for asynchronous commands | -| consult-async-split-style | Splitting style used for async commands | -| consult-async-split-styles-alist | Available splitting styles used for async commands | -| consult-bookmark-narrow | Narrowing configuration for =consult-bookmark= | -| consult-buffer-filter | Filter for =consult-buffer= | -| consult-buffer-sources | List of virtual buffer sources | -| consult-fd-args | Command line arguments for fd | -| consult-find-args | Command line arguments for find | -| consult-fontify-max-size | Buffers larger than this limit are not fontified | -| consult-fontify-preserve | Preserve fontification for line-based commands. | -| consult-git-grep-args | Command line arguments for git-grep | -| consult-goto-line-numbers | Show line numbers for =consult-goto-line= | -| consult-grep-max-columns | Maximal number of columns of the matching lines | -| consult-grep-args | Command line arguments for grep | -| consult-imenu-config | Mode-specific configuration for =consult-imenu= | -| consult-line-numbers-widen | Show absolute line numbers when narrowing is active | -| consult-line-start-from-top | Start the =consult-line= search from the top | -| consult-locate-args | Command line arguments for locate | -| consult-man-args | Command line arguments for man | -| consult-mode-command-filter | Filter for =consult-mode-command= | -| consult-mode-histories | Mode-specific history variables | -| consult-narrow-key | Narrowing prefix key during completion | -| consult-point-placement | Placement of the point when jumping to matches | -| consult-preview-key | Keys which triggers preview | -| consult-preview-allowed-hooks | List of =find-file= hooks to enable during preview | -| consult-preview-excluded-files | Regexps matched against file names during preview | -| consult-preview-max-count | Maximum number of files to keep open during preview | -| consult-preview-partial-size | Files larger than this size are previewed partially | -| consult-preview-partial-chunk | Size of the file chunk which is previewed partially | -| consult-preview-variables | Alist of variables to bind during preview | -| consult-project-buffer-sources | List of virtual project buffer sources | -| consult-project-function | Function which returns current project root | -| consult-register-prefix | Prefix string for register keys during completion | -| consult-ripgrep-args | Command line arguments for ripgrep | -| consult-themes | List of themes to be presented for selection | -| consult-widen-key | Widening key during completion | -| consult-yank-rotate | Rotate kill ring | - -** Fine-tuning of individual commands -:properties: -:alt_title: Fine-tuning -:description: Fine-grained configuration for special requirements -:end: - -*NOTE:* Consult supports fine-grained customization of individual commands. This -configuration feature exists for experienced users with special requirements. -There is the [[https://github.com/minad/consult/wiki][Consult wiki]], where we collect further configuration examples. - -Commands and buffer sources allow flexible, individual customization by using -the =consult-customize= macro. You can override any option passed to the internal -=consult--read= API. Note that since =consult--read= is part of the internal API, -options could be removed, replaced or renamed in future versions of the package. - -Useful options are: -- =:prompt= set the prompt string -- =:preview-key= set the preview key, default is =consult-preview-key= -- =:initial= set the initial input -- =:default= set the default value -- =:history= set the history variable symbol -- =:add-history= add items to the future history, for example symbol at point -- =:sort= enable or disable sorting -- =:group= set to nil to disable candidate grouping and titles. -- =:inherit-input-method= set to non-nil to inherit the input method. - -#+begin_src emacs-lisp -(consult-customize - ;; Disable preview for `consult-theme' completely. - consult-theme :preview-key nil - ;; Set preview for `consult-buffer' to key `M-.' - consult-buffer :preview-key "M-." - ;; For `consult-line' change the prompt and specify multiple preview - ;; keybindings. Note that you should bind and in the - ;; `minibuffer-local-completion-map' or `vertico-map' to the commands which - ;; select the previous or next candidate. - consult-line :prompt "Search: " - :preview-key '("S-" "S-")) -#+end_src - -The configuration values are evaluated at runtime, just before the completion -session is started. Therefore you can use for example =thing-at-point= to adjust -the initial input or the future history. - -#+begin_src emacs-lisp -(consult-customize - consult-line - :add-history (seq-some #'thing-at-point '(region symbol))) - -(defalias 'consult-line-thing-at-point 'consult-line) - -(consult-customize - consult-line-thing-at-point - :initial (thing-at-point 'symbol)) -#+end_src - -Generally it is possible to modify commands for your individual needs by the -following techniques: - -1. Use =consult-customize= in order to change the command or source settings. -2. Create your own wrapper function which passes modified arguments to the Consult functions. -3. Create your own buffer [[#multiple-sources][multi sources]] for =consult-buffer=. -4. Create advices to modify some internal behavior. -5. Write or propose a patch. - -* Recommended packages -:properties: -:description: Related packages recommended for installation -:end: - -I use and recommend this combination of packages: - -- consult: This package -- [[https://github.com/minad/vertico][vertico]]: Fast and minimal vertical completion system -- [[https://github.com/minad/marginalia][marginalia]]: Annotations for the completion candidates -- [[https://github.com/oantolin/embark][embark and embark-consult]]: Action commands, which can act on the completion candidates -- [[https://github.com/oantolin/orderless][orderless]]: Completion style which offers flexible candidate filtering -- [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep]]: Editing of grep buffers. Use with =consult-grep= via =embark-export=. - -There exist multiple fine completion UIs beside Vertico, which are supported by -Consult. Give them a try and find out which interaction model fits best for you. - -- The builtin completion UI, which pops up the =*Completions*= buffer. -- The builtin =icomplete-vertical-mode= in Emacs 28 or newer. -- [[https://git.sr.ht/~protesilaos/mct][mct by Protesilaos Stavrou]]: Minibuffer and Completions in Tandem, which builds - on the default completion UI. - -Note that all packages are independent and can be exchanged with alternative -components, since there exist no hard dependencies. Furthermore it is possible -to get started with only default completion and Consult and add more components -later to the mix. For example you can omit Marginalia if you don't need -annotations. I highly recommend the Embark package, but in order to familiarize -yourself with the other components, you can first start without it - or you could -use with Embark right away and add the other components later on. - -We document a [[https://github.com/minad/consult/wiki/Auxiliary-packages][list of auxiliary packages]] in the Consult wiki. These packages -integrate Consult with special programs or with other packages in the wider -Emacs ecosystem. - -* Bug reports -:properties: -:description: How to create reproducible bug reports -:end: - -If you find a bug or suspect that there is a problem with Consult, please carry -out the following steps: - -1. *Search through the issue tracker* if your issue has been reported before (and - has been resolved eventually) in the meantime. -2. *Remove all packages involved in the suspected bug from your installation.* -3. *Reinstall the newest version of all relevant packages*. Updating alone is not - sufficient, since package.el sometimes causes miscompilation. The list of - packages includes Consult, Compat, Vertico or other completion UIs, - Marginalia, Embark and Orderless. -4. Either use the default completion UI or ensure that exactly one of - =vertico-mode=, =mct-mode=, or =icomplete-mode= is enabled. The unsupported modes - =selectrum-mode=, =ivy-mode=, =helm-mode=, =ido-mode= and =ido-ubiquitous-mode= must be - disabled. -5. Ensure that the =completion-styles= variable is properly configured. Try to set - =completion-styles= to a list including =substring= or =orderless=. -6. Try to reproduce the issue with the newest stable Emacs version. Start a bare - bone Emacs instance with =emacs -Q= on the command line. Execute the following - minimal code snippets in the scratch buffer. This way we can exclude side - effects due to configuration settings. If other packages are relevant to - reproduce the issue, include them in the minimal configuration snippet. - -Minimal setup with Vertico for =emacs -Q=: -#+begin_src emacs-lisp -(package-initialize) -(require 'consult) -(require 'vertico) -(vertico-mode) -(setq completion-styles '(substring basic)) -#+end_src - -Minimal setup with the default completion system for =emacs -Q=: -#+begin_src emacs-lisp -(package-initialize) -(require 'consult) -(setq completion-styles '(substring basic)) -#+end_src - -Please provide the necessary important information with your bug report: - -- The minimal configuration snippet used to reproduce the issue. -- Your completion UI (Default completion, Vertico, Mct or Icomplete). -- A stack trace in case the bug triggers an exception. -- Your Emacs version, since bugs may be fixed or introduced in newer versions. -- Your operating system, since Emacs behavior varies subtly between Linux, Mac - and Windows. -- The package manager, e.g., straight.el or package.el, used to install the - Emacs packages, in order to exclude update issues. Did you install Consult as - part of the Doom Emacs distribution? -- Do you use Evil? Consult does not provide Evil integration out of the box, but - there is some support in [[https://github.com/emacs-evil/evil-collection][evil-collection]]. - -When evaluating Consult-related code snippets you should enable [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html][lexical binding]]. -Consult often relies on lambdas and lexical closures. - -* Contributions -:properties: -:description: Feature requests and pull requests -:end: - -Consult is a community effort, please participate in the discussions. -Contributions are welcome, but you may want to discuss potential contributions -first. Since this package is part of [[https://elpa.gnu.org/packages/consult.html][GNU ELPA]] contributions require a copyright -assignment to the FSF. - -If you have a proposal, take a look at the [[https://github.com/consult/issues][Consult issue tracker]] and the [[https://github.com/minad/consult/issues/6][Consult -wishlist]]. There have been many prior feature discussions. Please search through -the issue tracker, maybe your issue or feature request has already been -discussed. You can contribute to the [[https://github.com/minad/consult/wiki][Consult wiki]], in case you want to share -small configuration or command snippets. - -* Acknowledgments -:properties: -:description: Contributors and Sources of Inspiration -:end: - -This package took inspiration from [[https://github.com/abo-abo/swiper#counsel][Counsel]] by Oleh Krehel. Some of the Consult -commands originated in the Counsel package or the wiki of the Selectrum package. -This package exists only thanks to the help of these great contributors and -thanks to the feedback of many users. Thank you! - -Code contributions: [[https://github.com/aagon][Aymeric Agon-Rambosson]], [[https://github.com/amosbird][Amos Bird]], [[https://github.com/ashton314][Ashton Wiersdorf]], [[https://github.com/aspiers/][Adam -Spiers]], [[https://github.com/astoff][Augusto Stoffel]], [[https://github.com/clemera/][Clemens Radermacher]], [[https://github.com/fuzy112][Zhengyi]], [[https://github.com/geolessel][Geoffrey Lessel]], [[https://github.com/iostapyshyn][Illia -Ostapyshyn]], [[https://github.com/jakanakaevangeli][jakanakaevangeli]], [[https://github.com/jdtsmith][JD Smith]], [[https://github.com/jyp][Jean-Philippe Bernardy]], [[https://github.com/mattiasdrp][mattiasdrp]], -[[https://github.com/mohamed-abdelnour][Mohamed Abdelnour]], [[https://github.com/mohkale][Mohsin Kaleem]], [[https://github.com/noctuid][Fox Kiester]], [[https://github.com/oantolin/][Omar Antolín Camarena]], [[https://github.com/okamsn/][Earl -Hyatt]], [[https://github.com/omar-polo][Omar Polo]], [[https://github.com/piotrkwiecinski][Piotr Kwiecinski]], [[https://github.com/rswgnu][Robert Weiner]], [[https://github.com/s-kostyaev/][Sergey Kostyaev]], [[https://github.com/scvalex][Alexandru -Scvorțov]], [[https://github.com/tecosaur][Tecosaur]], [[https://github.com/thisirs][Sylvain Rousseau]], [[https://github.com/tomfitzhenry/][Tom Fitzhenry]], [[https://hg.serna.eu][Iñigo Serna]] and [[https://github.com/akreisher][Alex -Kreisher]]. - -Advice and useful discussions: [[https://github.com/Qkessler][Enrique Kessler Martínez]], [[https://github.com/alphapapa/][Adam Porter]], [[https://github.com/bdarcus][Bruce -d'Arcus]], [[https://github.com/clemera/][Clemens Radermacher]], [[https://github.com/dgutov/][Dmitry Gutov]], [[https://github.com/hmelman/][Howard Melman]], [[https://github.com/iyefrat][Itai Y. Efrat]], [[https://github.com/jdtsmith][JD -Smith]], [[https://github.com/manuel-uberti/][Manuel Uberti]], [[https://github.com/monnier/][Stefan Monnier]], [[https://github.com/oantolin/][Omar Antolín Camarena]], [[https://github.com/purcell/][Steve Purcell]], -[[https://github.com/raxod502][Radon Rosborough]], [[https://github.com/tomfitzhenry/][Tom Fitzhenry]] and [[https://protesilaos.com][Protesilaos Stavrou]]. - -#+html: blob - 2158c876254d5ff21b2fae26f9bbc13c9c8ff23c (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-autoloads.el +++ /dev/null @@ -1,445 +0,0 @@ -;;; consult-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from consult.el - -(autoload 'consult-completion-in-region "consult" "\ -Use minibuffer completion as the UI for `completion-at-point'. - -The function is called with 4 arguments: START END COLLECTION -PREDICATE. The arguments and expected return value are as -specified for `completion-in-region'. Use this function as a -value for `completion-in-region-function'. - -(fn START END COLLECTION &optional PREDICATE)") -(autoload 'consult-outline "consult" "\ -Jump to an outline heading, obtained by matching against `outline-regexp'. - -This command supports narrowing to a heading level and candidate -preview. The initial narrowing LEVEL can be given as prefix -argument. The symbol at point is added to the future history. - -(fn &optional LEVEL)" t) -(autoload 'consult-mark "consult" "\ -Jump to a marker in MARKERS list (defaults to buffer-local `mark-ring'). - -The command supports preview of the currently selected marker position. -The symbol at point is added to the future history. - -(fn &optional MARKERS)" t) -(autoload 'consult-global-mark "consult" "\ -Jump to a marker in MARKERS list (defaults to `global-mark-ring'). - -The command supports preview of the currently selected marker position. -The symbol at point is added to the future history. - -(fn &optional MARKERS)" t) -(autoload 'consult-line "consult" "\ -Search for a matching line. - -Depending on the setting `consult-point-placement' the command -jumps to the beginning or the end of the first match on the line -or the line beginning. The default candidate is the non-empty -line next to point. This command obeys narrowing. Optional -INITIAL input can be provided. The search starting point is -changed if the START prefix argument is set. The symbol at point -and the last `isearch-string' is added to the future history. - -(fn &optional INITIAL START)" t) -(autoload 'consult-line-multi "consult" "\ -Search for a matching line in multiple buffers. - -By default search across all project buffers. If the prefix -argument QUERY is non-nil, all buffers are searched. Optional -INITIAL input can be provided. The symbol at point and the last -`isearch-string' is added to the future history. In order to -search a subset of buffers, QUERY can be set to a plist according -to `consult--buffer-query'. - -(fn QUERY &optional INITIAL)" t) -(autoload 'consult-keep-lines "consult" "\ -Select a subset of the lines in the current buffer with live preview. - -The selected lines are kept and the other lines are deleted. When called -interactively, the lines selected are those that match the minibuffer input. In -order to match the inverse of the input, prefix the input with `! '. When -called from Elisp, the filtering is performed by a FILTER function. This -command obeys narrowing. - -FILTER is the filter function. -INITIAL is the initial input. - -(fn FILTER &optional INITIAL)" t) -(autoload 'consult-focus-lines "consult" "\ -Hide or show lines using overlays. - -The selected lines are shown and the other lines hidden. When called -interactively, the lines selected are those that match the minibuffer input. In -order to match the inverse of the input, prefix the input with `! '. With -optional prefix argument SHOW reveal the hidden lines. Alternatively the -command can be restarted to reveal the lines. When called from Elisp, the -filtering is performed by a FILTER function. This command obeys narrowing. - -FILTER is the filter function. -INITIAL is the initial input. - -(fn FILTER &optional SHOW INITIAL)" t) -(autoload 'consult-goto-line "consult" "\ -Read line number and jump to the line with preview. - -Enter either a line number to jump to the first column of the -given line or line:column in order to jump to a specific column. -Jump directly if a line number is given as prefix ARG. The -command respects narrowing and the settings -`consult-goto-line-numbers' and `consult-line-numbers-widen'. - -(fn &optional ARG)" t) -(autoload 'consult-recent-file "consult" "\ -Find recent file using `completing-read'." t) -(autoload 'consult-mode-command "consult" "\ -Run a command from any of the given MODES. - -If no MODES are specified, use currently active major and minor modes. - -(fn &rest MODES)" t) -(autoload 'consult-yank-from-kill-ring "consult" "\ -Select STRING from the kill ring and insert it. -With prefix ARG, put point at beginning, and mark at end, like `yank' does. - -This command behaves like `yank-from-kill-ring' in Emacs 28, which also offers -a `completing-read' interface to the `kill-ring'. Additionally the Consult -version supports preview of the selected string. - -(fn STRING &optional ARG)" t) -(autoload 'consult-yank-pop "consult" "\ -If there is a recent yank act like `yank-pop'. - -Otherwise select string from the kill ring and insert it. -See `yank-pop' for the meaning of ARG. - -This command behaves like `yank-pop' in Emacs 28, which also offers a -`completing-read' interface to the `kill-ring'. Additionally the Consult -version supports preview of the selected string. - -(fn &optional ARG)" t) -(autoload 'consult-yank-replace "consult" "\ -Select STRING from the kill ring. - -If there was no recent yank, insert the string. -Otherwise replace the just-yanked string with the selected string. - -There exists no equivalent of this command in Emacs 28. - -(fn STRING)" t) -(autoload 'consult-bookmark "consult" "\ -If bookmark NAME exists, open it, otherwise create a new bookmark with NAME. - -The command supports preview of file bookmarks and narrowing. See the -variable `consult-bookmark-narrow' for the narrowing configuration. - -(fn NAME)" t) -(autoload 'consult-complex-command "consult" "\ -Select and evaluate command from the command history. - -This command can act as a drop-in replacement for `repeat-complex-command'." t) -(autoload 'consult-history "consult" "\ -Insert string from HISTORY of current buffer. -In order to select from a specific HISTORY, pass the history -variable as argument. INDEX is the name of the index variable to -update, if any. BOL is the function which jumps to the beginning -of the prompt. See also `cape-history' from the Cape package. - -(fn &optional HISTORY INDEX BOL)" t) -(autoload 'consult-isearch-history "consult" "\ -Read a search string with completion from the Isearch history. - -This replaces the current search string if Isearch is active, and -starts a new Isearch session otherwise." t) -(autoload 'consult-minor-mode-menu "consult" "\ -Enable or disable minor mode. - -This is an alternative to `minor-mode-menu-from-indicator'." t) -(autoload 'consult-theme "consult" "\ -Disable current themes and enable THEME from `consult-themes'. - -The command supports previewing the currently selected theme. - -(fn THEME)" t) -(autoload 'consult-buffer "consult" "\ -Enhanced `switch-to-buffer' command with support for virtual buffers. - -The command supports recent files, bookmarks, views and project files as -virtual buffers. Buffers are previewed. Narrowing to buffers (b), files (f), -bookmarks (m) and project files (p) is supported via the corresponding -keys. In order to determine the project-specific files and buffers, the -`consult-project-function' is used. The virtual buffer SOURCES -default to `consult-buffer-sources'. See `consult--multi' for the -configuration of the virtual buffer sources. - -(fn &optional SOURCES)" t) -(autoload 'consult-project-buffer "consult" "\ -Enhanced `project-switch-to-buffer' command with support for virtual buffers. -The command may prompt you for a project directory if it is invoked from -outside a project. See `consult-buffer' for more details." t) -(autoload 'consult-buffer-other-window "consult" "\ -Variant of `consult-buffer', switching to a buffer in another window." t) -(autoload 'consult-buffer-other-frame "consult" "\ -Variant of `consult-buffer', switching to a buffer in another frame." t) -(autoload 'consult-buffer-other-tab "consult" "\ -Variant of `consult-buffer', switching to a buffer in another tab." t) -(autoload 'consult-grep "consult" "\ -Search with `grep' for files in DIR where the content matches a regexp. - -The initial input is given by the INITIAL argument. DIR can be -nil, a directory string or a list of file/directory paths. If -`consult-grep' is called interactively with a prefix argument, -the user can specify the directories or files to search in. -Multiple directories must be separated by comma in the -minibuffer, since they are read via `completing-read-multiple'. -By default the project directory is used if -`consult-project-function' is defined and returns non-nil. -Otherwise the `default-directory' is searched. - -The input string is split, the first part of the string (grep -input) is passed to the asynchronous grep process and the second -part of the string is passed to the completion-style filtering. - -The input string is split at a punctuation character, which is -given as the first character of the input string. The format is -similar to Perl-style regular expressions, e.g., /regexp/. -Furthermore command line options can be passed to grep, specified -behind --. The overall prompt input has the form -`#async-input -- grep-opts#filter-string'. - -Note that the grep input string is transformed from Emacs regular -expressions to Posix regular expressions. Always enter Emacs -regular expressions at the prompt. `consult-grep' behaves like -builtin Emacs search commands, e.g., Isearch, which take Emacs -regular expressions. Furthermore the asynchronous input split -into words, each word must match separately and in any order. -See `consult--regexp-compiler' for the inner workings. In order -to disable transformations of the grep input, adjust -`consult--regexp-compiler' accordingly. - -Here we give a few example inputs: - -#alpha beta : Search for alpha and beta in any order. -#alpha.*beta : Search for alpha before beta. -#\\(alpha\\|beta\\) : Search for alpha or beta (Note Emacs syntax!) -#word -- -C3 : Search for word, include 3 lines as context -#first#second : Search for first, quick filter for second. - -The symbol at point is added to the future history. - -(fn &optional DIR INITIAL)" t) -(autoload 'consult-git-grep "consult" "\ -Search with `git grep' for files in DIR with INITIAL input. -See `consult-grep' for details. - -(fn &optional DIR INITIAL)" t) -(autoload 'consult-ripgrep "consult" "\ -Search with `rg' for files in DIR with INITIAL input. -See `consult-grep' for details. - -(fn &optional DIR INITIAL)" t) -(autoload 'consult-find "consult" "\ -Search for files with `find' in DIR. -The file names must match the input regexp. INITIAL is the -initial minibuffer input. See `consult-grep' for details -regarding the asynchronous search and the arguments. - -(fn &optional DIR INITIAL)" t) -(autoload 'consult-fd "consult" "\ -Search for files with `fd' in DIR. -The file names must match the input regexp. INITIAL is the -initial minibuffer input. See `consult-grep' for details -regarding the asynchronous search and the arguments. - -(fn &optional DIR INITIAL)" t) -(autoload 'consult-locate "consult" "\ -Search with `locate' for files which match input given INITIAL input. - -The input is treated literally such that locate can take advantage of -the locate database index. Regular expressions would often force a slow -linear search through the entire database. The locate process is started -asynchronously, similar to `consult-grep'. See `consult-grep' for more -details regarding the asynchronous search. - -(fn &optional INITIAL)" t) -(autoload 'consult-man "consult" "\ -Search for man page given INITIAL input. - -The input string is not preprocessed and passed literally to the -underlying man commands. The man process is started asynchronously, -similar to `consult-grep'. See `consult-grep' for more details regarding -the asynchronous search. - -(fn &optional INITIAL)" t) -(register-definition-prefixes "consult" '("consult-")) - - -;;; Generated autoloads from consult-compile.el - -(autoload 'consult-compile-error "consult-compile" "\ -Jump to a compilation error in the current buffer. - -This command collects entries from compilation buffers and grep -buffers related to the current buffer. The command supports -preview of the currently selected error." t) -(register-definition-prefixes "consult-compile" '("consult-compile--")) - - -;;; Generated autoloads from consult-flymake.el - -(autoload 'consult-flymake "consult-flymake" "\ -Jump to Flymake diagnostic. -When PROJECT is non-nil then prompt with diagnostics from all -buffers in the current project instead of just the current buffer. - -(fn &optional PROJECT)" t) -(register-definition-prefixes "consult-flymake" '("consult-flymake--")) - - -;;; Generated autoloads from consult-imenu.el - -(autoload 'consult-imenu "consult-imenu" "\ -Select item from flattened `imenu' using `completing-read' with preview. - -The command supports preview and narrowing. See the variable -`consult-imenu-config', which configures the narrowing. -The symbol at point is added to the future history. - -See also `consult-imenu-multi'." t) -(autoload 'consult-imenu-multi "consult-imenu" "\ -Select item from the imenus of all buffers from the same project. - -In order to determine the buffers belonging to the same project, the -`consult-project-function' is used. Only the buffers with the -same major mode as the current buffer are used. See also -`consult-imenu' for more details. In order to search a subset of buffers, -QUERY can be set to a plist according to `consult--buffer-query'. - -(fn &optional QUERY)" t) -(register-definition-prefixes "consult-imenu" '("consult-imenu-")) - - -;;; Generated autoloads from consult-info.el - -(autoload 'consult-info "consult-info" "\ -Full text search through info MANUALS. - -(fn &rest MANUALS)" t) -(register-definition-prefixes "consult-info" '("consult-info--")) - - -;;; Generated autoloads from consult-kmacro.el - -(autoload 'consult-kmacro "consult-kmacro" "\ -Run a chosen keyboard macro. - -With prefix ARG, run the macro that many times. -Macros containing mouse clicks are omitted. - -(fn ARG)" t) -(register-definition-prefixes "consult-kmacro" '("consult-kmacro--")) - - -;;; Generated autoloads from consult-org.el - -(autoload 'consult-org-heading "consult-org" "\ -Jump to an Org heading. - -MATCH and SCOPE are as in `org-map-entries' and determine which -entries are offered. By default, all entries of the current -buffer are offered. - -(fn &optional MATCH SCOPE)" t) -(autoload 'consult-org-agenda "consult-org" "\ -Jump to an Org agenda heading. - -By default, all agenda entries are offered. MATCH is as in -`org-map-entries' and can used to refine this. - -(fn &optional MATCH)" t) -(register-definition-prefixes "consult-org" '("consult-org--")) - - -;;; Generated autoloads from consult-register.el - -(autoload 'consult-register-window "consult-register" "\ -Enhanced drop-in replacement for `register-preview'. - -BUFFER is the window buffer. -SHOW-EMPTY must be t if the window should be shown for an empty register list. - -(fn BUFFER &optional SHOW-EMPTY)") -(autoload 'consult-register-format "consult-register" "\ -Enhanced preview of register REG. -This function can be used as `register-preview-function'. -If COMPLETION is non-nil format the register for completion. - -(fn REG &optional COMPLETION)") -(autoload 'consult-register "consult-register" "\ -Load register and either jump to location or insert the stored text. - -This command is useful to search the register contents. For quick access -to registers it is still recommended to use the register functions -`consult-register-load' and `consult-register-store' or the built-in -built-in register access functions. The command supports narrowing, see -`consult-register--narrow'. Marker positions are previewed. See -`jump-to-register' and `insert-register' for the meaning of prefix ARG. - -(fn &optional ARG)" t) -(autoload 'consult-register-load "consult-register" "\ -Do what I mean with a REG. - -For a window configuration, restore it. For a number or text, insert it. -For a location, jump to it. See `jump-to-register' and `insert-register' -for the meaning of prefix ARG. - -(fn REG &optional ARG)" t) -(autoload 'consult-register-store "consult-register" "\ -Store register dependent on current context, showing an action menu. - -With an active region, store/append/prepend the contents, optionally -deleting the region when a prefix ARG is given. With a numeric prefix -ARG, store or add the number. Otherwise store point, frameset, window or -kmacro. - -(fn ARG)" t) -(register-definition-prefixes "consult-register" '("consult-register-")) - - -;;; Generated autoloads from consult-xref.el - -(autoload 'consult-xref "consult-xref" "\ -Show xrefs with preview in the minibuffer. - -This function can be used for `xref-show-xrefs-function'. -See `xref-show-xrefs-function' for the description of the -FETCHER and ALIST arguments. - -(fn FETCHER &optional ALIST)") -(register-definition-prefixes "consult-xref" '("consult-xref--")) - -;;; End of scraped data - -(provide 'consult-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; consult-autoloads.el ends here blob - 2d8c4bdf3c27f6c9b722e9376c583ebc6b926beb (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-compile.el +++ /dev/null @@ -1,127 +0,0 @@ -;;; consult-compile.el --- Provides the command `consult-compile-error' -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides the command `consult-compile-error'. This is an extra -;; package, to allow lazy loading of compile.el. The -;; `consult-compile-error' command is autoloaded. - -;;; Code: - -(require 'consult) -(require 'compile) - -(defvar consult-compile--history nil) - -(defconst consult-compile--narrow - '((?e . "Error") - (?w . "Warning") - (?i . "Info"))) - -(defun consult-compile--font-lock (str) - "Apply `font-lock' faces in STR, copy them to `face'." - (let ((pos 0) (len (length str))) - (while (< pos len) - (let* ((face (get-text-property pos 'font-lock-face str)) - (end (or (text-property-not-all pos len 'font-lock-face face str) len))) - (put-text-property pos end 'face face str) - (setq pos end))) - str)) - -(defun consult-compile--error-candidates (buffer) - "Return alist of errors and positions in BUFFER, a compilation buffer." - (with-current-buffer buffer - (let ((candidates) - (pos (point-min))) - (save-excursion - (while (setq pos (compilation-next-single-property-change pos 'compilation-message)) - (when-let (msg (get-text-property pos 'compilation-message)) - (goto-char pos) - (push (propertize - (consult-compile--font-lock (consult--buffer-substring pos (pos-eol))) - 'consult--type (pcase (compilation--message->type msg) - (0 ?i) - (1 ?w) - (_ ?e)) - 'consult--candidate (point-marker)) - candidates)))) - (nreverse candidates)))) - -(defun consult-compile--lookup (marker) - "Lookup error position given error MARKER." - (when-let (buffer (and marker (marker-buffer marker))) - (with-current-buffer buffer - (let ((next-error-highlight nil) - (compilation-current-error marker) - (overlay-arrow-position overlay-arrow-position)) - (ignore-errors - (save-window-excursion - (compilation-next-error-function 0) - (point-marker))))))) - -(defun consult-compile--compilation-buffers (file) - "Return a list of compilation buffers relevant to FILE." - (consult--buffer-query - :sort 'alpha :predicate - (lambda (buffer) - (with-current-buffer buffer - (and (compilation-buffer-internal-p) - (file-in-directory-p file default-directory)))))) - -(defun consult-compile--state () - "Like `consult--jump-state', also setting the current compilation error." - (let ((jump (consult--jump-state))) - (lambda (action marker) - (let ((pos (consult-compile--lookup marker))) - (when-let (buffer (and (eq action 'return) - marker - (marker-buffer marker))) - (with-current-buffer buffer - (setq compilation-current-error marker - overlay-arrow-position marker))) - (funcall jump action pos))))) - -;;;###autoload -(defun consult-compile-error () - "Jump to a compilation error in the current buffer. - -This command collects entries from compilation buffers and grep -buffers related to the current buffer. The command supports -preview of the currently selected error." - (interactive) - (consult--read - (or (mapcan #'consult-compile--error-candidates - (or (consult-compile--compilation-buffers - default-directory) - (user-error "No compilation buffers found for the current buffer"))) - (user-error "No compilation errors found")) - :prompt "Go to error: " - :category 'consult-compile-error - :sort nil - :require-match t - :history t ;; disable history - :lookup #'consult--lookup-candidate - :group (consult--type-group consult-compile--narrow) - :narrow (consult--type-narrow consult-compile--narrow) - :history '(:input consult-compile--history) - :state (consult-compile--state))) - -(provide 'consult-compile) -;;; consult-compile.el ends here blob - 3061ffe7426e58663cf9916e16b6e30e0783f04c (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-flymake.el +++ /dev/null @@ -1,116 +0,0 @@ -;;; consult-flymake.el --- Provides the command `consult-flymake' -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides the command `consult-flymake'. This is an extra package, -;; to allow lazy loading of flymake.el. The `consult-flymake' command -;; is autoloaded. - -;;; Code: - -(require 'consult) -(require 'flymake) -(eval-when-compile (require 'cl-lib)) - -(defconst consult-flymake--narrow - '((?e . "Error") - (?w . "Warning") - (?n . "Note"))) - -(defun consult-flymake--candidates (diags) - "Return Flymake errors from DIAGS as formatted candidates. -DIAGS should be a list of diagnostics as returned from `flymake-diagnostics'." - (let* ((diags - (mapcar - (lambda (diag) - (let ((buffer (flymake-diagnostic-buffer diag)) - (type (flymake-diagnostic-type diag))) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (save-excursion - (without-restriction - (goto-char (flymake-diagnostic-beg diag)) - (list (buffer-name buffer) - (line-number-at-pos) - type - (flymake-diagnostic-text diag) - (point-marker) - (flymake-diagnostic-end diag) - (pcase (flymake--lookup-type-property type 'flymake-category) - ('flymake-error ?e) - ('flymake-warning ?w) - (_ ?n))))))))) - diags)) - (diags (or (delq nil diags) - (user-error "No flymake errors (Status: %s)" - (if (seq-difference (flymake-running-backends) - (flymake-reporting-backends)) - 'running 'finished)))) - (buffer-width (cl-loop for x in diags maximize (length (nth 0 x)))) - (line-width (cl-loop for x in diags maximize (length (number-to-string (nth 1 x))))) - (fmt (format "%%-%ds %%-%dd %%-7s %%s" buffer-width line-width))) - (mapcar - (pcase-lambda (`(,buffer ,line ,type ,text ,beg ,end ,narrow)) - (propertize (format fmt buffer line - (propertize (format "%s" (flymake--lookup-type-property - type 'flymake-type-name type)) - 'face (flymake--lookup-type-property - type 'mode-line-face 'flymake-error)) - text) - 'consult--candidate (list beg (cons 0 (- end beg))) - 'consult--type narrow)) - ;; Sort by buffer, severity and position. - (sort diags - (pcase-lambda (`(,b1 _ ,t1 _ ,m1 _) `(,b2 _ ,t2 _ ,m2 _)) - (let ((s1 (flymake--severity t1)) - (s2 (flymake--severity t2))) - (or - (string-lessp b1 b2) - (and (string-equal b1 b2) - (or - (> s1 s2) - (and (= s1 s2) - (< m1 m2))))))))))) - -;;;###autoload -(defun consult-flymake (&optional project) - "Jump to Flymake diagnostic. -When PROJECT is non-nil then prompt with diagnostics from all -buffers in the current project instead of just the current buffer." - (interactive "P") - (consult--forbid-minibuffer) - (consult--read - (consult-flymake--candidates - (if-let (((and project (fboundp 'flymake--project-diagnostics))) - (project (project-current))) - (flymake--project-diagnostics project) - (flymake-diagnostics))) - :prompt "Flymake diagnostic: " - :category 'consult-flymake-error - :history t ;; disable history - :require-match t - :sort nil - :group (consult--type-group consult-flymake--narrow) - :narrow (consult--type-narrow consult-flymake--narrow) - :lookup #'consult--lookup-candidate - :state (consult--jump-state))) - -(provide 'consult-flymake) -;;; consult-flymake.el ends here blob - 3b072a60bc07611d89c53e55be057b219c5c074a (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-imenu.el +++ /dev/null @@ -1,260 +0,0 @@ -;;; consult-imenu.el --- Consult commands for imenu -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides imenu-related Consult commands. - -;;; Code: - -(require 'consult) -(require 'imenu) - -(defcustom consult-imenu-config - '((emacs-lisp-mode :toplevel "Functions" - :types ((?f "Functions" font-lock-function-name-face) - (?m "Macros" font-lock-function-name-face) - (?p "Packages" font-lock-constant-face) - (?t "Types" font-lock-type-face) - (?v "Variables" font-lock-variable-name-face)))) - "Imenu configuration, faces and narrowing keys used by `consult-imenu'. - -For each type a narrowing key and a name must be specified. The -face is optional. The imenu representation provided by the -backend usually puts functions directly at the toplevel. -`consult-imenu' moves them instead under the type specified by -:toplevel." - :type '(repeat (cons symbol plist)) - :group 'consult) - -(defface consult-imenu-prefix - '((t :inherit consult-key)) - "Face used to highlight imenu prefix in `consult-imenu'." - :group 'consult-faces) - -(defvar consult-imenu--history nil) -(defvar-local consult-imenu--cache nil) - -(defun consult-imenu--switch-buffer (name pos buf fn &rest args) - "Switch buffer before invoking special menu items. -NAME is the item name. -POS is the position. -BUF is the buffer. -FN is the original special item function. -ARGS are the arguments to the special item function." - (funcall consult--buffer-display buf) - (apply fn name pos args)) - -(defun consult-imenu--normalize (pos) - "Return normalized imenu POS." - (pcase pos - ;; Create marker from integer item - ((pred integerp) (setq pos (copy-marker pos))) - ;; Semantic uses overlay for positions - ((pred overlayp) (setq pos (copy-marker (overlay-start pos)))) - ;; Wrap special item - (`(,pos ,fn . ,args) - (setq pos `(,pos ,#'consult-imenu--switch-buffer ,(current-buffer) - ,fn ,@args)))) - (if (or (consp pos) - (eq imenu-default-goto-function #'imenu-default-goto-function)) - pos - (list pos #'consult-imenu--switch-buffer (current-buffer) - imenu-default-goto-function))) - -(defun consult-imenu--flatten (prefix face list types) - "Flatten imenu LIST. -PREFIX is prepended in front of all items. -FACE is the item face. -TYPES is the mode-specific types configuration." - (mapcan - (lambda (item) - (if (imenu--subalist-p item) - (let* ((name (concat (car item))) - (next-prefix name) - (next-face face)) - (add-face-text-property 0 (length name) - 'consult-imenu-prefix 'append name) - (if prefix - (setq next-prefix (concat prefix "/" name)) - (when-let (type (cdr (assoc name types))) - (put-text-property 0 (length name) 'consult--type (car type) name) - (setq next-face (cadr type)))) - (consult-imenu--flatten next-prefix next-face (cdr item) types)) - (list (cons - (if prefix - (let ((key (concat prefix " " (car item)))) - (add-face-text-property (1+ (length prefix)) (length key) - face 'append key) - key) - (car item)) - (consult-imenu--normalize (cdr item)))))) - list)) - -(defun consult-imenu--compute () - "Compute imenu candidates." - (consult--forbid-minibuffer) - (let* ((imenu-use-markers t) - ;; Generate imenu, see `imenu--make-index-alist'. - (items (imenu--truncate-items - (save-excursion - (without-restriction - (funcall imenu-create-index-function))))) - (config (cdr (seq-find (lambda (x) (derived-mode-p (car x))) consult-imenu-config)))) - ;; Fix toplevel items, e.g., emacs-lisp-mode toplevel items are functions - (when-let (toplevel (plist-get config :toplevel)) - (let ((tops (seq-remove (lambda (x) (listp (cdr x))) items)) - (rest (seq-filter (lambda (x) (listp (cdr x))) items))) - (setq items (nconc rest (and tops (list (cons toplevel tops))))))) - ;; Apply our flattening in order to ease searching the imenu. - (consult-imenu--flatten - nil nil items - (mapcar (pcase-lambda (`(,x ,y ,z)) (list y x z)) - (plist-get config :types))))) - -(defun consult-imenu--deduplicate (items) - "Deduplicate imenu ITEMS by appending a counter." - ;; Some imenu backends generate duplicate items (e.g. for overloaded methods in java) - (let ((ht (make-hash-table :test #'equal :size (length items)))) - (dolist (item items) - (if-let (count (gethash (car item) ht)) - (setcar item (format "%s (%s)" (car item) - (puthash (car item) (1+ count) ht))) - (puthash (car item) 0 ht))))) - -(defun consult-imenu--items () - "Return cached imenu candidates, may error." - (unless (equal (car consult-imenu--cache) (buffer-modified-tick)) - (setq consult-imenu--cache (cons (buffer-modified-tick) (consult-imenu--compute)))) - (cdr consult-imenu--cache)) - -(defun consult-imenu--items-safe () - "Return cached imenu candidates, will not error." - (condition-case err - (consult-imenu--items) - (t (message "Cannot create Imenu for buffer %s (%s)" - (buffer-name) (error-message-string err)) - nil))) - -(defun consult-imenu--multi-items (buffers) - "Return all imenu items from BUFFERS." - (consult--with-increased-gc - (let ((reporter (make-progress-reporter "Collecting" 0 (length buffers)))) - (prog1 - (apply #'append - (seq-map-indexed (lambda (buf idx) - (with-current-buffer buf - (prog1 (consult-imenu--items-safe) - (progress-reporter-update - reporter (1+ idx) (buffer-name))))) - buffers)) - (progress-reporter-done reporter))))) - -(defun consult-imenu--jump (item) - "Jump to imenu ITEM via `consult--jump'. -In contrast to the builtin `imenu' jump function, -this function can jump across buffers." - (pcase item - (`(,name ,pos ,fn . ,args) - (push-mark nil t) - (apply fn name pos args)) - (`(,_ . ,pos) - (consult--jump pos)) - (_ (error "Unknown imenu item: %S" item))) - (run-hooks 'imenu-after-jump-hook)) - -(defun consult-imenu--narrow () - "Return narrowing configuration for the current buffer." - (mapcar (lambda (x) (cons (car x) (cadr x))) - (plist-get (cdr (seq-find (lambda (x) (derived-mode-p (car x))) - consult-imenu-config)) - :types))) - -(defun consult-imenu--group () - "Create a imenu group function for the current buffer." - (when-let (narrow (consult-imenu--narrow)) - (lambda (cand transform) - (let ((type (get-text-property 0 'consult--type cand))) - (cond - ((and transform type) - (substring cand (1+ (next-single-property-change 0 'consult--type cand)))) - (transform cand) - (type (alist-get type narrow))))))) - -(defun consult-imenu--select (prompt items) - "Select from imenu ITEMS given PROMPT string." - (consult-imenu--deduplicate items) - (consult-imenu--jump - (consult--read - (or items (user-error "Imenu is empty")) - :state - (let ((preview (consult--jump-preview))) - (lambda (action cand) - ;; Only preview simple menu items which are markers, - ;; in order to avoid any bad side effects. - (funcall preview action (and (markerp (cdr cand)) (cdr cand))))) - :narrow - (when-let (narrow (consult-imenu--narrow)) - (list :predicate - (lambda (cand) - (eq (get-text-property 0 'consult--type (car cand)) consult--narrow)) - :keys narrow)) - :group (consult-imenu--group) - :prompt prompt - :require-match t - :category 'imenu - :lookup #'consult--lookup-cons - :history 'consult-imenu--history - :add-history (thing-at-point 'symbol) - :sort nil))) - -;;;###autoload -(defun consult-imenu () - "Select item from flattened `imenu' using `completing-read' with preview. - -The command supports preview and narrowing. See the variable -`consult-imenu-config', which configures the narrowing. -The symbol at point is added to the future history. - -See also `consult-imenu-multi'." - (interactive) - (consult-imenu--select - "Go to item: " - (consult--slow-operation "Building Imenu..." - (consult-imenu--items)))) - -;;;###autoload -(defun consult-imenu-multi (&optional query) - "Select item from the imenus of all buffers from the same project. - -In order to determine the buffers belonging to the same project, the -`consult-project-function' is used. Only the buffers with the -same major mode as the current buffer are used. See also -`consult-imenu' for more details. In order to search a subset of buffers, -QUERY can be set to a plist according to `consult--buffer-query'." - (interactive "P") - (unless (keywordp (car-safe query)) - (setq query (list :sort 'alpha :mode major-mode - :directory (and (not query) 'project)))) - (let ((buffers (consult--buffer-query-prompt "Go to item" query))) - (consult-imenu--select (car buffers) - (consult-imenu--multi-items (cdr buffers))))) - -(provide 'consult-imenu) -;;; consult-imenu.el ends here blob - 8c58caff145bcfeb1c0c42f662ebb45733b44fd6 (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-info.el +++ /dev/null @@ -1,181 +0,0 @@ -;;; consult-info.el --- Search through the info manuals -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides the command `consult-info'. This is an extra package, -;; to allow lazy loading of info.el. The `consult-info' command -;; is autoloaded. - -;;; Code: - -(require 'consult) -(require 'info) - -(defvar consult-info--history nil) - -(defun consult-info--candidates (manuals input) - "Dynamically find lines in MANUALS matching INPUT." - (pcase-let* ((`(,regexps . ,hl) - (funcall consult--regexp-compiler input 'emacs t)) - (re (concat "\\(\^_\n\\(?:.*Node:[ \t]*\\([^,\t\n]+\\)\\)?.*\n\\)\\|" (car regexps))) - (candidates nil) - (cand-idx 0) - (last-node nil) - (full-node nil)) - (pcase-dolist (`(,manual . ,buf) manuals) - (with-current-buffer buf - (setq last-node nil full-node nil) - (widen) - (goto-char (point-min)) - ;; TODO Info has support for subfiles, which is currently not supported - ;; by the `consult-info' search routine. Fortunately most (or all?) - ;; Emacs info files are generated with the --no-split option. See the - ;; comment in doc/emacs/Makefile.in. Given the computing powers these - ;; days split info files are probably also not necessary anymore. - ;; However it could happen that info files installed as part of the - ;; Linux distribution are split. - (while (and (not (eobp)) (re-search-forward re nil t)) - (if (match-end 1) - (progn - (if-let ((node (match-string 2))) - (unless (equal node last-node) - (setq full-node (concat "(" manual ")" node) - last-node node)) - (setq last-node nil full-node nil)) - (goto-char (1+ (pos-eol)))) - (let ((bol (pos-bol)) - (eol (pos-eol))) - (goto-char bol) - (when (and - full-node - ;; Information separator character - (>= (- (point) 2) (point-min)) - (not (eq (char-after (- (point) 2)) ?\^_)) - ;; Non-blank line, only printable characters on the line. - (not (looking-at-p "^\\s-*$")) - (looking-at-p "^[[:print:]]*$") - ;; Matches all regexps - (seq-every-p (lambda (r) - (goto-char bol) - (re-search-forward r eol t)) - (cdr regexps))) - (let ((cand (concat - (funcall hl (buffer-substring-no-properties bol eol)) - (consult--tofu-encode cand-idx)))) - (put-text-property 0 1 'consult--info (list full-node bol buf) cand) - (cl-incf cand-idx) - (push cand candidates))) - (goto-char (1+ eol))))))) - (nreverse candidates))) - -(defun consult-info--position (cand) - "Return position information for CAND." - (when-let ((pos (and cand (get-text-property 0 'consult--info cand))) - (matches (consult--point-placement cand 0)) - (dest (+ (cadr pos) (car matches)))) - `( ,(cdr matches) ,dest . ,pos))) - -(defun consult-info--action (cand) - "Jump to info CAND." - (pcase (consult-info--position cand) - (`( ,_matches ,pos ,node ,_bol ,_buf) - (info node) - (widen) - (goto-char pos) - (Info-select-node) - (run-hooks 'consult-after-jump-hook)))) - -(defun consult-info--state () - "Info manual preview state." - (let ((preview (consult--jump-preview))) - (lambda (action cand) - (pcase action - ('preview - (setq cand (consult-info--position cand)) - (funcall preview 'preview - (pcase cand - (`(,matches ,pos ,_node ,_bol ,buf) - (cons (set-marker (make-marker) pos buf) matches)))) - (let (Info-history Info-history-list Info-history-forward) - (when cand (ignore-errors (Info-select-node))))) - ('return - (consult-info--action cand)))))) - -(defun consult-info--group (cand transform) - "Return title for CAND or TRANSFORM the candidate." - (if transform cand - (car (get-text-property 0 'consult--info cand)))) - -(defun consult-info--prepare-buffers (manuals fun) - "Prepare buffers for MANUALS and call FUN with buffers." - (declare (indent 1)) - (let (buffers) - (unwind-protect - (let ((reporter (make-progress-reporter "Preparing" 0 (length manuals)))) - (consult--with-increased-gc - (seq-do-indexed - (lambda (manual idx) - (push (cons manual (generate-new-buffer (format "*info-preview-%s*" manual))) - buffers) - (with-current-buffer (cdar buffers) - (let (Info-history Info-history-list Info-history-forward) - (Info-mode) - (Info-find-node manual "Top"))) - (progress-reporter-update reporter (1+ idx) manual)) - manuals)) - (progress-reporter-done reporter) - (funcall fun (reverse buffers))) - (dolist (buf buffers) - (kill-buffer (cdr buf)))))) - -;;;###autoload -(defun consult-info (&rest manuals) - "Full text search through info MANUALS." - (interactive - (if Info-current-file - (list (file-name-base Info-current-file)) - (info-initialize) - (completing-read-multiple - "Info Manuals: " - (info--manual-names current-prefix-arg) - nil t))) - (consult-info--prepare-buffers manuals - (lambda (buffers) - (consult--read - (consult--dynamic-collection - (apply-partially #'consult-info--candidates buffers)) - :state (consult-info--state) - :prompt - (format "Info (%s): " - (string-join (if (length> manuals 3) - `(,@(seq-take manuals 3) ,"…") - manuals) - ", ")) - :require-match t - :sort nil - :category 'consult-info - :history '(:input consult-info--history) - :group #'consult-info--group - :initial (consult--async-split-initial "") - :add-history (consult--async-split-thingatpt 'symbol) - :lookup #'consult--lookup-member)))) - -(provide 'consult-info) -;;; consult-info.el ends here blob - 3e3418507c285db574b7d8b552de1e7f7e8b11d3 (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-kmacro.el +++ /dev/null @@ -1,90 +0,0 @@ -;;; consult-kmacro.el --- Provides the command `consult-kmacro' -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides the command `consult-kmacro'. This is an extra package, -;; to allow lazy loading of kmacro.el. The `consult-kmacro' command -;; is autoloaded. - -;;; Code: - -(require 'consult) -(require 'kmacro) - -(defvar consult-kmacro--history nil) - -(defun consult-kmacro--candidates () - "Return alist of kmacros and indices." - (thread-last - ;; List of macros - (append (and last-kbd-macro (list (kmacro-ring-head))) kmacro-ring) - ;; Emacs 29 uses OClosures. I like OClosures but it would have been better - ;; if public APIs wouldn't change like that. - (mapcar (lambda (x) - (if (eval-when-compile (> emacs-major-version 28)) - (list (kmacro--keys x) (kmacro--counter x) (kmacro--format x) x) - `(,@x ,x)))) - ;; Filter mouse clicks - (seq-remove (lambda (x) (seq-some #'mouse-event-p (car x)))) - ;; Format macros - (mapcar (pcase-lambda (`(,keys ,counter ,format ,km)) - (propertize - (format-kbd-macro keys 1) - 'consult--candidate km - 'consult-kmacro--annotation - ;; If the counter is 0 and the counter format is its default, - ;; then there is a good chance that the counter isn't actually - ;; being used. This can only be wrong when a user - ;; intentionally starts the counter with a negative value and - ;; then increments it to 0. - (cond - ((not (equal format "%d")) ;; show counter for non-default format - (format " (counter=%d, format=%s) " counter format)) - ((/= counter 0) ;; show counter if non-zero - (format " (counter=%d)" counter)))))) - (delete-dups))) - -;;;###autoload -(defun consult-kmacro (arg) - "Run a chosen keyboard macro. - -With prefix ARG, run the macro that many times. -Macros containing mouse clicks are omitted." - (interactive "p") - (let ((km (consult--read - (or (consult-kmacro--candidates) - (user-error "No keyboard macros defined")) - :prompt "Keyboard macro: " - :category 'consult-kmacro - :require-match t - :sort nil - :history 'consult-kmacro--history - :annotate - (lambda (cand) - (get-text-property 0 'consult-kmacro--annotation cand)) - :lookup #'consult--lookup-candidate))) - ;; Kmacros are lambdas (oclosures) on Emacs 29 - (funcall (if (eval-when-compile (> emacs-major-version 28)) - km - (kmacro-lambda-form km)) - arg))) - -(provide 'consult-kmacro) -;;; consult-kmacro.el ends here blob - 905da6d28432dd3fee40cfeeb23c45635cb10c1e (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-org.el +++ /dev/null @@ -1,144 +0,0 @@ -;;; consult-org.el --- Consult commands for org-mode -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides a `completing-read' interface for Org mode navigation. -;; This is an extra package, to allow lazy loading of Org. - -;;; Code: - -(require 'consult) -(require 'org) - -(defvar consult-org--history nil) - -(defun consult-org--narrow () - "Narrowing configuration for `consult-org' commands." - (let ((todo-kws - (seq-filter - (lambda (x) (<= ?a (car x) ?z)) - (mapcar (lambda (s) - (pcase-let ((`(,a ,b) (split-string s "("))) - (cons (downcase (string-to-char (or b a))) a))) - (apply #'append (mapcar #'cdr org-todo-keywords)))))) - (list :predicate - (lambda (cand) - (pcase-let ((`(,level ,todo ,prio . ,_) - (get-text-property 0 'consult-org--heading cand))) - (cond - ((<= ?1 consult--narrow ?9) (<= level (- consult--narrow ?0))) - ((<= ?A consult--narrow ?Z) (eq prio consult--narrow)) - (t (equal todo (alist-get consult--narrow todo-kws)))))) - :keys - (nconc (mapcar (lambda (c) (cons c (format "Level %c" c))) - (number-sequence ?1 ?9)) - (mapcar (lambda (c) (cons c (format "Priority %c" c))) - (number-sequence (max ?A org-highest-priority) - (min ?Z org-lowest-priority))) - todo-kws)))) - -(defun consult-org--headings (prefix match scope &rest skip) - "Return a list of Org heading candidates. - -If PREFIX is non-nil, prefix the candidates with the buffer name. -MATCH, SCOPE and SKIP are as in `org-map-entries'." - (let (buffer (idx 0)) - (apply - #'org-map-entries - (lambda () - ;; Reset the cache when the buffer changes, since `org-get-outline-path' uses the cache - (unless (eq buffer (buffer-name)) - (setq buffer (buffer-name) - org-outline-path-cache nil)) - (pcase-let* ((`(_ ,level ,todo ,prio ,_hl ,tags) (org-heading-components)) - (tags (if org-use-tag-inheritance - (when-let ((tags (org-get-tags))) - (concat ":" (string-join tags ":") ":")) - tags)) - (cand (org-format-outline-path - (org-get-outline-path 'with-self 'use-cache) - most-positive-fixnum))) - (when todo - (put-text-property 0 (length todo) 'face (org-get-todo-face todo) todo)) - (when tags - (put-text-property 0 (length tags) 'face 'org-tag tags)) - (setq cand (concat (and prefix buffer) (and prefix " ") cand (and tags " ") - tags (consult--tofu-encode idx))) - (cl-incf idx) - (add-text-properties 0 1 - `(org-marker ,(point-marker) - consult-org--heading (,level ,todo ,prio . ,buffer)) - cand) - cand)) - match scope skip))) - -(defun consult-org--annotate (cand) - "Annotate CAND for `consult-org-heading'." - (pcase-let ((`(,_level ,todo ,prio . ,_) - (get-text-property 0 'consult-org--heading cand))) - (consult--annotate-align - cand - (concat todo - (and prio (format #(" [#%c]" 1 6 (face org-priority)) prio)))))) - -(defun consult-org--group (cand transform) - "Return title for CAND or TRANSFORM the candidate." - (pcase-let ((`(,_level ,_todo ,_prio . ,buffer) - (get-text-property 0 'consult-org--heading cand))) - (if transform (substring cand (1+ (length buffer))) buffer))) - -;;;###autoload -(defun consult-org-heading (&optional match scope) - "Jump to an Org heading. - -MATCH and SCOPE are as in `org-map-entries' and determine which -entries are offered. By default, all entries of the current -buffer are offered." - (interactive (unless (derived-mode-p #'org-mode) - (user-error "Must be called from an Org buffer"))) - (let ((prefix (not (memq scope '(nil tree region region-start-level file))))) - (consult--read - (consult--slow-operation "Collecting headings..." - (or (consult-org--headings prefix match scope) - (user-error "No headings"))) - :prompt "Go to heading: " - :category 'org-heading - :sort nil - :require-match t - :history '(:input consult-org--history) - :narrow (consult-org--narrow) - :state (consult--jump-state) - :annotate #'consult-org--annotate - :group (and prefix #'consult-org--group) - :lookup (apply-partially #'consult--lookup-prop 'org-marker)))) - -;;;###autoload -(defun consult-org-agenda (&optional match) - "Jump to an Org agenda heading. - -By default, all agenda entries are offered. MATCH is as in -`org-map-entries' and can used to refine this." - (interactive) - (unless org-agenda-files - (user-error "No agenda files")) - (consult-org-heading match 'agenda)) - -(provide 'consult-org) -;;; consult-org.el ends here blob - 2fc021cb8e243620881f3b18e278103baf674897 (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from consult.el -*- no-byte-compile: t -*- -(define-package "consult" "1.5" "Consulting completing-read" '((emacs "27.1") (compat "29.1.4.4")) :commit "d8888bb67f881a3c4855c9ce7224de18a7dc3901" :maintainer '("Daniel Mendler" . "mail@daniel-mendler.de") :keywords '("matching" "files" "completion") :url "https://github.com/minad/consult") blob - ec01cae0439fdcfb05f8ca5032d64aaa5da65028 (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-register.el +++ /dev/null @@ -1,329 +0,0 @@ -;;; consult-register.el --- Consult commands for registers -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides register-related Consult commands. - -;;; Code: - -(require 'consult) -(require 'kmacro) - -(defcustom consult-register-prefix #("#" 0 1 (face consult-key)) - "Prepend prefix in front of register keys during completion." - :type '(choice (const nil) string) - :group 'consult) - -(defvar consult-register--narrow - '((?n . "Number") - (?s . "String") - (?p . "Point") - (?r . "Rectangle") - (?t . "Frameset") - (?k . "Kmacro") - (?f . "File") - (?b . "Buffer") - (?w . "Window")) - "Register type names. -Each element of the list must have the form (char . name).") - -(cl-defun consult-register--format-value (val) - "Format generic register VAL as string." - (with-output-to-string (register-val-describe val nil))) - -(cl-defgeneric consult-register--describe (val) - "Describe generic register VAL." - (list (consult-register--format-value val))) - -(cl-defmethod consult-register--describe ((val number)) - "Describe numeric register VAL." - (list (consult-register--format-value val) 'consult--type ?n)) - -(cl-defmethod consult-register--describe ((val string)) - "Describe string register VAL." - (list val 'consult--type - (if (eq (car (get-text-property 0 'yank-handler val)) - 'rectangle--insert-for-yank) - ?r ?s))) - -(cl-defmethod consult-register--describe ((val marker)) - "Describe marker register VAL." - (with-current-buffer (marker-buffer val) - (save-excursion - (without-restriction - (goto-char val) - (let* ((line (line-number-at-pos)) - (str (propertize (consult--line-with-mark val) - 'consult-location (cons val line)))) - (list (consult--format-file-line-match (buffer-name) line str) - 'multi-category `(consult-location . ,str) - 'consult--type ?p)))))) - -(defmacro consult-register--describe-kmacro () - "Generate method which describes kmacro register." - `(cl-defmethod consult-register--describe ((val ,(if (< emacs-major-version 30) 'kmacro-register 'kmacro))) - (list (consult-register--format-value val) 'consult--type ?k))) -(consult-register--describe-kmacro) - -(cl-defmethod consult-register--describe ((val (head file))) - "Describe file register VAL." - (list (propertize (abbreviate-file-name (cdr val)) 'face 'consult-file) - 'consult--type ?f 'multi-category `(file . ,(cdr val)))) - -(cl-defmethod consult-register--describe ((val (head buffer))) - "Describe buffer register VAL." - (list (propertize (cdr val) 'face 'consult-buffer) - 'consult--type ?f 'multi-category `(buffer . ,(cdr val)))) - -(cl-defmethod consult-register--describe ((val (head file-query))) - "Describe file-query register VAL." - (list (format "%s at position %d" - (propertize (abbreviate-file-name (cadr val)) - 'face 'consult-file) - (caddr val)) - 'consult--type ?f 'multi-category `(file . ,(cadr val)))) - -(cl-defmethod consult-register--describe ((val cons)) - "Describe rectangle or window-configuration register VAL." - (cond - ((stringp (car val)) - (list (string-join val "\n") 'consult--type ?r)) - ((window-configuration-p (car val)) - (list (consult-register--format-value val) - 'consult--type ?w)) - (t (list (consult-register--format-value val))))) - -(with-eval-after-load 'frameset - (cl-defmethod consult-register--describe ((val frameset-register)) - "Describe frameset register VAL." - (list (consult-register--format-value val) 'consult--type ?t))) - -;;;###autoload -(defun consult-register-window (buffer &optional show-empty) - "Enhanced drop-in replacement for `register-preview'. - -BUFFER is the window buffer. -SHOW-EMPTY must be t if the window should be shown for an empty register list." - (let ((regs (consult-register--alist 'noerror)) - (separator - (and (display-graphic-p) - (propertize #(" \n" 0 1 (display (space :align-to right))) - 'face '(:inherit consult-separator :height 1 :underline t))))) - (when (or show-empty regs) - (with-current-buffer-window buffer - (cons 'display-buffer-at-bottom - '((window-height . fit-window-to-buffer) - (preserve-size . (nil . t)))) - nil - (setq-local cursor-in-non-selected-windows nil - mode-line-format nil - truncate-lines t - window-min-height 1 - window-resize-pixelwise t) - (insert (mapconcat - (lambda (reg) - (concat (funcall register-preview-function reg) separator)) - regs nil)))))) - -;;;###autoload -(defun consult-register-format (reg &optional completion) - "Enhanced preview of register REG. -This function can be used as `register-preview-function'. -If COMPLETION is non-nil format the register for completion." - (pcase-let* ((`(,key . ,val) reg) - (key-str (propertize (single-key-description key) 'face 'consult-key)) - (key-len (max 3 (length key-str))) - (`(,str . ,props) (consult-register--describe val))) - (when (string-search "\n" str) - (let* ((lines (seq-take (seq-remove #'string-blank-p (split-string str "\n")) 3)) - (space (cl-loop for x in lines minimize (string-match-p "[^ ]" x)))) - (setq str (mapconcat (lambda (x) (substring x space)) - lines (concat "\n" (make-string (1+ key-len) ?\s)))))) - (setq str (concat - (and completion consult-register-prefix) - key-str (make-string (- key-len (length key-str)) ?\s) " " - str (and (not completion) "\n"))) - (when completion - (add-text-properties - 0 (length str) - `(consult--candidate ,(car reg) ,@props) - str)) - str)) - -(defun consult-register--alist (&optional noerror filter) - "Return register list, sorted and filtered with FILTER. -Raise an error if the list is empty and NOERROR is nil." - (or (sort (cl-loop for reg in register-alist - ;; Sometimes, registers are made without a `cdr' or with - ;; invalid markers. Such registers don't do anything, and - ;; can be ignored. - if (and (cdr reg) - (or (not (markerp (cdr reg))) (marker-buffer (cdr reg))) - (or (not filter) (funcall filter reg))) - collect reg) - #'car-less-than-car) - (and (not noerror) (user-error "All registers are empty")))) - -(defun consult-register--candidates (&optional filter) - "Return formatted completion candidates, filtered with FILTER." - (mapcar (lambda (reg) (consult-register-format reg 'completion)) - (consult-register--alist nil filter))) - -;;;###autoload -(defun consult-register (&optional arg) - "Load register and either jump to location or insert the stored text. - -This command is useful to search the register contents. For quick access -to registers it is still recommended to use the register functions -`consult-register-load' and `consult-register-store' or the built-in -built-in register access functions. The command supports narrowing, see -`consult-register--narrow'. Marker positions are previewed. See -`jump-to-register' and `insert-register' for the meaning of prefix ARG." - (interactive "P") - (consult-register-load - (consult--read - (consult-register--candidates) - :prompt "Register: " - :category 'multi-category - :state - (let ((preview (consult--jump-preview))) - (lambda (action cand) - ;; Preview only markers - (funcall preview action - (when-let (reg (get-register cand)) - (and (markerp reg) reg))))) - :group (consult--type-group consult-register--narrow) - :narrow (consult--type-narrow consult-register--narrow) - :sort nil - :require-match t - :history t ;; disable history - :lookup #'consult--lookup-candidate) - arg)) - -;;;###autoload -(defun consult-register-load (reg &optional arg) - "Do what I mean with a REG. - -For a window configuration, restore it. For a number or text, insert it. -For a location, jump to it. See `jump-to-register' and `insert-register' -for the meaning of prefix ARG." - (interactive - (list - (and (consult-register--alist) - (register-read-with-preview "Load register: ")) - current-prefix-arg)) - (condition-case err - (jump-to-register reg arg) - (user-error - (unless (string-search "access aborted" (error-message-string err)) - (insert-register reg (not arg)))))) - -(defun consult-register--action (action-list) - "Read register key and execute action from ACTION-LIST. - -This function is derived from `register-read-with-preview'." - (let* ((buffer "*Register Preview*") - (prefix (car action-list)) - (action-list (cdr action-list)) - (action (car (nth 0 action-list))) - (preview - (lambda () - (unless (get-buffer-window buffer) - (register-preview buffer 'show-empty) - (when-let (win (get-buffer-window buffer)) - (with-selected-window win - (let ((inhibit-read-only t)) - (goto-char (point-max)) - (insert - (propertize (concat prefix ": ") 'face 'consult-help) - (mapconcat - (lambda (x) - (concat (propertize (format "M-%c" (car x)) 'face 'consult-key) - " " (propertize (cadr x) 'face 'consult-help))) - action-list " ")) - (fit-window-to-buffer))))))) - (timer (when (numberp register-preview-delay) - (run-at-time register-preview-delay nil preview))) - (help-chars (seq-remove #'get-register (cons help-char help-event-list))) - key reg) - (unwind-protect - (while (not reg) - (while (memq (setq key - (read-key (propertize (caddr (assq action action-list)) - 'face 'minibuffer-prompt))) - help-chars) - (funcall preview)) - (setq key (if (and (eql key ?\e) (characterp last-input-event)) - ;; in terminal Emacs M-letter is read as two keys, ESC and the letter, - ;; use what would have been read in graphical Emacs - (logior #x8000000 last-input-event) - last-input-event)) - (cond - ((or (eq ?\C-g key) - (eq 'escape key) - (eq ?\C-\[ key)) - (keyboard-quit)) - ((and (numberp key) (assq (logxor #x8000000 key) action-list)) - (setq action (logxor #x8000000 key))) - ((characterp key) - (setq reg key)) - (t (user-error "Non-character input")))) - (when (timerp timer) - (cancel-timer timer)) - (let ((w (get-buffer-window buffer))) - (when (window-live-p w) - (delete-window w))) - (when (get-buffer buffer) - (kill-buffer buffer))) - (when reg - (funcall (cadddr (assq action action-list)) reg)))) - -;;;###autoload -(defun consult-register-store (arg) - "Store register dependent on current context, showing an action menu. - -With an active region, store/append/prepend the contents, optionally -deleting the region when a prefix ARG is given. With a numeric prefix -ARG, store or add the number. Otherwise store point, frameset, window or -kmacro." - (interactive "P") - (consult-register--action - (cond - ((use-region-p) - (let ((beg (region-beginning)) - (end (region-end))) - `("Region" - (?c "copy" "Copy region to register: " ,(lambda (r) (copy-to-register r beg end arg t))) - (?a "append" "Append region to register: " ,(lambda (r) (append-to-register r beg end arg))) - (?p "prepend" "Prepend region to register: " ,(lambda (r) (prepend-to-register r beg end arg)))))) - ((numberp arg) - `(,(format "Number %s" arg) - (?s "store" ,(format "Store %s in register: " arg) ,(lambda (r) (number-to-register arg r))) - (?a "add" ,(format "Add %s to register: " arg) ,(lambda (r) (increment-register arg r))))) - (t - `("Store" - (?p "point" "Point to register: " ,#'point-to-register) - (?f "file" "File to register: " ,(lambda (r) (set-register r `(file . ,(buffer-file-name))))) - (?t "frameset" "Frameset to register: " ,#'frameset-to-register) - (?w "window" "Window to register: " ,#'window-configuration-to-register) - ,@(and last-kbd-macro `((?k "kmacro" "Kmacro to register: " ,#'kmacro-to-register)))))))) - -(provide 'consult-register) -;;; consult-register.el ends here blob - 7188e3727037bce321a02f8f52ed55a427969031 (mode 644) blob + /dev/null --- elpa/consult-1.5/consult-xref.el +++ /dev/null @@ -1,122 +0,0 @@ -;;; consult-xref.el --- Xref integration for Consult -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides Xref integration for Consult. This is an extra package, to -;; allow lazy loading of xref.el. The `consult-xref' function is -;; autoloaded. - -;;; Code: - -(require 'consult) -(require 'xref) - -(defvar consult-xref--history nil) - -(defvar consult-xref--fetcher nil - "The current xref fetcher. -The fetch is stored globally such that it can be accessed by - Embark for `embark-export'.") - -(defun consult-xref--candidates () - "Return xref candidate list." - (let ((root (consult--project-root))) - (mapcar (lambda (xref) - (let* ((loc (xref-item-location xref)) - (group (if (fboundp 'xref--group-name-for-display) - ;; This function is available in xref 1.3.2 - (xref--group-name-for-display - (xref-location-group loc) root) - (xref-location-group loc))) - (cand (consult--format-file-line-match - group - (or (xref-location-line loc) 0) - (xref-item-summary xref)))) - (add-text-properties - 0 1 `(consult-xref ,xref consult--prefix-group ,group) cand) - cand)) - (funcall consult-xref--fetcher)))) - -(defun consult-xref--preview (display) - "Xref preview with DISPLAY function." - (let ((open (consult--temporary-files)) - (preview (consult--jump-preview))) - (lambda (action cand) - (unless cand - (funcall open)) - (let ((consult--buffer-display display)) - (funcall preview action - (when-let (loc (and cand (eq action 'preview) - (xref-item-location cand))) - (let ((type (type-of loc))) - ;; Only preview file and buffer markers - (pcase type - ('xref-buffer-location - (xref-location-marker loc)) - ((or 'xref-file-location 'xref-etags-location) - (consult--marker-from-line-column - (funcall open - ;; xref-location-group returns the file name - (let ((xref-file-name-display 'abs)) - (xref-location-group loc))) - (xref-location-line loc) - (if (eq type 'xref-file-location) - (xref-file-location-column loc) - 0))))))))))) - -;;;###autoload -(defun consult-xref (fetcher &optional alist) - "Show xrefs with preview in the minibuffer. - -This function can be used for `xref-show-xrefs-function'. -See `xref-show-xrefs-function' for the description of the -FETCHER and ALIST arguments." - (let* ((consult-xref--fetcher fetcher) - (candidates (consult-xref--candidates)) - (display (alist-get 'display-action alist))) - (unless candidates - (user-error "No xref locations")) - (xref-pop-to-location - (if (cdr candidates) - (apply - #'consult--read - candidates - (append - (consult--customize-get #'consult-xref) - (list - :prompt "Go to xref: " - :history 'consult-xref--history - :require-match t - :sort nil - :category 'consult-xref - :group #'consult--prefix-group - :state - ;; do not preview other frame - (when-let (fun (pcase-exhaustive display - ('frame nil) - ('window #'switch-to-buffer-other-window) - ('nil #'switch-to-buffer))) - (consult-xref--preview fun)) - :lookup (apply-partially #'consult--lookup-prop 'consult-xref)))) - (get-text-property 0 'consult-xref (car candidates))) - display))) - -(provide 'consult-xref) -;;; consult-xref.el ends here blob - b24db7598a515424c8b57e41107d914c55a93edd (mode 644) blob + /dev/null --- elpa/consult-1.5/consult.el +++ /dev/null @@ -1,5234 +0,0 @@ -;;; consult.el --- Consulting completing-read -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; Author: Daniel Mendler and Consult contributors -;; Maintainer: Daniel Mendler -;; Created: 2020 -;; Version: 1.5 -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4")) -;; Homepage: https://github.com/minad/consult -;; Keywords: matching, files, completion - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Consult implements a set of `consult-' commands, which aim to -;; improve the way you use Emacs. The commands are founded on -;; `completing-read', which selects from a list of candidate strings. -;; Consult provides an enhanced buffer switcher `consult-buffer' and -;; search and navigation commands like `consult-imenu' and -;; `consult-line'. Searching through multiple files is supported by the -;; asynchronous `consult-grep' command. Many Consult commands support -;; previewing candidates. If a candidate is selected in the completion -;; view, the buffer shows the candidate immediately. - -;; The Consult commands are compatible with multiple completion systems -;; based on the Emacs `completing-read' API, including the default -;; completion system, Vertico, Mct and Icomplete. - -;; See the README for an overview of the available Consult commands and -;; the documentation of the configuration and installation of the -;; package. - -;; The full list of contributors can be found in the acknowledgments -;; section of the README. - -;;; Code: - -(eval-when-compile - (require 'cl-lib) - (require 'subr-x)) -(require 'compat) -(require 'bookmark) - -(defgroup consult nil - "Consulting `completing-read'." - :link '(info-link :tag "Info Manual" "(consult)") - :link '(url-link :tag "Homepage" "https://github.com/minad/consult") - :link '(emacs-library-link :tag "Library Source" "consult.el") - :group 'files - :group 'outlines - :group 'minibuffer - :prefix "consult-") - -;;;; Customization - -(defcustom consult-narrow-key nil - "Prefix key for narrowing during completion. - -Good choices for this key are \"<\" and \"C-+\" for example. The -key must be a string accepted by `key-valid-p'." - :type '(choice key (const :tag "None" nil))) - -(defcustom consult-widen-key nil - "Key used for widening during completion. - -If this key is unset, defaults to twice the `consult-narrow-key'. -The key must be a string accepted by `key-valid-p'." - :type '(choice key (const :tag "None" nil))) - -(defcustom consult-project-function - #'consult--default-project-function - "Function which returns project root directory. -The function takes one boolean argument MAY-PROMPT. If -MAY-PROMPT is non-nil, the function may ask the prompt the user -for a project directory. The root directory is used by -`consult-buffer' and `consult-grep'." - :type `(choice - (const :tag "Default project function" ,#'consult--default-project-function) - (function :tag "Custom function") - (const :tag "No project integration" nil))) - -(defcustom consult-async-refresh-delay 0.2 - "Refreshing delay of the completion UI for asynchronous commands. - -The completion UI is only updated every -`consult-async-refresh-delay' seconds. This applies to -asynchronous commands like for example `consult-grep'." - :type '(float :tag "Delay in seconds")) - -(defcustom consult-async-input-throttle 0.5 - "Input throttle for asynchronous commands. - -The asynchronous process is started only every -`consult-async-input-throttle' seconds. This applies to asynchronous -commands, e.g., `consult-grep'." - :type '(float :tag "Delay in seconds")) - -(defcustom consult-async-input-debounce 0.2 - "Input debounce for asynchronous commands. - -The asynchronous process is started only when there has not been new -input for `consult-async-input-debounce' seconds. This applies to -asynchronous commands, e.g., `consult-grep'." - :type '(float :tag "Delay in seconds")) - -(defcustom consult-async-min-input 3 - "Minimum number of characters needed, before asynchronous process is called. - -This applies to asynchronous commands, e.g., `consult-grep'." - :type '(natnum :tag "Number of characters")) - -(defcustom consult-async-split-style 'perl - "Async splitting style, see `consult-async-split-styles-alist'." - :type '(choice (const :tag "No splitting" nil) - (const :tag "Comma" comma) - (const :tag "Semicolon" semicolon) - (const :tag "Perl" perl))) - -(defcustom consult-async-split-styles-alist - `((nil :function ,#'consult--split-nil) - (comma :separator ?, :function ,#'consult--split-separator) - (semicolon :separator ?\; :function ,#'consult--split-separator) - (perl :initial "#" :function ,#'consult--split-perl)) - "Async splitting styles." - :type '(alist :key-type symbol :value-type plist)) - -(defcustom consult-mode-histories - '((eshell-mode eshell-history-ring eshell-history-index eshell-bol) - (comint-mode comint-input-ring comint-input-ring-index comint-bol) - (term-mode term-input-ring term-input-ring-index term-bol)) - "Alist of mode histories (mode history index bol). -The histories can be rings or lists. Index, if provided, is a -variable to set to the index of the selection within the ring or -list. Bol, if provided is a function which jumps to the beginning -of the line after the prompt." - :type '(alist :key-type symbol - :value-type (group :tag "Include Index" - (symbol :tag "List/Ring") - (symbol :tag "Index Variable") - (symbol :tag "Bol Function")))) - -(defcustom consult-themes nil - "List of themes (symbols or regexps) to be presented for selection. -nil shows all `custom-available-themes'." - :type '(repeat (choice symbol regexp))) - -(defcustom consult-after-jump-hook (list #'recenter) - "Function called after jumping to a location. - -Commonly used functions for this hook are `recenter' and -`reposition-window'. You may want to add a function which pulses -the current line, e.g., `pulse-momentary-highlight-one-line' is -supported on Emacs 28 and newer. The hook called during preview -and for the jump after selection." - :type 'hook) - -(defcustom consult-line-start-from-top nil - "Start search from the top if non-nil. -Otherwise start the search at the current line and wrap around." - :type 'boolean) - -(defcustom consult-point-placement 'match-beginning - "Where to leave point when jumping to a match. -This setting affects the command `consult-line' and the `consult-grep' variants." - :type '(choice (const :tag "Beginning of the line" line-beginning) - (const :tag "Beginning of the match" match-beginning) - (const :tag "End of the match" match-end))) - -(defcustom consult-line-numbers-widen t - "Show absolute line numbers when narrowing is active. - -See also `display-line-numbers-widen'." - :type 'boolean) - -(defcustom consult-goto-line-numbers t - "Show line numbers for `consult-goto-line'." - :type 'boolean) - -(defcustom consult-fontify-preserve t - "Preserve fontification for line-based commands." - :type 'boolean) - -(defcustom consult-fontify-max-size 1048576 - "Buffers larger than this byte limit are not fontified. - -This is necessary in order to prevent a large startup time -for navigation commands like `consult-line'." - :type '(natnum :tag "Buffer size in bytes")) - -(defcustom consult-buffer-filter - '("\\` " - "\\`\\*Completions\\*\\'" - "\\`\\*Flymake log\\*\\'" - "\\`\\*Semantic SymRef\\*\\'" - "\\`\\*tramp/.*\\*\\'") - "Filter regexps for `consult-buffer'. - -The default setting is to filter ephemeral buffer names beginning -with a space character, the *Completions* buffer and a few log -buffers. The regular expressions are matched case sensitively." - :type '(repeat regexp)) - -(defcustom consult-buffer-sources - '(consult--source-hidden-buffer - consult--source-modified-buffer - consult--source-buffer - consult--source-recent-file - consult--source-file-register - consult--source-bookmark - consult--source-project-buffer-hidden - consult--source-project-recent-file-hidden) - "Sources used by `consult-buffer'. -See also `consult-project-buffer-sources'. -See `consult--multi' for a description of the source data structure." - :type '(repeat symbol)) - -(defcustom consult-project-buffer-sources - '(consult--source-project-buffer - consult--source-project-recent-file) - "Sources used by `consult-project-buffer'. -See also `consult-buffer-sources'. -See `consult--multi' for a description of the source data structure." - :type '(repeat symbol)) - -(defcustom consult-mode-command-filter - '(;; Filter commands - "-mode\\'" "--" - ;; Filter whole features - simple mwheel time so-long recentf tab-bar tab-line) - "Filter commands for `consult-mode-command'." - :type '(repeat (choice symbol regexp))) - -(defcustom consult-grep-max-columns 300 - "Maximal number of columns of grep output." - :type 'natnum) - -(defconst consult--grep-match-regexp - "\\`\\(?:\\./\\)?\\([^\n\0]+\\)\0\\([0-9]+\\)\\([-:\0]\\)" - "Regexp used to match file and line of grep output.") - -(defcustom consult-grep-args - '("grep" (consult--grep-exclude-args) - "--null --line-buffered --color=never --ignore-case\ - --with-filename --line-number -I -r") - "Command line arguments for grep, see `consult-grep'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-git-grep-args - "git --no-pager grep --null --color=never --ignore-case\ - --extended-regexp --line-number -I" - "Command line arguments for git-grep, see `consult-git-grep'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-ripgrep-args - "rg --null --line-buffered --color=never --max-columns=1000 --path-separator /\ - --smart-case --no-heading --with-filename --line-number --search-zip" - "Command line arguments for ripgrep, see `consult-ripgrep'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-find-args - "find . -not ( -path */.[A-Za-z]* -prune )" - "Command line arguments for find, see `consult-find'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-fd-args - '((if (executable-find "fdfind" 'remote) "fdfind" "fd") - "--full-path --color=never") - "Command line arguments for fd, see `consult-fd'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-locate-args - "locate --ignore-case" ;; --existing not supported by Debian plocate - "Command line arguments for locate, see `consult-locate'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-man-args - "man -k" - "Command line arguments for man, see `consult-man'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-preview-key 'any - "Preview trigger keys, can be nil, `any', a single key or a list of keys. -Debouncing can be specified via the `:debounce' attribute. The -individual keys must be strings accepted by `key-valid-p'." - :type '(choice (const :tag "Any key" any) - (list :tag "Debounced" - (const :debounce) - (float :tag "Seconds" 0.1) - (const any)) - (const :tag "No preview" nil) - (key :tag "Key") - (repeat :tag "List of keys" key))) - -(defcustom consult-preview-partial-size 1048576 - "Files larger than this byte limit are previewed partially." - :type '(natnum :tag "File size in bytes")) - -(defcustom consult-preview-partial-chunk 102400 - "Partial preview chunk size in bytes. -If a file is larger than `consult-preview-partial-size' only the -chunk from the beginning of the file is previewed." - :type '(natnum :tag "Chunk size in bytes")) - -(defcustom consult-preview-max-count 10 - "Number of file buffers to keep open temporarily during preview." - :type '(natnum :tag "Number of buffers")) - -(defcustom consult-preview-excluded-files - '("\\`/[^/|:]+:") ;; Do not preview remote files - "List of regexps matched against names of files, which are not previewed." - :type '(repeat regexp)) - -(defcustom consult-preview-allowed-hooks - '(global-font-lock-mode-check-buffers - save-place-find-file-hook) - "List of `find-file' hooks, which should be executed during file preview." - :type '(repeat symbol)) - -(defcustom consult-preview-variables - '((inhibit-message . t) - (enable-dir-local-variables . nil) - (enable-local-variables . :safe) - (non-essential . t) - (delay-mode-hooks . t)) - "Variables which are bound for file preview." - :type '(alist :key-type symbol)) - -(defcustom consult-bookmark-narrow - `((?f "File" bookmark-default-handler) - (?h "Help" help-bookmark-jump Info-bookmark-jump - Man-bookmark-jump woman-bookmark-jump) - (?p "Picture" image-bookmark-jump) - (?d "Docview" doc-view-bookmark-jump) - (?m "Mail" gnus-summary-bookmark-jump) - (?s "Eshell" eshell-bookmark-jump) - (?w "Web" eww-bookmark-jump xwidget-webkit-bookmark-jump-handler) - (?v "VC Directory" vc-dir-bookmark-jump) - (nil "Other")) - "Bookmark narrowing configuration. - -Each element of the list must have the form (char name handlers...)." - :type '(alist :key-type character :value-type (cons string (repeat function)))) - -(defcustom consult-yank-rotate - (if (boundp 'yank-from-kill-ring-rotate) - yank-from-kill-ring-rotate - t) - "Rotate the `kill-ring' in the `consult-yank' commands." - :type 'boolean) - -;;;; Faces - -(defgroup consult-faces nil - "Faces used by Consult." - :group 'consult - :group 'faces) - -(defface consult-preview-line - '((t :inherit consult-preview-insertion :extend t)) - "Face used for line previews.") - -(defface consult-highlight-match - '((t :inherit match)) - "Face used to highlight matches in the completion candidates. -Used for example by `consult-grep'.") - -(defface consult-highlight-mark - '((t :inherit consult-highlight-match)) - "Face used for mark positions in completion candidates. -Used for example by `consult-mark'. The face should be different -than the `cursor' face to avoid confusion.") - -(defface consult-preview-match - '((t :inherit isearch)) - "Face used for match previews, e.g., in `consult-line'.") - -(defface consult-preview-insertion - '((t :inherit region)) - "Face used for previews of text to be inserted. -Used by `consult-completion-in-region', `consult-yank' and `consult-history'.") - -(defface consult-narrow-indicator - '((t :inherit warning)) - "Face used for the narrowing indicator.") - -(defface consult-async-running - '((t :inherit consult-narrow-indicator)) - "Face used if asynchronous process is running.") - -(defface consult-async-finished - '((t :inherit success)) - "Face used if asynchronous process has finished.") - -(defface consult-async-failed - '((t :inherit error)) - "Face used if asynchronous process has failed.") - -(defface consult-async-split - '((t :inherit font-lock-negation-char-face)) - "Face used to highlight punctuation character.") - -(defface consult-help - '((t :inherit shadow)) - "Face used to highlight help, e.g., in `consult-register-store'.") - -(defface consult-key - '((t :inherit font-lock-keyword-face)) - "Face used to highlight keys, e.g., in `consult-register'.") - -(defface consult-line-number - '((t :inherit consult-key)) - "Face used to highlight location line in `consult-global-mark'.") - -(defface consult-file - '((t :inherit font-lock-function-name-face)) - "Face used to highlight files in `consult-buffer'.") - -(defface consult-grep-context - '((t :inherit shadow)) - "Face used to highlight grep context in `consult-grep'.") - -(defface consult-bookmark - '((t :inherit font-lock-constant-face)) - "Face used to highlight bookmarks in `consult-buffer'.") - -(defface consult-buffer - '((t)) - "Face used to highlight buffers in `consult-buffer'.") - -(defface consult-line-number-prefix - '((t :inherit line-number)) - "Face used to highlight line number prefixes.") - -(defface consult-line-number-wrapped - '((t :inherit consult-line-number-prefix :inherit font-lock-warning-face)) - "Face used to highlight line number prefixes after wrap around.") - -(defface consult-separator - '((((class color) (min-colors 88) (background light)) - :foreground "#ccc") - (((class color) (min-colors 88) (background dark)) - :foreground "#333")) - "Face used for thin line separators in `consult-register-window'.") - -;;;; Input history variables - -(defvar consult--path-history nil) -(defvar consult--grep-history nil) -(defvar consult--find-history nil) -(defvar consult--man-history nil) -(defvar consult--line-history nil) -(defvar consult--line-multi-history nil) -(defvar consult--theme-history nil) -(defvar consult--minor-mode-menu-history nil) -(defvar consult--buffer-history nil) - -;;;; Internal variables - -(defvar consult--regexp-compiler - #'consult--default-regexp-compiler - "Regular expression compiler used by `consult-grep' and other commands. -The function must return a list of regular expressions and a highlighter -function.") - -(defvar consult--customize-alist - ;; Disable preview in frames, since `consult--jump-preview' does not properly - ;; clean up. See gh:minad/consult#593. This issue should better be fixed in - ;; `consult--jump-preview'. - `((,#'consult-buffer-other-frame :preview-key nil) - (,#'consult-buffer-other-tab :preview-key nil)) - "Command configuration alist for fine-grained configuration. - -Each element of the list must have the form (command-name plist...). The -options set here will be evaluated and passed to `consult--read', when -called from the corresponding command. Note that the options depend on -the private `consult--read' API and should not be considered as stable -as the public API.") - -(defvar consult--buffer-display #'switch-to-buffer - "Buffer display function.") - -(defvar consult--completion-candidate-hook - (list #'consult--default-completion-minibuffer-candidate - #'consult--default-completion-list-candidate) - "Get candidate from completion system.") - -(defvar consult--completion-refresh-hook nil - "Refresh completion system.") - -(defvar-local consult--preview-function nil - "Minibuffer-local variable which exposes the current preview function. -This function can be called by custom completion systems from -outside the minibuffer.") - -(defvar consult--annotate-align-step 10 - "Round candidate width.") - -(defvar consult--annotate-align-width 0 - "Maximum candidate width used for annotation alignment.") - -(defconst consult--tofu-char #x200000 - "Special character used to encode line prefixes for disambiguation. -We use invalid characters outside the Unicode range.") - -(defconst consult--tofu-range #x100000 - "Special character range.") - -(defvar-local consult--narrow nil - "Current narrowing key.") - -(defvar-local consult--narrow-keys nil - "Narrowing prefixes of the current completion.") - -(defvar-local consult--narrow-predicate nil - "Narrowing predicate of the current completion.") - -(defvar-local consult--narrow-overlay nil - "Narrowing indicator overlay.") - -(defvar consult--gc-threshold (* 64 1024 1024) - "Large GC threshold for temporary increase.") - -(defvar consult--gc-percentage 0.5 - "Large GC percentage for temporary increase.") - -(defvar consult--process-chunk (* 1024 1024) - "Increase process output chunk size.") - -(defvar consult--async-log - " *consult-async*" - "Buffer for async logging output used by `consult--async-process'.") - -(defvar-local consult--focus-lines-overlays nil - "Overlays used by `consult-focus-lines'.") - -(defvar-local consult--org-fold-regions nil - "Stored regions for the org-fold API.") - -;;;; Miscellaneous helper functions - -(defun consult--key-parse (key) - "Parse KEY or signal error if invalid." - (unless (key-valid-p key) - (error "%S is not a valid key definition; see `key-valid-p'" key)) - (key-parse key)) - -(defun consult--in-buffer (fun &optional buffer) - "Ensure that FUN is executed inside BUFFER." - (unless buffer (setq buffer (current-buffer))) - (lambda (&rest args) - (with-current-buffer buffer - (apply fun args)))) - -(defun consult--completion-table-in-buffer (table &optional buffer) - "Ensure that completion TABLE is executed inside BUFFER." - (if (functionp table) - (consult--in-buffer - (lambda (str pred action) - (let ((result (funcall table str pred action))) - (pcase action - ('metadata - (setq result - (mapcar - (lambda (x) - (if (and (string-suffix-p "-function" (symbol-name (car-safe x))) (cdr x)) - (cons (car x) (consult--in-buffer (cdr x))) - x)) - result))) - ((and 'completion--unquote (guard (functionp (cadr result)))) - (cl-callf consult--in-buffer (cadr result) buffer) - (cl-callf consult--in-buffer (cadddr result) buffer))) - result)) - buffer) - table)) - -(defun consult--build-args (arg) - "Return ARG as a flat list of split strings. - -Turn ARG into a list, and for each element either: -- split it if it a string. -- eval it if it is an expression." - (seq-mapcat (lambda (x) - (if (stringp x) - (split-string-and-unquote x) - (ensure-list (eval x 'lexical)))) - (ensure-list arg))) - -(defun consult--command-split (str) - "Return command argument and options list given input STR." - (save-match-data - (let ((opts (when (string-match " +--\\( +\\|\\'\\)" str) - (prog1 (substring str (match-end 0)) - (setq str (substring str 0 (match-beginning 0))))))) - ;; split-string-and-unquote fails if the quotes are invalid. Ignore it. - (cons str (and opts (ignore-errors (split-string-and-unquote opts))))))) - -(defmacro consult--keep! (list form) - "Evaluate FORM for every element of LIST and keep the non-nil results." - (declare (indent 1)) - (cl-with-gensyms (head prev result) - `(let* ((,head (cons nil ,list)) - (,prev ,head)) - (while (cdr ,prev) - (if-let (,result (let ((it (cadr ,prev))) ,form)) - (progn - (pop ,prev) - (setcar ,prev ,result)) - (setcdr ,prev (cddr ,prev)))) - (setq ,list (cdr ,head)) - nil))) - -;; Upstream bug#46326, Consult issue gh:minad/consult#193. -(defmacro consult--minibuffer-with-setup-hook (fun &rest body) - "Variant of `minibuffer-with-setup-hook' using a symbol and `fset'. - -This macro is only needed to prevent memory leaking issues with -the upstream `minibuffer-with-setup-hook' macro. -FUN is the hook function and BODY opens the minibuffer." - (declare (indent 1) (debug t)) - (let ((hook (gensym "hook")) - (append)) - (when (eq (car-safe fun) :append) - (setq append '(t) fun (cadr fun))) - `(let ((,hook (make-symbol "consult--minibuffer-setup-hook"))) - (fset ,hook (lambda () - (remove-hook 'minibuffer-setup-hook ,hook) - (funcall ,fun))) - (unwind-protect - (progn - (add-hook 'minibuffer-setup-hook ,hook ,@append) - ,@body) - (remove-hook 'minibuffer-setup-hook ,hook))))) - -(defun consult--completion-filter (pattern cands category _highlight) - "Filter CANDS with PATTERN. - -CATEGORY is the completion category, used to find the completion style via -`completion-category-defaults' and `completion-category-overrides'. -HIGHLIGHT must be non-nil if the resulting strings should be highlighted." - ;; completion-all-completions returns an improper list - ;; where the last link is not necessarily nil. - (nconc (completion-all-completions pattern cands nil (length pattern) - `(metadata (category . ,category))) - nil)) - -(defun consult--completion-filter-complement (pattern cands category _highlight) - "Filter CANDS with complement of PATTERN. -See `consult--completion-filter' for the arguments CATEGORY and HIGHLIGHT." - (let ((ht (consult--string-hash (consult--completion-filter pattern cands category nil)))) - (seq-remove (lambda (x) (gethash x ht)) cands))) - -(defun consult--completion-filter-dispatch (pattern cands category highlight) - "Filter CANDS with PATTERN with optional complement. -Either using `consult--completion-filter' or -`consult--completion-filter-complement', depending on if the pattern starts -with a bang. See `consult--completion-filter' for the arguments CATEGORY and -HIGHLIGHT." - (cond - ((string-match-p "\\`!? ?\\'" pattern) cands) ;; empty pattern - ((string-prefix-p "! " pattern) (consult--completion-filter-complement - (substring pattern 2) cands category nil)) - (t (consult--completion-filter pattern cands category highlight)))) - -(defmacro consult--each-line (beg end &rest body) - "Iterate over each line. - -The line beginning/ending BEG/END is bound in BODY." - (declare (indent 2)) - (cl-with-gensyms (max) - `(save-excursion - (let ((,beg (point-min)) (,max (point-max)) end) - (while (< ,beg ,max) - (goto-char ,beg) - (setq ,end (pos-eol)) - ,@body - (setq ,beg (1+ ,end))))))) - -(defun consult--display-width (string) - "Compute width of STRING taking display and invisible properties into account." - (let ((pos 0) (width 0) (end (length string))) - (while (< pos end) - (let ((nextd (next-single-property-change pos 'display string end)) - (display (get-text-property pos 'display string))) - (if (stringp display) - (setq width (+ width (string-width display)) - pos nextd) - (while (< pos nextd) - (let ((nexti (next-single-property-change pos 'invisible string nextd))) - (unless (get-text-property pos 'invisible string) - (setq width (+ width (compat-call string-width string pos nexti)))) - (setq pos nexti)))))) - width)) - -(defun consult--string-hash (strings) - "Create hash table from STRINGS." - (let ((ht (make-hash-table :test #'equal :size (length strings)))) - (dolist (str strings) - (puthash str t ht)) - ht)) - -(defmacro consult--local-let (binds &rest body) - "Buffer local let BINDS of dynamic variables in BODY." - (declare (indent 1)) - (let ((buffer (gensym "buffer")) - (local (mapcar (lambda (x) (cons (gensym "local") (car x))) binds))) - `(let ((,buffer (current-buffer)) - ,@(mapcar (lambda (x) `(,(car x) (local-variable-p ',(cdr x)))) local)) - (unwind-protect - (progn - ,@(mapcar (lambda (x) `(make-local-variable ',(car x))) binds) - (let (,@binds) - ,@body)) - (when (buffer-live-p ,buffer) - (with-current-buffer ,buffer - ,@(mapcar (lambda (x) - `(unless ,(car x) - (kill-local-variable ',(cdr x)))) - local))))))) - -(defvar consult--fast-abbreviate-file-name nil) -(defun consult--fast-abbreviate-file-name (name) - "Return abbreviate file NAME. -This function is a pure variant of `abbreviate-file-name', which -does not access the file system. This is important if we require -that the operation is fast, even for remote paths or paths on -network file systems." - (save-match-data - (let (case-fold-search) ;; Assume that file system is case sensitive. - (setq name (directory-abbrev-apply name)) - (if (string-match (with-memoization consult--fast-abbreviate-file-name - (directory-abbrev-make-regexp (expand-file-name "~"))) - name) - (concat "~" (substring name (match-beginning 1))) - name)))) - -(defun consult--left-truncate-file (file) - "Return abbreviated file name of FILE for use in `completing-read' prompt." - (save-match-data - (let ((afile (abbreviate-file-name file))) - (if (string-match "/\\([^/]+\\)/\\([^/]+/?\\)\\'" afile) - (propertize (format "…/%s/%s" (match-string 1 afile) (match-string 2 afile)) - 'help-echo afile) - afile)))) - -(defun consult--directory-prompt (prompt dir) - "Return prompt, paths and default directory. - -PROMPT is the prompt prefix. The directory is appended to the -prompt prefix. For projects only the project name is shown. The -`default-directory' is not shown. Other directories are -abbreviated and only the last two path components are shown. - -If DIR is a string, it is returned as default directory. If DIR -is a list of strings, the list is returned as search paths. If -DIR is nil the `consult-project-function' is tried to retrieve -the default directory. If no project is found the -`default-directory' is returned as is. Otherwise the user is -asked for the directories or files to search via -`completing-read-multiple'." - (let* ((paths nil) - (dir - (pcase dir - ((pred stringp) dir) - ('nil (or (consult--project-root) default-directory)) - (_ - (pcase (if (stringp (car-safe dir)) - dir - ;; Preserve this-command across `completing-read-multiple' call, - ;; such that `consult-customize' continues to work. - (let ((this-command this-command) - (def (abbreviate-file-name default-directory)) - ;; TODO: `minibuffer-completing-file-name' is - ;; mostly deprecated, but still in use. Packages - ;; should instead use the completion metadata. - (minibuffer-completing-file-name t) - (ignore-case read-file-name-completion-ignore-case)) - (consult--minibuffer-with-setup-hook - (lambda () - (setq-local completion-ignore-case ignore-case) - (set-syntax-table minibuffer-local-filename-syntax)) - (completing-read-multiple "Directories or files: " - #'completion-file-name-table - nil t def 'consult--path-history def)))) - ((and `(,p) (guard (file-directory-p p))) p) - (ps (setq paths (mapcar (lambda (p) - (file-relative-name (expand-file-name p))) - ps)) - default-directory))))) - (edir (file-name-as-directory (expand-file-name dir))) - (pdir (let ((default-directory edir)) - ;; Bind default-directory in order to find the project - (consult--project-root)))) - (list - (format "%s (%s): " prompt - (pcase paths - (`(,p) (consult--left-truncate-file p)) - (`(,p . ,_) - (format "%d paths, %s, …" (length paths) (consult--left-truncate-file p))) - ((guard (equal edir pdir)) (concat "Project " (consult--project-name pdir))) - (_ (consult--left-truncate-file edir)))) - (or paths '(".")) - edir))) - -(defun consult--default-project-function (may-prompt) - "Return project root directory. -When no project is found and MAY-PROMPT is non-nil ask the user." - (when-let (proj (project-current may-prompt)) - (cond - ((fboundp 'project-root) (project-root proj)) - ((fboundp 'project-roots) (car (project-roots proj)))))) - -(defun consult--project-root (&optional may-prompt) - "Return project root as absolute path. -When no project is found and MAY-PROMPT is non-nil ask the user." - ;; Preserve this-command across project selection, - ;; such that `consult-customize' continues to work. - (let ((this-command this-command)) - (when-let (root (and consult-project-function - (funcall consult-project-function may-prompt))) - (expand-file-name root)))) - -(defun consult--project-name (dir) - "Return the project name for DIR." - (if (string-match "/\\([^/]+\\)/\\'" dir) - (propertize (match-string 1 dir) 'help-echo (abbreviate-file-name dir)) - dir)) - -(defun consult--format-file-line-match (file line match) - "Format string FILE:LINE:MATCH with faces." - (setq line (number-to-string line) - match (concat file ":" line ":" match) - file (length file)) - (put-text-property 0 file 'face 'consult-file match) - (put-text-property (1+ file) (+ 1 file (length line)) 'face 'consult-line-number match) - match) - -(defun consult--make-overlay (beg end &rest props) - "Make consult overlay between BEG and END with PROPS." - (let ((ov (make-overlay beg end))) - (while props - (overlay-put ov (car props) (cadr props)) - (setq props (cddr props))) - ov)) - -(defun consult--remove-dups (list) - "Remove duplicate strings from LIST." - (delete-dups (copy-sequence list))) - -(defsubst consult--in-range-p (pos) - "Return t if position POS lies in range `point-min' to `point-max'." - (<= (point-min) pos (point-max))) - -(defun consult--completion-window-p () - "Return non-nil if the selected window belongs to the completion UI." - (or (eq (selected-window) (active-minibuffer-window)) - (eq #'completion-list-mode (buffer-local-value 'major-mode (window-buffer))))) - -(defun consult--original-window () - "Return window which was just selected just before the minibuffer was entered. -In contrast to `minibuffer-selected-window' never return nil and -always return an appropriate non-minibuffer window." - (or (minibuffer-selected-window) - (if (window-minibuffer-p (selected-window)) - (next-window) - (selected-window)))) - -(defun consult--forbid-minibuffer () - "Raise an error if executed from the minibuffer." - (when (minibufferp) - (user-error "`%s' called inside the minibuffer" this-command))) - -(defun consult--require-minibuffer () - "Raise an error if executed outside the minibuffer." - (unless (minibufferp) - (user-error "`%s' must be called inside the minibuffer" this-command))) - -(defun consult--fontify-all () - "Ensure that the whole buffer is fontified." - ;; Font-locking is lazy, i.e., if a line has not been looked at yet, the line - ;; is not font-locked. We would observe this if consulting an unfontified - ;; line. Therefore we have to enforce font-locking now, which is slow. In - ;; order to prevent is hang-up we check the buffer size against - ;; `consult-fontify-max-size'. - (when (and consult-fontify-preserve jit-lock-mode - (< (buffer-size) consult-fontify-max-size)) - (jit-lock-fontify-now))) - -(defun consult--fontify-region (start end) - "Ensure that region between START and END is fontified." - (when (and consult-fontify-preserve jit-lock-mode) - (jit-lock-fontify-now start end))) - -(defmacro consult--with-increased-gc (&rest body) - "Temporarily increase the GC limit in BODY to optimize for throughput." - (cl-with-gensyms (overwrite) - `(let* ((,overwrite (> consult--gc-threshold gc-cons-threshold)) - (gc-cons-threshold (if ,overwrite consult--gc-threshold gc-cons-threshold)) - (gc-cons-percentage (if ,overwrite consult--gc-percentage gc-cons-percentage))) - ,@body))) - -(defmacro consult--slow-operation (message &rest body) - "Show delayed MESSAGE if BODY takes too long. -Also temporarily increase the GC limit via `consult--with-increased-gc'." - (declare (indent 1)) - `(let (set-message-function) ;; bug#63253: Broken `with-delayed-message' - (with-delayed-message (1 ,message) - (consult--with-increased-gc - ,@body)))) - -(defun consult--count-lines (pos) - "Move to position POS and return number of lines." - (let ((line 1)) - (while (< (point) pos) - (forward-line) - (when (<= (point) pos) - (cl-incf line))) - (goto-char pos) - line)) - -(defun consult--marker-from-line-column (buffer line column) - "Get marker in BUFFER from LINE and COLUMN." - (when (buffer-live-p buffer) - (with-current-buffer buffer - (save-excursion - (without-restriction - (goto-char (point-min)) - ;; Location data might be invalid by now! - (ignore-errors - (forward-line (1- line)) - (goto-char (min (+ (point) column) (pos-eol)))) - (point-marker)))))) - -(defun consult--line-prefix (&optional curr-line) - "Annotate `consult-location' candidates with line numbers. -CURR-LINE is the current line number." - (setq curr-line (or curr-line -1)) - (let* ((width (length (number-to-string (line-number-at-pos - (point-max) - consult-line-numbers-widen)))) - (before (format #("%%%dd " 0 6 (face consult-line-number-wrapped)) width)) - (after (format #("%%%dd " 0 6 (face consult-line-number-prefix)) width))) - (lambda (cand) - (let ((line (cdr (get-text-property 0 'consult-location cand)))) - (list cand (format (if (< line curr-line) before after) line) ""))))) - -(defsubst consult--location-candidate (cand marker line tofu &rest props) - "Add MARKER and LINE as `consult-location' text property to CAND. -Furthermore add the additional text properties PROPS, and append -TOFU suffix for disambiguation." - (setq cand (concat cand (consult--tofu-encode tofu))) - (add-text-properties 0 1 `(consult-location (,marker . ,line) ,@props) cand) - cand) - -;; There is a similar variable `yank-excluded-properties'. Unfortunately -;; we cannot use it here since it excludes too much (e.g., invisible) -;; and at the same time not enough (e.g., cursor-sensor-functions). -(defconst consult--remove-text-properties - '(category cursor cursor-intangible cursor-sensor-functions field follow-link - fontified front-sticky help-echo insert-behind-hooks insert-in-front-hooks - intangible keymap local-map modification-hooks mouse-face pointer read-only - rear-nonsticky yank-handler) - "List of text properties to remove from buffer strings.") - -(defsubst consult--buffer-substring (beg end &optional fontify) - "Return buffer substring between BEG and END. -If FONTIFY and `consult-fontify-preserve' are non-nil, first ensure that the -region has been fontified." - (if consult-fontify-preserve - (let (str) - (when fontify (consult--fontify-region beg end)) - (setq str (buffer-substring beg end)) - ;; TODO Propose the upstream addition of a function - ;; `preserve-list-of-text-properties', which should be as efficient as - ;; `remove-list-of-text-properties'. - (remove-list-of-text-properties - 0 (- end beg) consult--remove-text-properties str) - str) - (buffer-substring-no-properties beg end))) - -(defun consult--line-with-mark (marker) - "Current line string where the MARKER position is highlighted." - (let* ((beg (pos-bol)) - (end (pos-eol)) - (str (consult--buffer-substring beg end 'fontify))) - (if (>= marker end) - (concat str #(" " 0 1 (face consult-highlight-mark))) - (put-text-property (- marker beg) (- (1+ marker) beg) - 'face 'consult-highlight-mark str) - str))) - -;;;; Tofu cooks - -(defsubst consult--tofu-p (char) - "Return non-nil if CHAR is a tofu." - (<= consult--tofu-char char (+ consult--tofu-char consult--tofu-range -1))) - -(defun consult--tofu-hide (str) - "Hide the tofus in STR." - (let* ((max (length str)) - (end max)) - (while (and (> end 0) (consult--tofu-p (aref str (1- end)))) - (cl-decf end)) - (when (< end max) - (setq str (copy-sequence str)) - (put-text-property end max 'invisible t str)) - str)) - -(defsubst consult--tofu-append (cand id) - "Append tofu-encoded ID to CAND. -The ID must fit within a single character. It must be smaller -than `consult--tofu-range'." - (setq id (char-to-string (+ consult--tofu-char id))) - (add-text-properties 0 1 '(invisible t consult-strip t) id) - (concat cand id)) - -(defsubst consult--tofu-get (cand) - "Extract tofu-encoded ID from CAND. -See `consult--tofu-append'." - (- (aref cand (1- (length cand))) consult--tofu-char)) - -;; We must disambiguate the lines by adding a prefix such that two lines with -;; the same text can be distinguished. In order to avoid matching the line -;; number, such that the user can search for numbers with `consult-line', we -;; encode the line number as characters outside the Unicode range. By doing -;; that, no accidental matching can occur. -(defun consult--tofu-encode (n) - "Return tofu-encoded number N as a string. -Large numbers are encoded as multiple tofu characters." - (let (str tofu) - (while (progn - (setq tofu (char-to-string - (+ consult--tofu-char (% n consult--tofu-range))) - str (if str (concat tofu str) tofu)) - (and (>= n consult--tofu-range) - (setq n (/ n consult--tofu-range))))) - (add-text-properties 0 (length str) '(invisible t consult-strip t) str) - str)) - -;;;; Regexp utilities - -(defun consult--find-highlights (str start &rest ignored-faces) - "Find highlighted regions in STR from position START. -Highlighted regions have a non-nil face property. -IGNORED-FACES are ignored when searching for matches." - (let (highlights - (end (length str)) - (beg start)) - (while (< beg end) - (let ((next (next-single-property-change beg 'face str end)) - (val (get-text-property beg 'face str))) - (when (and val - (not (memq val ignored-faces)) - (not (and (consp val) - (seq-some (lambda (x) (memq x ignored-faces)) val)))) - (push (cons (- beg start) (- next start)) highlights)) - (setq beg next))) - (nreverse highlights))) - -(defun consult--point-placement (str start &rest ignored-faces) - "Compute point placement from STR with START offset. -IGNORED-FACES are ignored when searching for matches. -Return cons of point position and a list of match begin/end pairs." - (let* ((matches (apply #'consult--find-highlights str start ignored-faces)) - (pos (pcase-exhaustive consult-point-placement - ('match-beginning (or (caar matches) 0)) - ('match-end (or (cdar (last matches)) 0)) - ('line-beginning 0)))) - (dolist (match matches) - (cl-decf (car match) pos) - (cl-decf (cdr match) pos)) - (cons pos matches))) - -(defun consult--highlight-regexps (regexps ignore-case str) - "Highlight REGEXPS in STR. -If a regular expression contains capturing groups, only these are highlighted. -If no capturing groups are used highlight the whole match. Case is ignored -if IGNORE-CASE is non-nil." - (dolist (re regexps) - (let ((i 0)) - (while (and (let ((case-fold-search ignore-case)) - (string-match re str i)) - ;; Ensure that regexp search made progress (edge case for .*) - (> (match-end 0) i)) - ;; Unfortunately there is no way to avoid the allocation of the match - ;; data, since the number of capturing groups is unknown. - (let ((m (match-data))) - (setq i (cadr m) m (or (cddr m) m)) - (while m - (when (car m) - (add-face-text-property (car m) (cadr m) - 'consult-highlight-match nil str)) - (setq m (cddr m))))))) - str) - -(defconst consult--convert-regexp-table - (append - ;; For simplicity, treat word beginning/end as word boundaries, - ;; since PCRE does not make this distinction. Usually the - ;; context determines if \b is the beginning or the end. - '(("\\<" . "\\b") ("\\>" . "\\b") - ("\\_<" . "\\b") ("\\_>" . "\\b")) - ;; Treat \` and \' as beginning and end of line. This is more - ;; widely supported and makes sense for line-based commands. - '(("\\`" . "^") ("\\'" . "$")) - ;; Historical: Unescaped *, +, ? are supported at the beginning - (mapcan (lambda (x) - (mapcar (lambda (y) - (cons (concat x y) - (concat (string-remove-prefix "\\" x) "\\" y))) - '("*" "+" "?"))) - '("" "\\(" "\\(?:" "\\|" "^")) - ;; Different escaping - (mapcan (lambda (x) `(,x (,(cdr x) . ,(car x)))) - '(("\\|" . "|") - ("\\(" . "(") ("\\)" . ")") - ("\\{" . "{") ("\\}" . "}")))) - "Regexp conversion table.") - -(defun consult--convert-regexp (regexp type) - "Convert Emacs REGEXP to regexp syntax TYPE." - (if (memq type '(emacs basic)) - regexp - ;; Support for Emacs regular expressions is fairly complete for basic - ;; usage. There are a few unsupported Emacs regexp features: - ;; - \= point matching - ;; - Syntax classes \sx \Sx - ;; - Character classes \cx \Cx - ;; - Explicitly numbered groups (?3:group) - (replace-regexp-in-string - (rx (or "\\\\" "\\^" ;; Pass through - (seq (or "\\(?:" "\\|") (any "*+?")) ;; Historical: \|+ or \(?:* etc - (seq "\\(" (any "*+")) ;; Historical: \(* or \(+ - (seq (or bos "^") (any "*+?")) ;; Historical: + or * at the beginning - (seq (opt "\\") (any "(){|}")) ;; Escape parens/braces/pipe - (seq "\\" (any "'<>`")) ;; Special escapes - (seq "\\_" (any "<>")))) ;; Beginning or end of symbol - (lambda (x) (or (cdr (assoc x consult--convert-regexp-table)) x)) - regexp 'fixedcase 'literal))) - -(defun consult--default-regexp-compiler (input type ignore-case) - "Compile the INPUT string to a list of regular expressions. -The function should return a pair, the list of regular expressions and a -highlight function. The highlight function should take a single -argument, the string to highlight given the INPUT. TYPE is the desired -type of regular expression, which can be `basic', `extended', `emacs' or -`pcre'. If IGNORE-CASE is non-nil return a highlight function which -matches case insensitively." - (setq input (consult--split-escaped input)) - (cons (mapcar (lambda (x) (consult--convert-regexp x type)) input) - (when-let (regexps (seq-filter #'consult--valid-regexp-p input)) - (apply-partially #'consult--highlight-regexps regexps ignore-case)))) - -(defun consult--split-escaped (str) - "Split STR at spaces, which can be escaped with backslash." - (mapcar - (lambda (x) (string-replace "\0" " " x)) - (split-string (replace-regexp-in-string - "\\\\\\\\\\|\\\\ " - (lambda (x) (if (equal x "\\ ") "\0" x)) - str 'fixedcase 'literal) - " +" t))) - -(defun consult--join-regexps (regexps type) - "Join REGEXPS of TYPE." - ;; Add look-ahead wrapper only if there is more than one regular expression - (cond - ((and (eq type 'pcre) (cdr regexps)) - (concat "^" (mapconcat (lambda (x) (format "(?=.*%s)" x)) - regexps ""))) - ((eq type 'basic) - (string-join regexps ".*")) - (t - (when (length> regexps 3) - (message "Too many regexps, %S ignored. Use post-filtering!" - (string-join (seq-drop regexps 3) " ")) - (setq regexps (seq-take regexps 3))) - (consult--join-regexps-permutations regexps (and (eq type 'emacs) "\\"))))) - -(defun consult--join-regexps-permutations (regexps esc) - "Join all permutations of REGEXPS. -ESC is the escaping string for choice and groups." - (pcase regexps - ('nil "") - (`(,r) r) - (_ (mapconcat - (lambda (r) - (concat esc "(" r esc ").*" esc "(" - (consult--join-regexps-permutations (remove r regexps) esc) - esc ")")) - regexps (concat esc "|"))))) - -(defun consult--valid-regexp-p (re) - "Return t if regexp RE is valid." - (condition-case nil - (progn (string-match-p re "") t) - (invalid-regexp nil))) - -(defun consult--regexp-filter (regexps) - "Create filter regexp from REGEXPS." - (if (stringp regexps) - regexps - (mapconcat (lambda (x) (concat "\\(?:" x "\\)")) regexps "\\|"))) - -;;;; Lookup functions - -(defun consult--lookup-member (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES list, return original element." - (car (member selected candidates))) - -(defun consult--lookup-cons (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES alist, return cons." - (assoc selected candidates)) - -(defun consult--lookup-cdr (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES alist, return `cdr' of element." - (cdr (assoc selected candidates))) - -(defun consult--lookup-location (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES list of `consult-location' category. -Return the location marker." - (when-let (found (member selected candidates)) - (setq found (car (consult--get-location (car found)))) - ;; Check that marker is alive - (and (or (not (markerp found)) (marker-buffer found)) found))) - -(defun consult--lookup-prop (prop selected candidates &rest _) - "Lookup SELECTED in CANDIDATES list and return PROP value." - (when-let (found (member selected candidates)) - (get-text-property 0 prop (car found)))) - -(defun consult--lookup-candidate (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES list and return property `consult--candidate'." - (consult--lookup-prop 'consult--candidate selected candidates)) - -;;;; Preview support - -(defun consult--filter-find-file-hook (orig &rest hooks) - "Filter `find-file-hook' by `consult-preview-allowed-hooks'. -This function is an advice for `run-hooks'. -ORIG is the original function, HOOKS the arguments." - (if (memq 'find-file-hook hooks) - (cl-letf* (((default-value 'find-file-hook) - (seq-filter (lambda (x) - (memq x consult-preview-allowed-hooks)) - (default-value 'find-file-hook))) - (find-file-hook (default-value 'find-file-hook))) - (apply orig hooks)) - (apply orig hooks))) - -(defun consult--find-file-temporarily-1 (name) - "Open file NAME, helper function for `consult--find-file-temporarily'." - (when-let (((not (seq-find (lambda (x) (string-match-p x name)) - consult-preview-excluded-files))) - ;; file-attributes may throw permission denied error - (attrs (ignore-errors (file-attributes name))) - (size (file-attribute-size attrs))) - (let* ((partial (>= size consult-preview-partial-size)) - (buffer (if partial - (generate-new-buffer (format "consult-partial-preview-%s" name)) - (find-file-noselect name 'nowarn))) - (success nil)) - (unwind-protect - (with-current-buffer buffer - (if (not partial) - (when (or (eq major-mode 'hexl-mode) - (and (eq major-mode 'fundamental-mode) - (save-excursion (search-forward "\0" nil 'noerror)))) - (error "No preview of binary file `%s'" - (file-name-nondirectory name))) - (with-silent-modifications - (setq buffer-read-only t) - (insert-file-contents name nil 0 consult-preview-partial-chunk) - (goto-char (point-max)) - (insert "\nFile truncated. End of partial preview.\n") - (goto-char (point-min))) - (when (save-excursion (search-forward "\0" nil 'noerror)) - (error "No partial preview of binary file `%s'" - (file-name-nondirectory name))) - ;; Auto detect major mode and hope for the best, given that the - ;; file is only previewed partially. If an error is thrown the - ;; buffer will be killed and preview is aborted. - (set-auto-mode) - (font-lock-mode 1)) - (when (bound-and-true-p so-long-detected-p) - (error "No preview of file `%s' with long lines" - (file-name-nondirectory name))) - (setq success (current-buffer))) - (unless success - (kill-buffer buffer)))))) - -(defun consult--find-file-temporarily (name) - "Open file NAME temporarily for preview." - (let ((vars (delq nil - (mapcar - (pcase-lambda (`(,k . ,v)) - (if (boundp k) - (list k v (default-value k) (symbol-value k)) - (message "consult-preview-variables: The variable `%s' is not bound" k) - nil)) - consult-preview-variables)))) - (condition-case err - (unwind-protect - (progn - (advice-add #'run-hooks :around #'consult--filter-find-file-hook) - (pcase-dolist (`(,k ,v . ,_) vars) - (set-default k v) - (set k v)) - (consult--find-file-temporarily-1 name)) - (advice-remove #'run-hooks #'consult--filter-find-file-hook) - (pcase-dolist (`(,k ,_ ,d ,v) vars) - (set-default k d) - (set k v))) - (error - (message "%s" (error-message-string err)) - nil)))) - -(defun consult--temporary-files () - "Return a function to open files temporarily for preview." - (let ((dir default-directory) - (hook (make-symbol "consult--temporary-files-upgrade-hook")) - (orig-buffers (buffer-list)) - temporary-buffers) - (fset hook - (lambda (_) - ;; Fully initialize previewed files and keep them alive. - (unless (consult--completion-window-p) - (let (live-files) - (pcase-dolist (`(,file . ,buf) temporary-buffers) - (when-let (wins (and (buffer-live-p buf) - (get-buffer-window-list buf))) - (push (cons file (mapcar - (lambda (win) - (cons win (window-state-get win t))) - wins)) - live-files))) - (pcase-dolist (`(,_ . ,buf) temporary-buffers) - (kill-buffer buf)) - (setq temporary-buffers nil) - (pcase-dolist (`(,file . ,wins) live-files) - (when-let (buf (find-file-noselect file)) - (push buf orig-buffers) - (pcase-dolist (`(,win . ,state) wins) - (setf (car (alist-get 'buffer state)) buf) - (window-state-put state win)))))))) - (lambda (&optional name) - (if name - (let ((default-directory dir)) - (setq name (abbreviate-file-name (expand-file-name name))) - (or - ;; Find existing fully initialized buffer (non-previewed). We have - ;; to check for fully initialized buffer before accessing the - ;; previewed buffers, since `embark-act' can open a buffer which is - ;; currently previewed, such that we end up with two buffers for - ;; the same file - one previewed and only partially initialized and - ;; one fully initialized. In this case we prefer the fully - ;; initialized buffer. For directories `get-file-buffer' returns nil, - ;; therefore we have to special case Dired. - (if (and (fboundp 'dired-find-buffer-nocreate) (file-directory-p name)) - (dired-find-buffer-nocreate name) - (get-file-buffer name)) - ;; Find existing previewed buffer. Previewed buffers are not fully - ;; initialized (hooks are delayed) in order to ensure fast preview. - (cdr (assoc name temporary-buffers)) - ;; Finally, if no existing buffer has been found, open the file for - ;; preview. - (when-let (buf (consult--find-file-temporarily name)) - ;; Only add new buffer if not already in the list - (unless (or (rassq buf temporary-buffers) (memq buf orig-buffers)) - (add-hook 'window-selection-change-functions hook) - (push (cons name buf) temporary-buffers) - ;; Disassociate buffer from file by setting `buffer-file-name' - ;; and `dired-directory' to nil and rename the buffer. This - ;; lets us open an already previewed buffer with the Embark - ;; default action C-. RET. - (with-current-buffer buf - (rename-buffer - (format " Preview:%s" - (file-name-nondirectory (directory-file-name name))) - 'unique)) - ;; The buffer disassociation is delayed to avoid breaking modes - ;; like `pdf-view-mode' or `doc-view-mode' which rely on - ;; `buffer-file-name'. Executing (set-visited-file-name nil) - ;; early also prevents the major mode initialization. - (let ((hook (make-symbol "consult--temporary-files-disassociate-hook"))) - (fset hook (lambda () - (when (buffer-live-p buf) - (with-current-buffer buf - (remove-hook 'pre-command-hook hook) - (setq-local buffer-read-only t - dired-directory nil - buffer-file-name nil))))) - (add-hook 'pre-command-hook hook)) - ;; Only keep a few buffers alive - (while (length> temporary-buffers consult-preview-max-count) - (kill-buffer (cdar (last temporary-buffers))) - (setq temporary-buffers (nbutlast temporary-buffers)))) - buf))) - (remove-hook 'window-selection-change-functions hook) - (pcase-dolist (`(,_ . ,buf) temporary-buffers) - (kill-buffer buf)) - (setq temporary-buffers nil))))) - -(defun consult--invisible-open-permanently () - "Open overlays which hide the current line. -See `isearch-open-necessary-overlays' and `isearch-open-overlay-temporary'." - (if (and (derived-mode-p 'org-mode) (fboundp 'org-fold-show-set-visibility)) - ;; New Org 9.6 fold-core API - (let ((inhibit-redisplay t)) ;; HACK: Prevent flicker due to premature redisplay - (org-fold-show-set-visibility 'canonical)) - (dolist (ov (overlays-in (pos-bol) (pos-eol))) - (when-let (fun (overlay-get ov 'isearch-open-invisible)) - (when (invisible-p (overlay-get ov 'invisible)) - (funcall fun ov)))))) - -(defun consult--invisible-open-temporarily () - "Temporarily open overlays which hide the current line. -See `isearch-open-necessary-overlays' and `isearch-open-overlay-temporary'." - (if (and (derived-mode-p 'org-mode) - (fboundp 'org-fold-show-set-visibility) - (fboundp 'org-fold-core-get-regions) - (fboundp 'org-fold-core-region)) - ;; New Org 9.6 fold-core API - ;; TODO The provided Org API `org-fold-show-set-visibility' cannot be used - ;; efficiently. We obtain all regions in the whole buffer in order to - ;; restore them. A better show API would return all the applied - ;; modifications such that we can restore the ones which got modified. - (progn - (unless consult--org-fold-regions - (setq consult--org-fold-regions - (delq nil (org-fold-core-get-regions - :with-markers t :from (point-min) :to (point-max)))) - (when consult--org-fold-regions - (let ((hook (make-symbol "consult--invisible-open-temporarily-cleanup-hook")) - (buffer (current-buffer)) - (depth (recursion-depth))) - (fset hook - (lambda () - (when (= (recursion-depth) depth) - (remove-hook 'minibuffer-exit-hook hook) - (run-at-time - 0 nil - (lambda () - (when (buffer-live-p buffer) - (with-current-buffer buffer - (pcase-dolist (`(,beg ,end ,_) consult--org-fold-regions) - (when (markerp beg) (set-marker beg nil)) - (when (markerp end) (set-marker end nil))) - (kill-local-variable 'consult--org-fold-regions)))))))) - (add-hook 'minibuffer-exit-hook hook)))) - (let ((inhibit-redisplay t)) ;; HACK: Prevent flicker due to premature redisplay - (org-fold-show-set-visibility 'canonical)) - (list (lambda () - (pcase-dolist (`(,beg ,end ,spec) consult--org-fold-regions) - (org-fold-core-region beg end t spec))))) - (let (restore) - (dolist (ov (overlays-in (pos-bol) (pos-eol))) - (let ((inv (overlay-get ov 'invisible))) - (when (and (invisible-p inv) (overlay-get ov 'isearch-open-invisible)) - (push (if-let (fun (overlay-get ov 'isearch-open-invisible-temporary)) - (progn - (funcall fun ov nil) - (lambda () (funcall fun ov t))) - (overlay-put ov 'invisible nil) - (lambda () (overlay-put ov 'invisible inv))) - restore)))) - restore))) - -(defun consult--jump-ensure-buffer (pos) - "Ensure that buffer of marker POS is displayed, return t if successful." - (or (not (markerp pos)) - ;; Switch to buffer if it is not visible - (when-let ((buf (marker-buffer pos))) - (or (and (eq (current-buffer) buf) (eq (window-buffer) buf)) - (consult--buffer-action buf 'norecord) - t)))) - -(defun consult--jump (pos) - "Jump to POS. -First push current position to mark ring, then move to new -position and run `consult-after-jump-hook'." - (when pos - ;; Extract marker from list with with overlay positions, see `consult--line-match' - (when (consp pos) (setq pos (car pos))) - ;; When the marker is in the same buffer, record previous location - ;; such that the user can jump back quickly. - (when (or (not (markerp pos)) (eq (current-buffer) (marker-buffer pos))) - ;; push-mark mutates markers in the mark-ring and the mark-marker. - ;; Therefore we transform the marker to a number to be safe. - ;; We all love side effects! - (setq pos (+ pos 0)) - (push-mark (point) t)) - (when (consult--jump-ensure-buffer pos) - (unless (= (goto-char pos) (point)) ;; Widen if jump failed - (widen) - (goto-char pos)) - (consult--invisible-open-permanently) - (run-hooks 'consult-after-jump-hook))) - nil) - -(defun consult--jump-preview () - "The preview function used if selecting from a list of candidate positions. -The function can be used as the `:state' argument of `consult--read'." - (let (restore) - (lambda (action cand) - (when (eq action 'preview) - (mapc #'funcall restore) - (setq restore nil) - ;; TODO Better buffer preview support - ;; 1. Use consult--buffer-preview instead of consult--jump-ensure-buffer - ;; 2. Remove function consult--jump-ensure-buffer - ;; 3. Remove consult-buffer-other-* from consult-customize-alist - (when-let ((pos (or (car-safe cand) cand)) ;; Candidate can be previewed - ((consult--jump-ensure-buffer pos))) - (let ((saved-min (point-min-marker)) - (saved-max (point-max-marker)) - (saved-pos (point-marker))) - (set-marker-insertion-type saved-max t) ;; Grow when text is inserted - (push (lambda () - (when-let ((buf (marker-buffer saved-pos))) - (with-current-buffer buf - (narrow-to-region saved-min saved-max) - (goto-char saved-pos) - (set-marker saved-pos nil) - (set-marker saved-min nil) - (set-marker saved-max nil)))) - restore)) - (unless (= (goto-char pos) (point)) ;; Widen if jump failed - (widen) - (goto-char pos)) - (setq restore (nconc (consult--invisible-open-temporarily) restore)) - ;; Ensure that cursor is properly previewed (gh:minad/consult#764) - (unless (eq cursor-in-non-selected-windows 'box) - (let ((orig cursor-in-non-selected-windows) - (buf (current-buffer))) - (push - (if (local-variable-p 'cursor-in-non-selected-windows) - (lambda () - (when (buffer-live-p buf) - (with-current-buffer buf - (setq-local cursor-in-non-selected-windows orig)))) - (lambda () - (when (buffer-live-p buf) - (with-current-buffer buf - (kill-local-variable 'cursor-in-non-selected-windows))))) - restore) - (setq-local cursor-in-non-selected-windows 'box))) - ;; Match previews - (let ((overlays - (list (save-excursion - (let ((vbeg (progn (beginning-of-visual-line) (point))) - (vend (progn (end-of-visual-line) (point))) - (end (pos-eol))) - (consult--make-overlay vbeg (if (= vend end) (1+ end) vend) - 'face 'consult-preview-line - 'window (selected-window) - 'priority 1)))))) - (dolist (match (cdr-safe cand)) - (push (consult--make-overlay (+ (point) (car match)) - (+ (point) (cdr match)) - 'face 'consult-preview-match - 'window (selected-window) - 'priority 2) - overlays)) - (push (lambda () (mapc #'delete-overlay overlays)) restore)) - (run-hooks 'consult-after-jump-hook)))))) - -(defun consult--jump-state () - "The state function used if selecting from a list of candidate positions." - (consult--state-with-return (consult--jump-preview) #'consult--jump)) - -(defun consult--get-location (cand) - "Return location from CAND." - (let ((loc (get-text-property 0 'consult-location cand))) - (when (consp (car loc)) - ;; Transform cheap marker to real marker - (setcar loc (set-marker (make-marker) (cdar loc) (caar loc)))) - loc)) - -(defun consult--location-state (candidates) - "Location state function. -The cheap location markers from CANDIDATES are upgraded on window -selection change to full Emacs markers." - (let ((jump (consult--jump-state)) - (hook (make-symbol "consult--location-upgrade-hook"))) - (fset hook - (lambda (_) - (unless (consult--completion-window-p) - (remove-hook 'window-selection-change-functions hook) - (mapc #'consult--get-location - (if (functionp candidates) (funcall candidates) candidates))))) - (lambda (action cand) - (pcase action - ('setup (add-hook 'window-selection-change-functions hook)) - ('exit (remove-hook 'window-selection-change-functions hook))) - (funcall jump action cand)))) - -(defun consult--state-with-return (state return) - "Compose STATE function with RETURN function." - (lambda (action cand) - (funcall state action cand) - (when (and cand (eq action 'return)) - (funcall return cand)))) - -(defmacro consult--define-state (type) - "Define state function for TYPE." - `(defun ,(intern (format "consult--%s-state" type)) () - ,(format "State function for %ss with preview. -The result can be passed as :state argument to `consult--read'." type) - (consult--state-with-return (,(intern (format "consult--%s-preview" type))) - #',(intern (format "consult--%s-action" type))))) - -(defun consult--preview-key-normalize (preview-key) - "Normalize PREVIEW-KEY, return alist of keys and debounce times." - (let ((keys) - (debounce 0)) - (setq preview-key (ensure-list preview-key)) - (while preview-key - (if (eq (car preview-key) :debounce) - (setq debounce (cadr preview-key) - preview-key (cddr preview-key)) - (let ((key (car preview-key))) - (unless (eq key 'any) - (setq key (consult--key-parse key))) - (push (cons key debounce) keys)) - (pop preview-key))) - keys)) - -(defun consult--preview-key-debounce (preview-key cand) - "Return debounce value of PREVIEW-KEY given the current candidate CAND." - (when (and (consp preview-key) (memq :keys preview-key)) - (setq preview-key (funcall (plist-get preview-key :predicate) cand))) - (let ((map (make-sparse-keymap)) - (keys (this-single-command-keys)) - any) - (pcase-dolist (`(,k . ,d) (consult--preview-key-normalize preview-key)) - (if (eq k 'any) - (setq any d) - (define-key map k `(lambda () ,d)))) - (setq keys (lookup-key map keys)) - (if (functionp keys) (funcall keys) any))) - -(defun consult--preview-append-local-pch (fun) - "Append FUN to local `post-command-hook' list." - ;; Symbol indirection because of bug#46407. - (let ((hook (make-symbol "consult--preview-post-command-hook"))) - (fset hook fun) - ;; TODO Emacs 28 has a bug, where the hook--depth-alist is not cleaned up properly - ;; Do not use the broken add-hook here. - ;;(add-hook 'post-command-hook hook 'append 'local) - (setq-local post-command-hook - (append - (remove t post-command-hook) - (list hook) - (and (memq t post-command-hook) '(t)))))) - -(defun consult--with-preview-1 (preview-key state transform candidate save-input fun) - "Add preview support for FUN. -See `consult--with-preview' for the arguments -PREVIEW-KEY, STATE, TRANSFORM, CANDIDATE and SAVE-INPUT." - (let ((mb-input "") mb-narrow selected timer previewed) - (consult--minibuffer-with-setup-hook - (if (and state preview-key) - (lambda () - (let ((hook (make-symbol "consult--preview-minibuffer-exit-hook")) - (depth (recursion-depth))) - (fset hook - (lambda () - (when (= (recursion-depth) depth) - (remove-hook 'minibuffer-exit-hook hook) - (when timer - (cancel-timer timer) - (setq timer nil)) - (with-selected-window (consult--original-window) - ;; STEP 3: Reset preview - (when previewed - (funcall state 'preview nil)) - ;; STEP 4: Notify the preview function of the minibuffer exit - (funcall state 'exit nil))))) - (add-hook 'minibuffer-exit-hook hook)) - ;; STEP 1: Setup the preview function - (with-selected-window (consult--original-window) - (funcall state 'setup nil)) - (setq consult--preview-function - (lambda () - (when-let ((cand (funcall candidate))) - ;; Drop properties to prevent bugs regarding candidate - ;; lookup, which must handle candidates without - ;; properties. Otherwise the arguments passed to the - ;; lookup function are confusing, since during preview - ;; the candidate has properties but for the final lookup - ;; after completion it does not. - (setq cand (substring-no-properties cand)) - (with-selected-window (active-minibuffer-window) - (let ((input (minibuffer-contents-no-properties)) - (narrow consult--narrow) - (win (consult--original-window))) - (with-selected-window win - (when-let ((transformed (funcall transform narrow input cand)) - (debounce (consult--preview-key-debounce preview-key transformed))) - (when timer - (cancel-timer timer) - (setq timer nil)) - ;; The transformed candidate may have text - ;; properties, which change the preview display. - ;; This matters for example for `consult-grep', - ;; where the current candidate and input may - ;; stay equal, but the highlighting of the - ;; candidate changes while the candidates list - ;; is lagging a bit behind and updates - ;; asynchronously. - ;; - ;; In older Consult versions we instead compared - ;; the input without properties, since I worried - ;; that comparing the transformed candidates - ;; could be potentially expensive. However - ;; comparing the transformed candidates is more - ;; correct. The transformed candidate is the - ;; thing which is actually previewed. - (unless (equal-including-properties previewed transformed) - (if (> debounce 0) - (setq timer - (run-at-time - debounce nil - (lambda () - ;; Preview only when a completion - ;; window is selected and when - ;; the preview window is alive. - (when (and (consult--completion-window-p) - (window-live-p win)) - (with-selected-window win - ;; STEP 2: Preview candidate - (funcall state 'preview (setq previewed transformed))))))) - ;; STEP 2: Preview candidate - (funcall state 'preview (setq previewed transformed))))))))))) - (consult--preview-append-local-pch - (lambda () - (setq mb-input (minibuffer-contents-no-properties) - mb-narrow consult--narrow) - (funcall consult--preview-function)))) - (lambda () - (consult--preview-append-local-pch - (lambda () - (setq mb-input (minibuffer-contents-no-properties) - mb-narrow consult--narrow))))) - (unwind-protect - (setq selected (when-let (result (funcall fun)) - (when-let ((save-input) - (list (symbol-value save-input)) - ((equal (car list) result))) - (set save-input (cdr list))) - (funcall transform mb-narrow mb-input result))) - (when save-input - (add-to-history save-input mb-input)) - (when state - ;; STEP 5: The preview function should perform its final action - (funcall state 'return selected)))))) - -(defmacro consult--with-preview (preview-key state transform candidate save-input &rest body) - "Add preview support to BODY. - -STATE is the state function. -TRANSFORM is the transformation function. -CANDIDATE is the function returning the current candidate. -PREVIEW-KEY are the keys which triggers the preview. -SAVE-INPUT can be a history variable symbol to save the input. - -The state function takes two arguments, an action argument and the -selected candidate. The candidate argument can be nil if no candidate is -selected or if the selection was aborted. The function is called in -sequence with the following arguments: - - 1. \\='setup nil After entering the mb (minibuffer-setup-hook). -⎧ 2. \\='preview CAND/nil Preview candidate CAND or reset if CAND is nil. -⎪ \\='preview CAND/nil -⎪ \\='preview CAND/nil -⎪ ... -⎩ 3. \\='preview nil Reset preview. - 4. \\='exit nil Before exiting the mb (minibuffer-exit-hook). - 5. \\='return CAND/nil After leaving the mb, CAND has been selected. - -The state function is always executed with the original window selected, -see `consult--original-window'. The state function is called once in -the beginning of the minibuffer setup with the `setup' argument. This is -useful in order to perform certain setup operations which require that -the minibuffer is initialized. During completion candidates are -previewed. Then the function is called with the `preview' argument and a -candidate CAND or nil if no candidate is selected. Furthermore if nil is -passed for CAND, then the preview must be undone and the original state -must be restored. The call with the `exit' argument happens once at the -end of the completion process, just before exiting the minibuffer. The -minibuffer is still alive at that point. Both `setup' and `exit' are -only useful for setup and cleanup operations. They don't receive a -candidate as argument. After leaving the minibuffer, the selected -candidate or nil is passed to the state function with the action -argument `return'. At this point the state function can perform the -actual action on the candidate. The state function with the `return' -argument is the continuation of `consult--read'. Via `unwind-protect' it -is guaranteed, that if the `setup' action of a state function is -invoked, the state function will also be called with `exit' and -`return'." - (declare (indent 5)) - `(consult--with-preview-1 ,preview-key ,state ,transform ,candidate ,save-input (lambda () ,@body))) - -;;;; Narrowing and grouping - -(defun consult--prefix-group (cand transform) - "Return title for CAND or TRANSFORM the candidate. -The candidate must have a `consult--prefix-group' property." - (if transform - (substring cand (1+ (length (get-text-property 0 'consult--prefix-group cand)))) - (get-text-property 0 'consult--prefix-group cand))) - -(defun consult--type-group (types) - "Return group function for TYPES." - (lambda (cand transform) - (if transform cand - (alist-get (get-text-property 0 'consult--type cand) types)))) - -(defun consult--type-narrow (types) - "Return narrowing configuration from TYPES." - (list :predicate - (lambda (cand) (eq (get-text-property 0 'consult--type cand) consult--narrow)) - :keys types)) - -(defun consult--widen-key () - "Return widening key, if `consult-widen-key' is not set. -The default is twice the `consult-narrow-key'." - (cond - (consult-widen-key - (consult--key-parse consult-widen-key)) - (consult-narrow-key - (let ((key (consult--key-parse consult-narrow-key))) - (vconcat key key))))) - -(defun consult-narrow (key) - "Narrow current completion with KEY. - -This command is used internally by the narrowing system of `consult--read'." - (interactive - (list (unless (equal (this-single-command-keys) (consult--widen-key)) - last-command-event))) - (consult--require-minibuffer) - (setq consult--narrow key) - (when consult--narrow-predicate - (setq minibuffer-completion-predicate (and consult--narrow consult--narrow-predicate))) - (when consult--narrow-overlay - (delete-overlay consult--narrow-overlay)) - (when consult--narrow - (setq consult--narrow-overlay - (consult--make-overlay - (1- (minibuffer-prompt-end)) (minibuffer-prompt-end) - 'before-string - (propertize (format " [%s]" (alist-get consult--narrow - consult--narrow-keys)) - 'face 'consult-narrow-indicator)))) - (run-hooks 'consult--completion-refresh-hook)) - -(defconst consult--narrow-delete - `(menu-item - "" nil :filter - ,(lambda (&optional _) - (when (equal (minibuffer-contents-no-properties) "") - (lambda () - (interactive) - (consult-narrow nil)))))) - -(defconst consult--narrow-space - `(menu-item - "" nil :filter - ,(lambda (&optional _) - (let ((str (minibuffer-contents-no-properties))) - (when-let (pair (or (and (length= str 1) - (assoc (aref str 0) consult--narrow-keys)) - (and (equal str "") - (assoc ?\s consult--narrow-keys)))) - (lambda () - (interactive) - (delete-minibuffer-contents) - (consult-narrow (car pair)))))))) - -(defun consult-narrow-help () - "Print narrowing help as a `minibuffer-message'. - -This command can be bound to a key in `consult-narrow-map', -to make it available for commands with narrowing." - (interactive) - (consult--require-minibuffer) - (let ((minibuffer-message-timeout 1000000)) - (minibuffer-message - (mapconcat (lambda (x) - (concat - (propertize (key-description (list (car x))) 'face 'consult-key) - " " - (propertize (cdr x) 'face 'consult-help))) - consult--narrow-keys - " ")))) - -(defun consult--narrow-setup (settings map) - "Setup narrowing with SETTINGS and keymap MAP." - (if (memq :keys settings) - (setq consult--narrow-predicate (plist-get settings :predicate) - consult--narrow-keys (plist-get settings :keys)) - (setq consult--narrow-predicate nil - consult--narrow-keys settings)) - (when-let ((key consult-narrow-key)) - (setq key (consult--key-parse key)) - (dolist (pair consult--narrow-keys) - (define-key map (vconcat key (vector (car pair))) - (cons (cdr pair) #'consult-narrow)))) - (when-let ((widen (consult--widen-key))) - (define-key map widen (cons "All" #'consult-narrow))) - (when-let ((init (and (memq :keys settings) (plist-get settings :initial)))) - (consult-narrow init))) - -;; Emacs 28: hide in M-X -(put #'consult-narrow-help 'completion-predicate #'ignore) -(put #'consult-narrow 'completion-predicate #'ignore) - -;;;; Splitting completion style - -(defun consult--split-perl (str &optional _plist) - "Split input STR in async input and filtering part. - -The function returns a list with three elements: The async -string, the start position of the completion filter string and a -force flag. If the first character is a punctuation character it -determines the separator. Examples: \"/async/filter\", -\"#async#filter\"." - (if (string-match-p "^[[:punct:]]" str) - (save-match-data - (let ((q (regexp-quote (substring str 0 1)))) - (string-match (concat "^" q "\\([^" q "]*\\)\\(" q "\\)?") str) - `(,(match-string 1 str) - ,(match-end 0) - ;; Force update it two punctuation characters are entered. - ,(match-end 2) - ;; List of highlights - (0 . ,(match-beginning 1)) - ,@(and (match-end 2) `((,(match-beginning 2) . ,(match-end 2))))))) - `(,str ,(length str)))) - -(defun consult--split-nil (str &optional _plist) - "Treat the complete input STR as async input." - `(,str ,(length str))) - -(defun consult--split-separator (str plist) - "Split input STR in async input and filtering part at first separator. -PLIST is the splitter configuration, including the separator." - (let ((sep (regexp-quote (char-to-string (plist-get plist :separator))))) - (save-match-data - (if (string-match (format "^\\([^%s]+\\)\\(%s\\)?" sep sep) str) - `(,(match-string 1 str) - ,(match-end 0) - ;; Force update it space is entered. - ,(match-end 2) - ;; List of highlights - ,@(and (match-end 2) `((,(match-beginning 2) . ,(match-end 2))))) - `(,str ,(length str)))))) - -(defun consult--split-setup (split) - "Setup splitting completion style with splitter function SPLIT." - (let* ((styles completion-styles) - (catdef completion-category-defaults) - (catovr completion-category-overrides) - (try (lambda (str table pred point) - (let ((completion-styles styles) - (completion-category-defaults catdef) - (completion-category-overrides catovr) - (pos (cadr (funcall split str)))) - (pcase (completion-try-completion (substring str pos) table pred - (max 0 (- point pos))) - ('t t) - (`(,newstr . ,newpt) - (cons (concat (substring str 0 pos) newstr) - (+ pos newpt))))))) - (all (lambda (str table pred point) - (let ((completion-styles styles) - (completion-category-defaults catdef) - (completion-category-overrides catovr) - (pos (cadr (funcall split str)))) - (completion-all-completions (substring str pos) table pred - (max 0 (- point pos))))))) - (setq-local completion-styles-alist (cons `(consult--split ,try ,all "") - completion-styles-alist) - completion-styles '(consult--split) - completion-category-defaults nil - completion-category-overrides nil))) - -;;;; Asynchronous filtering functions - -(defun consult--async-p (fun) - "Return t if FUN is an asynchronous completion function." - (and (functionp fun) - (condition-case nil - (progn (funcall fun "" nil 'metadata) nil) - (wrong-number-of-arguments t)))) - -(defmacro consult--with-async (bind &rest body) - "Setup asynchronous completion in BODY. - -BIND is the asynchronous function binding." - (declare (indent 1)) - (let ((async (car bind))) - `(let ((,async ,@(cdr bind)) - (new-chunk (max read-process-output-max consult--process-chunk)) - orig-chunk) - (consult--minibuffer-with-setup-hook - ;; Append such that we overwrite the completion style setting of - ;; `fido-mode'. See `consult--async-split' and - ;; `consult--split-setup'. - (:append - (lambda () - (when (consult--async-p ,async) - (setq orig-chunk read-process-output-max - read-process-output-max new-chunk) - (funcall ,async 'setup) - (let* ((mb (current-buffer)) - (fun (lambda () - (when-let (win (active-minibuffer-window)) - (when (eq (window-buffer win) mb) - (with-current-buffer mb - (let ((inhibit-modification-hooks t)) - ;; Push input string to request refresh. - (funcall ,async (minibuffer-contents-no-properties)))))))) - ;; We use a symbol in order to avoid adding lambdas to - ;; the hook variable. Symbol indirection because of - ;; bug#46407. - (hook (make-symbol "consult--async-after-change-hook"))) - ;; Delay modification hook to ensure that minibuffer is still - ;; alive after the change, such that we don't restart a new - ;; asynchronous search right before exiting the minibuffer. - (fset hook (lambda (&rest _) (run-at-time 0 nil fun))) - (add-hook 'after-change-functions hook nil 'local) - (funcall hook))))) - (let ((,async (if (consult--async-p ,async) ,async (lambda (_) ,async)))) - (unwind-protect - ,(macroexp-progn body) - (funcall ,async 'destroy) - (when (and orig-chunk (eq read-process-output-max new-chunk)) - (setq read-process-output-max orig-chunk)))))))) - -(defun consult--async-sink () - "Create ASYNC sink function. - -An async function must accept a single action argument. For the -\\='setup action it is guaranteed that the call originates from -the minibuffer. For the other actions no assumption about the -context can be made. - -\\='setup Setup the internal closure state. Return nil. -\\='destroy Destroy the internal closure state. Return nil. -\\='flush Flush the list of candidates. Return nil. -\\='refresh Request UI refresh. Return nil. -nil Return the list of candidates. -list Append the list to the already existing candidates list and return it. -string Update with the current user input string. Return nil." - (let (candidates last buffer) - (lambda (action) - (pcase-exhaustive action - ('setup - (setq buffer (current-buffer)) - nil) - ((or (pred stringp) 'destroy) nil) - ('flush (setq candidates nil last nil)) - ('refresh - ;; Refresh the UI when the current minibuffer window belongs - ;; to the current asynchronous completion session. - (when-let (win (active-minibuffer-window)) - (when (eq (window-buffer win) buffer) - (with-selected-window win - (run-hooks 'consult--completion-refresh-hook) - ;; Interaction between asynchronous completion functions and - ;; preview: We have to trigger preview immediately when - ;; candidates arrive (gh:minad/consult#436). - (when (and consult--preview-function candidates) - (funcall consult--preview-function))))) - nil) - ('nil candidates) - ((pred consp) - (setq last (last (if last (setcdr last action) (setq candidates action)))) - candidates))))) - -(defun consult--async-split-style () - "Return the async splitting style function and initial string." - (or (alist-get consult-async-split-style consult-async-split-styles-alist) - (user-error "Splitting style `%s' not found" consult-async-split-style))) - -(defun consult--async-split-initial (initial) - "Return initial string for async command. -INITIAL is the additional initial string." - (concat (plist-get (consult--async-split-style) :initial) initial)) - -(defun consult--async-split-thingatpt (thing) - "Return THING at point with async initial prefix." - (when-let (str (thing-at-point thing)) - (consult--async-split-initial str))) - -(defun consult--async-split (async &optional split) - "Create async function, which splits the input string. -ASYNC is the async sink. -SPLIT is the splitting function." - (unless split - (let* ((style (consult--async-split-style)) - (fn (plist-get style :function))) - (setq split (lambda (str) (funcall fn str style))))) - (lambda (action) - (pcase action - ('setup - (consult--split-setup split) - (funcall async 'setup)) - ((pred stringp) - (pcase-let* ((`(,async-str ,_ ,force . ,highlights) - (funcall split action)) - (async-len (length async-str)) - (input-len (length action)) - (end (minibuffer-prompt-end))) - ;; Highlight punctuation characters - (remove-list-of-text-properties end (+ end input-len) '(face)) - (dolist (hl highlights) - (put-text-property (+ end (car hl)) (+ end (cdr hl)) - 'face 'consult-async-split)) - (funcall async - ;; Pass through if the input is long enough! - (if (or force (>= async-len consult-async-min-input)) - async-str - ;; Pretend that there is no input - "")))) - (_ (funcall async action))))) - -(defun consult--async-indicator (async) - "Create async function with a state indicator overlay. -ASYNC is the async sink." - (let (ov) - (lambda (action &optional state) - (pcase action - ('indicator - (overlay-put ov 'display - (pcase-exhaustive state - ('running #("*" 0 1 (face consult-async-running))) - ('finished #(":" 0 1 (face consult-async-finished))) - ('killed #(";" 0 1 (face consult-async-failed))) - ('failed #("!" 0 1 (face consult-async-failed)))))) - ('setup - (setq ov (make-overlay (- (minibuffer-prompt-end) 2) - (- (minibuffer-prompt-end) 1))) - (funcall async 'setup)) - ('destroy - (delete-overlay ov) - (funcall async 'destroy)) - (_ (funcall async action)))))) - -(defun consult--async-log (formatted &rest args) - "Log FORMATTED ARGS to variable `consult--async-log'." - (with-current-buffer (get-buffer-create consult--async-log) - (goto-char (point-max)) - (insert (apply #'format formatted args)))) - -(defun consult--async-process (async builder &rest props) - "Create process source async function. - -ASYNC is the async function which receives the candidates. -BUILDER is the command line builder function. -PROPS are optional properties passed to `make-process'." - (setq async (consult--async-indicator async)) - (let (proc proc-buf last-args count) - (lambda (action) - (pcase action - ("" ;; If no input is provided kill current process - (when proc - (delete-process proc) - (kill-buffer proc-buf) - (setq proc nil proc-buf nil)) - (setq last-args nil)) - ((pred stringp) - (funcall async action) - (let* ((args (funcall builder action))) - (unless (stringp (car args)) - (setq args (car args))) - (unless (equal args last-args) - (setq last-args args) - (when proc - (delete-process proc) - (kill-buffer proc-buf) - (setq proc nil proc-buf nil)) - (when args - (let* ((flush t) - (rest "") - (proc-filter - (lambda (_ out) - (when flush - (setq flush nil) - (funcall async 'flush)) - (let ((lines (split-string out "[\r\n]+"))) - (if (not (cdr lines)) - (setq rest (concat rest (car lines))) - (setcar lines (concat rest (car lines))) - (let* ((len (length lines)) - (last (nthcdr (- len 2) lines))) - (setq rest (cadr last) - count (+ count len -1)) - (setcdr last nil) - (funcall async lines)))))) - (proc-sentinel - (lambda (_ event) - (when flush - (setq flush nil) - (funcall async 'flush)) - (funcall async 'indicator - (cond - ((string-prefix-p "killed" event) 'killed) - ((string-prefix-p "finished" event) 'finished) - (t 'failed))) - (when (and (string-prefix-p "finished" event) (not (equal rest ""))) - (cl-incf count) - (funcall async (list rest))) - (consult--async-log - "consult--async-process sentinel: event=%s lines=%d\n" - (string-trim event) count) - (when (> (buffer-size proc-buf) 0) - (with-current-buffer (get-buffer-create consult--async-log) - (goto-char (point-max)) - (insert ">>>>> stderr >>>>>\n") - (let ((beg (point))) - (insert-buffer-substring proc-buf) - (save-excursion - (goto-char beg) - (message #("%s" 0 2 (face error)) - (buffer-substring-no-properties (pos-bol) (pos-eol))))) - (insert "<<<<< stderr <<<<<\n"))))) - (process-adaptive-read-buffering nil)) - (funcall async 'indicator 'running) - (consult--async-log "consult--async-process started %S\n" args) - (setq count 0 - proc-buf (generate-new-buffer " *consult-async-stderr*") - proc (apply #'make-process - `(,@props - :connection-type pipe - :name ,(car args) - ;;; XXX tramp bug, the stderr buffer must be empty - :stderr ,proc-buf - :noquery t - :command ,args - :filter ,proc-filter - :sentinel ,proc-sentinel))))))) - nil) - ('destroy - (when proc - (delete-process proc) - (kill-buffer proc-buf) - (setq proc nil proc-buf nil)) - (funcall async 'destroy)) - (_ (funcall async action)))))) - -(defun consult--async-highlight (async builder) - "Return a new ASYNC function with candidate highlighting. -BUILDER is the command line builder function." - (let (highlight) - (lambda (action) - (cond - ((stringp action) - (setq highlight (cdr (funcall builder action))) - (funcall async action)) - ((and (consp action) highlight) - (dolist (str action) - (funcall highlight str)) - (funcall async action)) - (t (funcall async action)))))) - -(defun consult--async-throttle (async &optional throttle debounce) - "Create async function from ASYNC which throttles input. - -The THROTTLE delay defaults to `consult-async-input-throttle'. -The DEBOUNCE delay defaults to `consult-async-input-debounce'." - (setq throttle (or throttle consult-async-input-throttle) - debounce (or debounce consult-async-input-debounce)) - (let* ((input "") (timer (timer-create)) (last 0)) - (lambda (action) - (pcase action - ((pred stringp) - (unless (equal action input) - (cancel-timer timer) - (funcall async "") ;; cancel running process - (setq input action) - (unless (equal action "") - (timer-set-function timer (lambda () - (setq last (float-time)) - (funcall async action))) - (timer-set-time - timer - (timer-relative-time - nil (max debounce (- (+ last throttle) (float-time))))) - (timer-activate timer))) - nil) - ('destroy - (cancel-timer timer) - (funcall async 'destroy)) - (_ (funcall async action)))))) - -(defun consult--async-refresh-immediate (async) - "Create async function from ASYNC, which refreshes the display. - -The refresh happens immediately when candidates are pushed." - (lambda (action) - (pcase action - ((or (pred consp) 'flush) - (prog1 (funcall async action) - (funcall async 'refresh))) - (_ (funcall async action))))) - -(defun consult--async-refresh-timer (async &optional delay) - "Create async function from ASYNC, which refreshes the display. - -The refresh happens after a DELAY, defaulting to `consult-async-refresh-delay'." - (let ((delay (or delay consult-async-refresh-delay)) - (timer (timer-create))) - (timer-set-function timer async '(refresh)) - (lambda (action) - (prog1 (funcall async action) - (pcase action - ((or (pred consp) 'flush) - (unless (memq timer timer-list) - (timer-set-time timer (timer-relative-time nil delay)) - (timer-activate timer))) - ('destroy - (cancel-timer timer))))))) - -(defmacro consult--async-command (builder &rest args) - "Asynchronous command pipeline. -ARGS is a list of `make-process' properties and transforms. -BUILDER is the command line builder function, which takes the -input string and must either return a list of command line -arguments or a pair of the command line argument list and a -highlighting function." - (declare (indent 1)) - `(thread-first - (consult--async-sink) - (consult--async-refresh-timer) - ,@(seq-take-while (lambda (x) (not (keywordp x))) args) - (consult--async-process - ,builder - ,@(seq-drop-while (lambda (x) (not (keywordp x))) args)) - (consult--async-throttle) - (consult--async-split))) - -(defmacro consult--async-transform (async &rest transform) - "Use FUN to TRANSFORM candidates of ASYNC." - (cl-with-gensyms (async-var action-var) - `(let ((,async-var ,async)) - (lambda (,action-var) - (funcall ,async-var (if (consp ,action-var) (,@transform ,action-var) ,action-var)))))) - -(defun consult--async-map (async fun) - "Map candidates of ASYNC by FUN." - (consult--async-transform async mapcar fun)) - -(defun consult--async-filter (async fun) - "Filter candidates of ASYNC by FUN." - (consult--async-transform async seq-filter fun)) - -;;;; Dynamic collections based - -(defun consult--dynamic-compute (async fun &optional debounce) - "Dynamic computation of candidates. -ASYNC is the sink. -FUN computes the candidates given the input. -DEBOUNCE is the time after which an interrupted computation -should be restarted." - (setq debounce (or debounce consult-async-input-debounce)) - (setq async (consult--async-indicator async)) - (let* ((request) (current) (timer) - (cancel (lambda () (when timer (cancel-timer timer) (setq timer nil)))) - (start (lambda (req) (setq request req) (funcall async 'refresh)))) - (lambda (action) - (pcase action - ((and 'nil (guard (not request))) - (funcall async nil)) - ('nil - (funcall cancel) - (let ((state 'killed)) - (unwind-protect - (progn - (funcall async 'indicator 'running) - (redisplay) - ;; Run computation - (let ((response (funcall fun request))) - ;; Flush and update candidate list - (funcall async 'flush) - (setq state 'finished current request) - (funcall async response))) - (funcall async 'indicator state) - ;; If the computation was killed, restart it after some time. - (when (eq state 'killed) - (setq timer (run-at-time debounce nil start request))) - (setq request nil)))) - ((pred stringp) - (funcall cancel) - (if (or (equal action "") (equal action current)) - (funcall async 'indicator 'finished) - (funcall start action))) - ('destroy - (funcall cancel) - (funcall async 'destroy)) - (_ (funcall async action)))))) - -(defun consult--dynamic-collection (fun) - "Dynamic collection with input splitting. -FUN computes the candidates given the input." - (thread-first - (consult--async-sink) - (consult--dynamic-compute fun) - (consult--async-throttle) - (consult--async-split))) - -;;;; Special keymaps - -(defvar-keymap consult-async-map - :doc "Keymap added for commands with asynchronous candidates." - ;; Overwriting some unusable defaults of default minibuffer completion. - " " #'self-insert-command - ;; Remap Emacs 29 history and default completion for now - ;; (gh:minad/consult#613). - " " #'ignore - " " #'consult-history) - -(defvar-keymap consult-narrow-map - :doc "Narrowing keymap which is added to the local minibuffer map. -Note that `consult-narrow-key' and `consult-widen-key' are bound dynamically." - "SPC" consult--narrow-space - "DEL" consult--narrow-delete) - -;;;; Internal API: consult--read - -(defun consult--annotate-align (cand ann) - "Align annotation ANN by computing the maximum CAND width." - (setq consult--annotate-align-width - (max consult--annotate-align-width - (* (ceiling (consult--display-width cand) - consult--annotate-align-step) - consult--annotate-align-step))) - (when ann - (concat - #(" " 0 1 (display (space :align-to (+ left consult--annotate-align-width)))) - ann))) - -(defun consult--add-history (async items) - "Add ITEMS to the minibuffer future history. -ASYNC must be non-nil for async completion functions." - (delete-dups - (append - ;; the defaults are at the beginning of the future history - (ensure-list minibuffer-default) - ;; then our custom items - (remove "" (remq nil (ensure-list items))) - ;; Add all the completions for non-async commands. For async commands this - ;; feature is not useful, since if one selects a completion candidate, the - ;; async search is restarted using that candidate string. This usually does - ;; not yield a desired result since the async input uses a special format, - ;; e.g., `#grep#filter'. - (unless async - (all-completions "" - minibuffer-completion-table - minibuffer-completion-predicate))))) - -(defun consult--setup-keymap (keymap async narrow preview-key) - "Setup minibuffer keymap. - -KEYMAP is a command-specific keymap. -ASYNC must be non-nil for async completion functions. -NARROW are the narrow settings. -PREVIEW-KEY are the preview keys." - (let ((old-map (current-local-map)) - (map (make-sparse-keymap))) - - ;; Add narrow keys - (when narrow - (consult--narrow-setup narrow map)) - - ;; Preview trigger keys - (when (and (consp preview-key) (memq :keys preview-key)) - (setq preview-key (plist-get preview-key :keys))) - (setq preview-key (mapcar #'car (consult--preview-key-normalize preview-key))) - (when preview-key - (dolist (key preview-key) - (unless (or (eq key 'any) (lookup-key old-map key)) - (define-key map key #'ignore)))) - - ;; Put the keymap together - (use-local-map - (make-composed-keymap - (delq nil (list keymap - (and async consult-async-map) - (and narrow consult-narrow-map) - map)) - old-map)))) - -(defun consult--tofu-hide-in-minibuffer (&rest _) - "Hide the tofus in the minibuffer." - (let* ((min (minibuffer-prompt-end)) - (max (point-max)) - (pos max)) - (while (and (> pos min) (consult--tofu-p (char-before pos))) - (cl-decf pos)) - (when (< pos max) - (add-text-properties pos max '(invisible t rear-nonsticky t cursor-intangible t))))) - -(defun consult--read-annotate (fun cand) - "Annotate CAND with annotation function FUN." - (pcase (funcall fun cand) - (`(,_ ,_ ,suffix) suffix) - (ann ann))) - -(defun consult--read-affixate (fun cands) - "Affixate CANDS with annotation function FUN." - (mapcar (lambda (cand) - (let ((ann (funcall fun cand))) - (if (consp ann) - ann - (setq ann (or ann "")) - (list cand "" - ;; The default completion UI adds the - ;; `completions-annotations' face if no other faces are - ;; present. - (if (text-property-not-all 0 (length ann) 'face nil ann) - ann - (propertize ann 'face 'completions-annotations)))))) - cands)) - -(cl-defun consult--read-1 (table &key - prompt predicate require-match history default - keymap category initial narrow add-history annotate - state preview-key sort lookup group inherit-input-method) - "See `consult--read' for the documentation of the arguments." - (consult--minibuffer-with-setup-hook - (:append (lambda () - (add-hook 'after-change-functions #'consult--tofu-hide-in-minibuffer nil 'local) - (consult--setup-keymap keymap (consult--async-p table) narrow preview-key) - (setq-local minibuffer-default-add-function - (apply-partially #'consult--add-history (consult--async-p table) add-history)))) - (consult--with-async (async table) - (consult--with-preview - preview-key state - (lambda (narrow input cand) - (funcall lookup cand (funcall async nil) input narrow)) - (apply-partially #'run-hook-with-args-until-success - 'consult--completion-candidate-hook) - (pcase-exhaustive history - (`(:input ,var) var) - ((pred symbolp))) - ;; Do not unnecessarily let-bind the lambdas to avoid over-capturing in - ;; the interpreter. This will make closures and the lambda string - ;; representation larger, which makes debugging much worse. Fortunately - ;; the over-capturing problem does not affect the bytecode interpreter - ;; which does a proper scope analysis. - (let* ((metadata `(metadata - ,@(when category `((category . ,category))) - ,@(when group `((group-function . ,group))) - ,@(when annotate - `((affixation-function - . ,(apply-partially #'consult--read-affixate annotate)) - (annotation-function - . ,(apply-partially #'consult--read-annotate annotate)))) - ,@(unless sort '((cycle-sort-function . identity) - (display-sort-function . identity))))) - (consult--annotate-align-width 0) - (selected - (completing-read - prompt - (lambda (str pred action) - (let ((result (complete-with-action action (funcall async nil) str pred))) - (if (eq action 'metadata) - (if (and (eq (car result) 'metadata) (cdr result)) - ;; Merge metadata - `(metadata ,@(cdr metadata) ,@(cdr result)) - metadata) - result))) - predicate require-match initial - (if (symbolp history) history (cadr history)) - default - inherit-input-method))) - ;; Repair the null completion semantics. `completing-read' may return - ;; an empty string even if REQUIRE-MATCH is non-nil. One can always - ;; opt-in to null completion by passing the empty string for DEFAULT. - (when (and (eq require-match t) (not default) (equal selected "")) - (user-error "No selection")) - selected))))) - -(cl-defun consult--read (table &rest options &key - prompt predicate require-match history default - keymap category initial narrow add-history annotate - state preview-key sort lookup group inherit-input-method) - "Enhanced completing read function to select from TABLE. - -The function is a thin wrapper around `completing-read'. Keyword -arguments are used instead of positional arguments for code -clarity. On top of `completing-read' it additionally supports -computing the candidate list asynchronously, candidate preview -and narrowing. You should use `completing-read' instead of -`consult--read' if you don't use asynchronous candidate -computation or candidate preview. - -Keyword OPTIONS: - -PROMPT is the string which is shown as prompt in the minibuffer. -PREDICATE is a filter function called for each candidate, returns -nil or t. -REQUIRE-MATCH equals t means that an exact match is required. -HISTORY is the symbol of the history variable. -DEFAULT is the default selected value. -ADD-HISTORY is a list of items to add to the history. -CATEGORY is the completion category symbol. -SORT should be set to nil if the candidates are already sorted. -This will disable sorting in the completion UI. -LOOKUP is a lookup function passed the selected candidate string, -the list of candidates, the current input string and the current -narrowing value. -ANNOTATE is a function passed a candidate string. The function -should either return an annotation string or a list of three -strings (candidate prefix postfix). -INITIAL is the initial input string. -STATE is the state function, see `consult--with-preview'. -GROUP is a completion metadata `group-function' as documented in -the Elisp manual. -PREVIEW-KEY are the preview keys. Can be nil, `any', a single -key or a list of keys. -NARROW is an alist of narrowing prefix strings and description. -KEYMAP is a command-specific keymap. -INHERIT-INPUT-METHOD, if non-nil the minibuffer inherits the -input method." - ;; supported types - (cl-assert (or (functionp table) ;; dynamic table or asynchronous function - (obarrayp table) ;; obarray - (hash-table-p table) ;; hash table - (not table) ;; empty list - (stringp (car table)) ;; string list - (and (consp (car table)) (stringp (caar table))) ;; string alist - (and (consp (car table)) (symbolp (caar table))))) ;; symbol alist - (ignore prompt predicate require-match history default - keymap category initial narrow add-history annotate - state preview-key sort lookup group inherit-input-method) - (apply #'consult--read-1 table - (append - (consult--customize-get) - options - (list :prompt "Select: " - :preview-key consult-preview-key - :sort t - :lookup (lambda (selected &rest _) selected))))) - -;;;; Internal API: consult--prompt - -(cl-defun consult--prompt-1 (&key prompt history add-history initial default - keymap state preview-key transform inherit-input-method) - "See `consult--prompt' for documentation." - (consult--minibuffer-with-setup-hook - (:append (lambda () - (consult--setup-keymap keymap nil nil preview-key) - (setq-local minibuffer-default-add-function - (apply-partially #'consult--add-history nil add-history)))) - (consult--with-preview - preview-key state - (lambda (_narrow inp _cand) (funcall transform inp)) - (lambda () "") - history - (read-from-minibuffer prompt initial nil nil history default inherit-input-method)))) - -(cl-defun consult--prompt (&rest options &key prompt history add-history initial default - keymap state preview-key transform inherit-input-method) - "Read from minibuffer. - -Keyword OPTIONS: - -PROMPT is the string to prompt with. -TRANSFORM is a function which is applied to the current input string. -HISTORY is the symbol of the history variable. -INITIAL is initial input. -DEFAULT is the default selected value. -ADD-HISTORY is a list of items to add to the history. -STATE is the state function, see `consult--with-preview'. -PREVIEW-KEY are the preview keys (nil, `any', a single key or a list of keys). -KEYMAP is a command-specific keymap." - (ignore prompt history add-history initial default - keymap state preview-key transform inherit-input-method) - (apply #'consult--prompt-1 - (append - (consult--customize-get) - options - (list :prompt "Input: " - :preview-key consult-preview-key - :transform #'identity)))) - -;;;; Internal API: consult--multi - -(defsubst consult--multi-source (sources cand) - "Lookup source for CAND in SOURCES list." - (aref sources (consult--tofu-get cand))) - -(defun consult--multi-predicate (sources cand) - "Predicate function called for each candidate CAND given SOURCES." - (let* ((src (consult--multi-source sources cand)) - (narrow (plist-get src :narrow)) - (type (or (car-safe narrow) narrow -1))) - (or (eq consult--narrow type) - (not (or consult--narrow (plist-get src :hidden)))))) - -(defun consult--multi-narrow (sources) - "Return narrow list from SOURCES." - (thread-last sources - (mapcar (lambda (src) - (when-let (narrow (plist-get src :narrow)) - (if (consp narrow) - narrow - (when-let (name (plist-get src :name)) - (cons narrow name)))))) - (delq nil) - (delete-dups))) - -(defun consult--multi-annotate (sources cand) - "Annotate candidate CAND from multi SOURCES." - (consult--annotate-align - cand - (let ((src (consult--multi-source sources cand))) - (if-let ((fun (plist-get src :annotate))) - (funcall fun (cdr (get-text-property 0 'multi-category cand))) - (plist-get src :name))))) - -(defun consult--multi-group (sources cand transform) - "Return title of candidate CAND or TRANSFORM the candidate given SOURCES." - (if transform cand - (plist-get (consult--multi-source sources cand) :name))) - -(defun consult--multi-preview-key (sources) - "Return preview keys from SOURCES." - (list :predicate - (lambda (cand) - (if (plist-member (cdr cand) :preview-key) - (plist-get (cdr cand) :preview-key) - consult-preview-key)) - :keys - (delete-dups - (seq-mapcat (lambda (src) - (let ((key (if (plist-member src :preview-key) - (plist-get src :preview-key) - consult-preview-key))) - (ensure-list key))) - sources)))) - -(defun consult--multi-lookup (sources selected candidates _input narrow &rest _) - "Lookup SELECTED in CANDIDATES given SOURCES, with potential NARROW." - (if (or (string-blank-p selected) - (not (consult--tofu-p (aref selected (1- (length selected)))))) - ;; Non-existing candidate without Tofu or default submitted (empty string) - (let* ((src (cond - (narrow (seq-find (lambda (src) - (let ((n (plist-get src :narrow))) - (eq (or (car-safe n) n -1) narrow))) - sources)) - ((seq-find (lambda (src) (plist-get src :default)) sources)) - ((seq-find (lambda (src) (not (plist-get src :hidden))) sources)) - ((aref sources 0)))) - (idx (seq-position sources src)) - (def (and (string-blank-p selected) ;; default candidate - (seq-find (lambda (cand) (eq idx (consult--tofu-get cand))) candidates)))) - (if def - (cons (cdr (get-text-property 0 'multi-category def)) src) - `(,selected :match nil ,@src))) - (if-let (found (member selected candidates)) - ;; Existing candidate submitted - (cons (cdr (get-text-property 0 'multi-category (car found))) - (consult--multi-source sources selected)) - ;; Non-existing Tofu'ed candidate submitted, e.g., via Embark - `(,(substring selected 0 -1) :match nil ,@(consult--multi-source sources selected))))) - -(defun consult--multi-candidates (sources) - "Return `consult--multi' candidates from SOURCES." - (let ((idx 0) candidates) - (seq-doseq (src sources) - (let* ((face (and (plist-member src :face) `(face ,(plist-get src :face)))) - (cat (plist-get src :category)) - (items (plist-get src :items)) - (items (if (functionp items) (funcall items) items))) - (dolist (item items) - (let* ((str (or (car-safe item) item)) - (cand (consult--tofu-append str idx))) - ;; Preserve existing `multi-category' datum of the candidate. - (if (and (eq str item) (get-text-property 0 'multi-category str)) - (when face (add-text-properties 0 (length str) face cand)) - ;; Attach `multi-category' datum and face. - (add-text-properties - 0 (length str) - `(multi-category (,cat . ,(or (cdr-safe item) item)) ,@face) cand)) - (push cand candidates)))) - (cl-incf idx)) - (nreverse candidates))) - -(defun consult--multi-enabled-sources (sources) - "Return vector of enabled SOURCES." - (vconcat - (seq-filter (lambda (src) - (if-let (pred (plist-get src :enabled)) - (funcall pred) - t)) - (mapcar (lambda (src) - (if (symbolp src) (symbol-value src) src)) - sources)))) - -(defun consult--multi-state (sources) - "State function given SOURCES." - (when-let (states (delq nil (mapcar (lambda (src) - (when-let (fun (plist-get src :state)) - (cons src (funcall fun)))) - sources))) - (let (last-fun) - (pcase-lambda (action `(,cand . ,src)) - (pcase action - ('setup - (pcase-dolist (`(,_ . ,fun) states) - (funcall fun 'setup nil))) - ('exit - (pcase-dolist (`(,_ . ,fun) states) - (funcall fun 'exit nil))) - ('preview - (let ((selected-fun (cdr (assq src states)))) - ;; If the candidate source changed during preview communicate to - ;; the last source, that none of its candidates is previewed anymore. - (when (and last-fun (not (eq last-fun selected-fun))) - (funcall last-fun 'preview nil)) - (setq last-fun selected-fun) - (when selected-fun - (funcall selected-fun 'preview cand)))) - ('return - (let ((selected-fun (cdr (assq src states)))) - ;; Finish all the sources, except the selected one. - (pcase-dolist (`(,_ . ,fun) states) - (unless (eq fun selected-fun) - (funcall fun 'return nil))) - ;; Finish the source with the selected candidate - (when selected-fun - (funcall selected-fun 'return cand))))))))) - -(defun consult--multi (sources &rest options) - "Select from candidates taken from a list of SOURCES. - -OPTIONS is the plist of options passed to `consult--read'. The following -options are supported: :require-match, :history, :keymap, :initial, -:add-history, :sort and :inherit-input-method. The other options of -`consult--read' are used by the implementation of `consult--multi' and -should not be overwritten, except in in special scenarios. - -The function returns the selected candidate in the form (cons candidate -source-plist). The plist has the key :match with a value nil if the -candidate does not exist, t if the candidate exists and `new' if the -candidate has been created. The sources of the source list can either be -symbols of source variables or source values. Source values must be -plists with fields from the following list. - -Required source fields: -* :category - Completion category symbol. -* :items - List of strings to select from or function returning - list of strings. Note that the strings can use text properties - to carry metadata, which is then available to the :annotate, - :action and :state functions. - -Optional source fields: -* :name - Name of the source as a string, used for narrowing, - group titles and annotations. -* :narrow - Narrowing character or (character . string) pair. -* :enabled - Function which must return t if the source is enabled. -* :hidden - When t candidates of this source are hidden by default. -* :face - Face used for highlighting the candidates. -* :annotate - Annotation function called for each candidate, returns string. -* :history - Name of history variable to add selected candidate. -* :default - Must be t if the first item of the source is the default value. -* :action - Function called with the selected candidate. -* :new - Function called with new candidate name, only if :require-match is nil. -* :state - State constructor for the source, must return the - state function. The state function is informed about state - changes of the UI and can be used to implement preview. -* Other custom source fields can be added depending on the use - case. Note that the source is returned by `consult--multi' - together with the selected candidate." - (let* ((sources (consult--multi-enabled-sources sources)) - (candidates (consult--with-increased-gc - (consult--multi-candidates sources))) - (selected - (apply #'consult--read - candidates - (append - options - (list - :category 'multi-category - :predicate (apply-partially #'consult--multi-predicate sources) - :annotate (apply-partially #'consult--multi-annotate sources) - :group (apply-partially #'consult--multi-group sources) - :lookup (apply-partially #'consult--multi-lookup sources) - :preview-key (consult--multi-preview-key sources) - :narrow (consult--multi-narrow sources) - :state (consult--multi-state sources)))))) - (when-let (history (plist-get (cdr selected) :history)) - (add-to-history history (car selected))) - (if (plist-member (cdr selected) :match) - (when-let (fun (plist-get (cdr selected) :new)) - (funcall fun (car selected)) - (plist-put (cdr selected) :match 'new)) - (when-let (fun (plist-get (cdr selected) :action)) - (funcall fun (car selected))) - (setq selected `(,(car selected) :match t ,@(cdr selected)))) - selected)) - -;;;; Customization macro - -(defun consult--customize-put (cmds prop form) - "Set property PROP to FORM of commands CMDS." - (dolist (cmd cmds) - (cond - ((and (boundp cmd) (consp (symbol-value cmd))) - (setf (plist-get (symbol-value cmd) prop) (eval form 'lexical))) - ((functionp cmd) - (setf (plist-get (alist-get cmd consult--customize-alist) prop) form)) - (t (user-error "%s is neither a Command command nor a source" cmd)))) - nil) - -(defmacro consult-customize (&rest args) - "Set properties of commands or sources. -ARGS is a list of commands or sources followed by the list of -keyword-value pairs. For `consult-customize' to succeed, the -customized sources and commands must exist. When a command is -invoked, the value of `this-command' is used to lookup the -corresponding customization options." - (let (setter) - (while args - (let ((cmds (seq-take-while (lambda (x) (not (keywordp x))) args))) - (setq args (seq-drop-while (lambda (x) (not (keywordp x))) args)) - (while (keywordp (car args)) - (push `(consult--customize-put ',cmds ,(car args) ',(cadr args)) setter) - (setq args (cddr args))))) - (macroexp-progn setter))) - -(defun consult--customize-get (&optional cmd) - "Get configuration from `consult--customize-alist' for CMD." - (mapcar (lambda (x) (eval x 'lexical)) - (alist-get (or cmd this-command) consult--customize-alist))) - -;;;; Commands - -;;;;; Command: consult-completion-in-region - -(defun consult--insertion-preview (start end) - "State function for previewing a candidate in a specific region. -The candidates are previewed in the region from START to END. This function is -used as the `:state' argument for `consult--read' in the `consult-yank' family -of functions and in `consult-completion-in-region'." - (unless (or (minibufferp) - ;; Disable preview if anything odd is going on with the markers. - ;; Otherwise we get "Marker points into wrong buffer errors". See - ;; gh:minad/consult#375, where Org mode source blocks are - ;; completed in a different buffer than the original buffer. This - ;; completion is probably also problematic in my Corfu completion - ;; package. - (not (eq (window-buffer) (current-buffer))) - (and (markerp start) (not (eq (marker-buffer start) (current-buffer)))) - (and (markerp end) (not (eq (marker-buffer end) (current-buffer))))) - (let (ov) - (lambda (action cand) - (cond - ((and (not cand) ov) - (delete-overlay ov) - (setq ov nil)) - ((and (eq action 'preview) cand) - (unless ov - (setq ov (consult--make-overlay start end - 'invisible t - 'window (selected-window)))) - ;; Use `add-face-text-property' on a copy of "cand in order to merge face properties - (setq cand (copy-sequence cand)) - (add-face-text-property 0 (length cand) 'consult-preview-insertion t cand) - ;; Use the `before-string' property since the overlay might be empty. - (overlay-put ov 'before-string cand))))))) - -;;;###autoload -(defun consult-completion-in-region (start end collection &optional predicate) - "Use minibuffer completion as the UI for `completion-at-point'. - -The function is called with 4 arguments: START END COLLECTION -PREDICATE. The arguments and expected return value are as -specified for `completion-in-region'. Use this function as a -value for `completion-in-region-function'." - (barf-if-buffer-read-only) - (let* ((initial (buffer-substring-no-properties start end)) - (metadata (completion-metadata initial collection predicate)) - ;; TODO: `minibuffer-completing-file-name' is mostly deprecated, but - ;; still in use. Packages should instead use the completion metadata. - (minibuffer-completing-file-name - (eq 'file (completion-metadata-get metadata 'category))) - (threshold (completion--cycle-threshold metadata)) - (all (completion-all-completions initial collection predicate (length initial))) - ;; Wrap all annotation functions to ensure that they are executed - ;; in the original buffer. - (exit-fun (plist-get completion-extra-properties :exit-function)) - (ann-fun (plist-get completion-extra-properties :annotation-function)) - (aff-fun (plist-get completion-extra-properties :affixation-function)) - (docsig-fun (plist-get completion-extra-properties :company-docsig)) - (completion-extra-properties - `(,@(and ann-fun (list :annotation-function (consult--in-buffer ann-fun))) - ,@(and aff-fun (list :affixation-function (consult--in-buffer aff-fun))) - ;; Provide `:annotation-function' if `:company-docsig' is specified. - ,@(and docsig-fun (not ann-fun) (not aff-fun) - (list :annotation-function - (consult--in-buffer - (lambda (cand) - (concat (propertize " " 'display '(space :align-to center)) - (funcall docsig-fun cand))))))))) - ;; error if `threshold' is t or the improper list `all' is too short - (if (and threshold - (or (not (consp (ignore-errors (nthcdr threshold all)))) - (and completion-cycling completion-all-sorted-completions))) - (completion--in-region start end collection predicate) - (let* ((limit (car (completion-boundaries initial collection predicate ""))) - (this-command #'consult-completion-in-region) - (completion - (cond - ((atom all) nil) - ((and (consp all) (atom (cdr all))) - (concat (substring initial 0 limit) (car all))) - (t - (consult--local-let ((enable-recursive-minibuffers t)) - ;; Evaluate completion table in the original buffer. - ;; This is a reasonable thing to do and required by - ;; some completion tables in particular by lsp-mode. - ;; See gh:minad/vertico#61. - (consult--read (consult--completion-table-in-buffer collection) - :prompt "Completion: " - :state (consult--insertion-preview start end) - :predicate predicate - :initial initial)))))) - (if completion - (progn - ;; bug#55205: completion--replace removes properties! - (completion--replace start end (setq completion (concat completion))) - (when exit-fun - (funcall exit-fun completion - ;; If completion is finished and cannot be further - ;; completed, return `finished'. Otherwise return - ;; `exact'. - (if (eq (try-completion completion collection predicate) t) - 'finished 'exact))) - t) - (message "No completion") - nil))))) - -;;;;; Command: consult-outline - -(defun consult--outline-candidates () - "Return alist of outline headings and positions." - (consult--forbid-minibuffer) - (let* ((line (line-number-at-pos (point-min) consult-line-numbers-widen)) - (heading-regexp (concat "^\\(?:" - ;; default definition from outline.el - (or (bound-and-true-p outline-regexp) "[*\^L]+") - "\\)")) - (heading-alist (bound-and-true-p outline-heading-alist)) - (level-fun (or (bound-and-true-p outline-level) - (lambda () ;; as in the default from outline.el - (or (cdr (assoc (match-string 0) heading-alist)) - (- (match-end 0) (match-beginning 0)))))) - (buffer (current-buffer)) - candidates) - (save-excursion - (goto-char (point-min)) - (while (save-excursion - (if-let (fun (bound-and-true-p outline-search-function)) - (funcall fun) - (re-search-forward heading-regexp nil t))) - (cl-incf line (consult--count-lines (match-beginning 0))) - (push (consult--location-candidate - (consult--buffer-substring (pos-bol) (pos-eol) 'fontify) - (cons buffer (point)) (1- line) (1- line) - 'consult--outline-level (funcall level-fun)) - candidates) - (goto-char (1+ (pos-eol))))) - (unless candidates - (user-error "No headings")) - (nreverse candidates))) - -;;;###autoload -(defun consult-outline (&optional level) - "Jump to an outline heading, obtained by matching against `outline-regexp'. - -This command supports narrowing to a heading level and candidate -preview. The initial narrowing LEVEL can be given as prefix -argument. The symbol at point is added to the future history." - (interactive - (list (and current-prefix-arg (prefix-numeric-value current-prefix-arg)))) - (let* ((candidates (consult--slow-operation - "Collecting headings..." - (consult--outline-candidates))) - (min-level (- (cl-loop for cand in candidates minimize - (get-text-property 0 'consult--outline-level cand)) - ?1)) - (narrow-pred (lambda (cand) - (<= (get-text-property 0 'consult--outline-level cand) - (+ consult--narrow min-level)))) - (narrow-keys (mapcar (lambda (c) (cons c (format "Level %c" c))) - (number-sequence ?1 ?9))) - (narrow-init (and level (max ?1 (min ?9 (+ level ?0)))))) - (consult--read - candidates - :prompt "Go to heading: " - :annotate (consult--line-prefix) - :category 'consult-location - :sort nil - :require-match t - :lookup #'consult--line-match - :narrow `(:predicate ,narrow-pred :keys ,narrow-keys :initial ,narrow-init) - :history '(:input consult--line-history) - :add-history (thing-at-point 'symbol) - :state (consult--location-state candidates)))) - -;;;;; Command: consult-mark - -(defun consult--mark-candidates (markers) - "Return list of candidates strings for MARKERS." - (consult--forbid-minibuffer) - (let ((candidates) - (current-buf (current-buffer))) - (save-excursion - (dolist (marker markers) - (when-let ((pos (marker-position marker)) - (buf (marker-buffer marker))) - (when (and (eq buf current-buf) - (consult--in-range-p pos)) - (goto-char pos) - ;; `line-number-at-pos' is a very slow function, which should be - ;; replaced everywhere. However in this case the slow - ;; line-number-at-pos does not hurt much, since the mark ring is - ;; usually small since it is limited by `mark-ring-max'. - (push (consult--location-candidate - (consult--line-with-mark marker) marker - (line-number-at-pos pos consult-line-numbers-widen) - marker) - candidates))))) - (unless candidates - (user-error "No marks")) - (nreverse (delete-dups candidates)))) - -;;;###autoload -(defun consult-mark (&optional markers) - "Jump to a marker in MARKERS list (defaults to buffer-local `mark-ring'). - -The command supports preview of the currently selected marker position. -The symbol at point is added to the future history." - (interactive) - (consult--read - (consult--mark-candidates - (or markers (cons (mark-marker) mark-ring))) - :prompt "Go to mark: " - :annotate (consult--line-prefix) - :category 'consult-location - :sort nil - :require-match t - :lookup #'consult--lookup-location - :history '(:input consult--line-history) - :add-history (thing-at-point 'symbol) - :state (consult--jump-state))) - -;;;;; Command: consult-global-mark - -(defun consult--global-mark-candidates (markers) - "Return list of candidates strings for MARKERS." - (consult--forbid-minibuffer) - (let ((candidates)) - (save-excursion - (dolist (marker markers) - (when-let ((pos (marker-position marker)) - (buf (marker-buffer marker))) - (unless (minibufferp buf) - (with-current-buffer buf - (when (consult--in-range-p pos) - (goto-char pos) - ;; `line-number-at-pos' is slow, see comment in `consult--mark-candidates'. - (let* ((line (line-number-at-pos pos consult-line-numbers-widen)) - (prefix (consult--format-file-line-match (buffer-name buf) line "")) - (cand (concat prefix (consult--line-with-mark marker) (consult--tofu-encode marker)))) - (put-text-property 0 (length prefix) 'consult-strip t cand) - (put-text-property 0 (length cand) 'consult-location (cons marker line) cand) - (push cand candidates)))))))) - (unless candidates - (user-error "No global marks")) - (nreverse (delete-dups candidates)))) - -;;;###autoload -(defun consult-global-mark (&optional markers) - "Jump to a marker in MARKERS list (defaults to `global-mark-ring'). - -The command supports preview of the currently selected marker position. -The symbol at point is added to the future history." - (interactive) - (consult--read - (consult--global-mark-candidates - (or markers global-mark-ring)) - :prompt "Go to global mark: " - ;; Despite `consult-global-mark' formatting the candidates in grep-like - ;; style, we are not using the `consult-grep' category, since the candidates - ;; have location markers attached. - :category 'consult-location - :sort nil - :require-match t - :lookup #'consult--lookup-location - :history '(:input consult--line-history) - :add-history (thing-at-point 'symbol) - :state (consult--jump-state))) - -;;;;; Command: consult-line - -(defun consult--line-candidates (top curr-line) - "Return list of line candidates. -Start from top if TOP non-nil. -CURR-LINE is the current line number." - (consult--forbid-minibuffer) - (consult--fontify-all) - (let* ((buffer (current-buffer)) - (line (line-number-at-pos (point-min) consult-line-numbers-widen)) - default-cand candidates) - (consult--each-line beg end - (unless (looking-at-p "^\\s-*$") - (push (consult--location-candidate - (consult--buffer-substring beg end) - (cons buffer beg) line line) - candidates) - (when (and (not default-cand) (>= line curr-line)) - (setq default-cand candidates))) - (cl-incf line)) - (unless candidates - (user-error "No lines")) - (nreverse - (if (or top (not default-cand)) - candidates - (let ((before (cdr default-cand))) - (setcdr default-cand nil) - (nconc before candidates)))))) - -(defun consult--line-point-placement (selected candidates highlighted &rest ignored-faces) - "Find point position on matching line. -SELECTED is the currently selected candidate. -CANDIDATES is the list of candidates. -HIGHLIGHTED is the highlighted string to determine the match position. -IGNORED-FACES are ignored when determining the match position." - (when-let (pos (consult--lookup-location selected candidates)) - (if highlighted - (let* ((matches (apply #'consult--point-placement highlighted 0 ignored-faces)) - (dest (+ pos (car matches)))) - ;; Only create a new marker when jumping across buffers (for example - ;; `consult-line-multi'). Avoid creating unnecessary markers, when - ;; scrolling through candidates, since creating markers is not free. - (when (and (markerp pos) (not (eq (marker-buffer pos) (current-buffer)))) - (setq dest (move-marker (make-marker) dest (marker-buffer pos)))) - (cons dest (cdr matches))) - pos))) - -(defun consult--line-match (selected candidates input &rest _) - "Lookup position of match. -SELECTED is the currently selected candidate. -CANDIDATES is the list of candidates. -INPUT is the input string entered by the user." - (consult--line-point-placement selected candidates - (and (not (string-blank-p input)) - (car (consult--completion-filter - input - (list (substring-no-properties selected)) - 'consult-location 'highlight))) - 'completions-first-difference)) - -;;;###autoload -(defun consult-line (&optional initial start) - "Search for a matching line. - -Depending on the setting `consult-point-placement' the command -jumps to the beginning or the end of the first match on the line -or the line beginning. The default candidate is the non-empty -line next to point. This command obeys narrowing. Optional -INITIAL input can be provided. The search starting point is -changed if the START prefix argument is set. The symbol at point -and the last `isearch-string' is added to the future history." - (interactive (list nil (not (not current-prefix-arg)))) - (let* ((curr-line (line-number-at-pos (point) consult-line-numbers-widen)) - (top (not (eq start consult-line-start-from-top))) - (candidates (consult--slow-operation "Collecting lines..." - (consult--line-candidates top curr-line)))) - (consult--read - candidates - :prompt (if top "Go to line from top: " "Go to line: ") - :annotate (consult--line-prefix curr-line) - :category 'consult-location - :sort nil - :require-match t - ;; Always add last `isearch-string' to future history - :add-history (list (thing-at-point 'symbol) isearch-string) - :history '(:input consult--line-history) - :lookup #'consult--line-match - :default (car candidates) - ;; Add `isearch-string' as initial input if starting from Isearch - :initial (or initial - (and isearch-mode - (prog1 isearch-string (isearch-done)))) - :state (consult--location-state candidates)))) - -;;;;; Command: consult-line-multi - -(defun consult--line-multi-match (selected candidates &rest _) - "Lookup position of match. -SELECTED is the currently selected candidate. -CANDIDATES is the list of candidates." - (consult--line-point-placement selected candidates - (car (member selected candidates)))) - -(defun consult--line-multi-group (cand transform) - "Group function used by `consult-line-multi'. -If TRANSFORM non-nil, return transformed CAND, otherwise return title." - (if transform cand - (let* ((marker (car (get-text-property 0 'consult-location cand))) - (buf (if (consp marker) - (car marker) ;; Handle cheap marker - (marker-buffer marker)))) - (if buf (buffer-name buf) "Dead buffer")))) - -(defun consult--line-multi-candidates (buffers input) - "Collect matching candidates from multiple buffers. -INPUT is the user input which should be matched. -BUFFERS is the list of buffers." - (pcase-let ((`(,regexps . ,hl) - (funcall consult--regexp-compiler - input 'emacs completion-ignore-case)) - (candidates nil) - (cand-idx 0)) - (save-match-data - (dolist (buf buffers (nreverse candidates)) - (with-current-buffer buf - (save-excursion - (let ((line (line-number-at-pos (point-min) consult-line-numbers-widen))) - (goto-char (point-min)) - (while (and (not (eobp)) - (save-excursion (re-search-forward (car regexps) nil t))) - (cl-incf line (consult--count-lines (match-beginning 0))) - (let ((bol (pos-bol)) - (eol (pos-eol))) - (goto-char bol) - (when (and (not (looking-at-p "^\\s-*$")) - (seq-every-p (lambda (r) - (goto-char bol) - (re-search-forward r eol t)) - (cdr regexps))) - (push (consult--location-candidate - (funcall hl (buffer-substring-no-properties bol eol)) - (cons buf bol) (1- line) cand-idx) - candidates) - (cl-incf cand-idx)) - (goto-char (1+ eol))))))))))) - -;;;###autoload -(defun consult-line-multi (query &optional initial) - "Search for a matching line in multiple buffers. - -By default search across all project buffers. If the prefix -argument QUERY is non-nil, all buffers are searched. Optional -INITIAL input can be provided. The symbol at point and the last -`isearch-string' is added to the future history. In order to -search a subset of buffers, QUERY can be set to a plist according -to `consult--buffer-query'." - (interactive "P") - (unless (keywordp (car-safe query)) - (setq query (list :sort 'alpha-current :directory (and (not query) 'project)))) - (pcase-let* ((`(,prompt . ,buffers) (consult--buffer-query-prompt "Go to line" query)) - (collection (consult--dynamic-collection - (apply-partially #'consult--line-multi-candidates - buffers)))) - (consult--read - collection - :prompt prompt - :annotate (consult--line-prefix) - :category 'consult-location - :sort nil - :require-match t - ;; Always add last Isearch string to future history - :add-history (mapcar #'consult--async-split-initial - (delq nil (list (thing-at-point 'symbol) - isearch-string))) - :history '(:input consult--line-multi-history) - :lookup #'consult--line-multi-match - ;; Add `isearch-string' as initial input if starting from Isearch - :initial (consult--async-split-initial - (or initial - (and isearch-mode - (prog1 isearch-string (isearch-done))))) - :state (consult--location-state (lambda () (funcall collection nil))) - :group #'consult--line-multi-group))) - -;;;;; Command: consult-keep-lines - -(defun consult--keep-lines-state (filter) - "State function for `consult-keep-lines' with FILTER function." - (let ((font-lock-orig font-lock-mode) - (whitespace-orig (bound-and-true-p whitespace-mode)) - (hl-line-orig (bound-and-true-p hl-line-mode)) - (point-orig (point)) - lines content-orig replace last-input) - (if (use-region-p) - (save-restriction - ;; Use the same behavior as `keep-lines'. - (let ((rbeg (region-beginning)) - (rend (save-excursion - (goto-char (region-end)) - (unless (or (bolp) (eobp)) - (forward-line 0)) - (point)))) - (consult--fontify-region rbeg rend) - (narrow-to-region rbeg rend) - (consult--each-line beg end - (push (consult--buffer-substring beg end) lines)) - (setq content-orig (buffer-string) - replace (lambda (content &optional pos) - (delete-region rbeg rend) - (insert-before-markers content) - (goto-char (or pos rbeg)) - (setq rend (+ rbeg (length content))) - (add-face-text-property rbeg rend 'region t))))) - (consult--fontify-all) - (setq content-orig (buffer-string) - replace (lambda (content &optional pos) - (delete-region (point-min) (point-max)) - (insert content) - (goto-char (or pos (point-min))))) - (consult--each-line beg end - (push (consult--buffer-substring beg end) lines))) - (setq lines (nreverse lines)) - (lambda (action input) - ;; Restoring content and point position - (when (and (eq action 'return) last-input) - ;; No undo recording, modification hooks, buffer modified-status - (with-silent-modifications (funcall replace content-orig point-orig))) - ;; Committing or new input provided -> Update - (when (and input ;; Input has been provided - (or - ;; Committing, but not with empty input - (and (eq action 'return) (not (string-match-p "\\`!? ?\\'" input))) - ;; Input has changed - (not (equal input last-input)))) - (let ((filtered-content - (if (string-match-p "\\`!? ?\\'" input) - ;; Special case the empty input for performance. - ;; Otherwise it could happen that the minibuffer is empty, - ;; but the buffer has not been updated. - content-orig - (if (eq action 'return) - (apply #'concat (mapcan (lambda (x) (list x "\n")) - (funcall filter input lines))) - (while-no-input - ;; Heavy computation is interruptible if *not* committing! - ;; Allocate new string candidates since the matching function mutates! - (apply #'concat (mapcan (lambda (x) (list x "\n")) - (funcall filter input (mapcar #'copy-sequence lines))))))))) - (when (stringp filtered-content) - (when font-lock-mode (font-lock-mode -1)) - (when (bound-and-true-p whitespace-mode) (whitespace-mode -1)) - (when (bound-and-true-p hl-line-mode) (hl-line-mode -1)) - (if (eq action 'return) - (atomic-change-group - ;; Disable modification hooks for performance - (let ((inhibit-modification-hooks t)) - (funcall replace filtered-content))) - ;; No undo recording, modification hooks, buffer modified-status - (with-silent-modifications - (funcall replace filtered-content) - (setq last-input input)))))) - ;; Restore modes - (when (eq action 'return) - (when hl-line-orig (hl-line-mode 1)) - (when whitespace-orig (whitespace-mode 1)) - (when font-lock-orig (font-lock-mode 1)))))) - -;;;###autoload -(defun consult-keep-lines (filter &optional initial) - "Select a subset of the lines in the current buffer with live preview. - -The selected lines are kept and the other lines are deleted. When called -interactively, the lines selected are those that match the minibuffer input. In -order to match the inverse of the input, prefix the input with `! '. When -called from Elisp, the filtering is performed by a FILTER function. This -command obeys narrowing. - -FILTER is the filter function. -INITIAL is the initial input." - (interactive - (list (lambda (pattern cands) - ;; Use consult-location completion category when filtering lines - (consult--completion-filter-dispatch - pattern cands 'consult-location 'highlight)))) - (consult--forbid-minibuffer) - (let ((ro buffer-read-only)) - (unwind-protect - (consult--minibuffer-with-setup-hook - (lambda () - (when ro - (minibuffer-message - (substitute-command-keys - " [Unlocked read-only buffer. \\[minibuffer-keyboard-quit] to quit.]")))) - (setq buffer-read-only nil) - (consult--with-increased-gc - (consult--prompt - :prompt "Keep lines: " - :initial initial - :history 'consult--line-history - :state (consult--keep-lines-state filter)))) - (setq buffer-read-only ro)))) - -;;;;; Command: consult-focus-lines - -(defun consult--focus-lines-state (filter) - "State function for `consult-focus-lines' with FILTER function." - (let (lines overlays last-input pt-orig pt-min pt-max) - (save-excursion - (save-restriction - (if (not (use-region-p)) - (consult--fontify-all) - (consult--fontify-region (region-beginning) (region-end)) - (narrow-to-region - (region-beginning) - ;; Behave the same as `keep-lines'. - ;; Move to the next line. - (save-excursion - (goto-char (region-end)) - (unless (or (bolp) (eobp)) - (forward-line 0)) - (point)))) - (setq pt-orig (point) pt-min (point-min) pt-max (point-max)) - (let ((i 0)) - (consult--each-line beg end - ;; Use "\n" for empty lines, since we need a non-empty string to - ;; attach the text property to. - (let ((line (if (eq beg end) (char-to-string ?\n) - (buffer-substring-no-properties beg end)))) - (put-text-property 0 1 'consult--focus-line (cons (cl-incf i) beg) line) - (push line lines))) - (setq lines (nreverse lines))))) - (lambda (action input) - ;; New input provided -> Update - (when (and input (not (equal input last-input))) - (let (new-overlays) - (pcase (while-no-input - (unless (string-match-p "\\`!? ?\\'" input) ;; Empty input. - (let* ((inhibit-quit (eq action 'return)) ;; Non interruptible, when quitting! - (not (string-prefix-p "! " input)) - (stripped (string-remove-prefix "! " input)) - (matches (funcall filter stripped lines)) - (old-ind 0) - (block-beg pt-min) - (block-end pt-min)) - (while old-ind - (let ((match (pop matches)) (ind nil) (beg pt-max) (end pt-max) prop) - (when match - (setq prop (get-text-property 0 'consult--focus-line match) - ind (car prop) - beg (cdr prop) - ;; Check for empty lines, see above. - end (+ 1 beg (if (equal match "\n") 0 (length match))))) - (unless (eq ind (1+ old-ind)) - (let ((a (if not block-beg block-end)) - (b (if not block-end beg))) - (when (/= a b) - (push (consult--make-overlay a b 'invisible t) new-overlays))) - (setq block-beg beg)) - (setq block-end end old-ind ind))))) - 'commit) - ('commit - (mapc #'delete-overlay overlays) - (setq last-input input overlays new-overlays)) - (_ (mapc #'delete-overlay new-overlays))))) - (when (eq action 'return) - (cond - ((not input) - (mapc #'delete-overlay overlays) - (goto-char pt-orig)) - ((equal input "") - (consult-focus-lines nil 'show) - (goto-char pt-orig)) - (t - ;; Successfully terminated -> Remember invisible overlays - (setq consult--focus-lines-overlays - (nconc consult--focus-lines-overlays overlays)) - ;; move point past invisible - (goto-char (if-let (ov (and (invisible-p pt-orig) - (seq-find (lambda (ov) (overlay-get ov 'invisible)) - (overlays-at pt-orig)))) - (overlay-end ov) - pt-orig)))))))) - -;;;###autoload -(defun consult-focus-lines (filter &optional show initial) - "Hide or show lines using overlays. - -The selected lines are shown and the other lines hidden. When called -interactively, the lines selected are those that match the minibuffer input. In -order to match the inverse of the input, prefix the input with `! '. With -optional prefix argument SHOW reveal the hidden lines. Alternatively the -command can be restarted to reveal the lines. When called from Elisp, the -filtering is performed by a FILTER function. This command obeys narrowing. - -FILTER is the filter function. -INITIAL is the initial input." - (interactive - (list (lambda (pattern cands) - ;; Use consult-location completion category when filtering lines - (consult--completion-filter-dispatch - pattern cands 'consult-location nil)) - current-prefix-arg)) - (if show - (progn - (mapc #'delete-overlay consult--focus-lines-overlays) - (setq consult--focus-lines-overlays nil) - (message "All lines revealed")) - (consult--forbid-minibuffer) - (consult--with-increased-gc - (consult--prompt - :prompt - (if consult--focus-lines-overlays - "Focus on lines (RET to reveal): " - "Focus on lines: ") - :initial initial - :history 'consult--line-history - :state (consult--focus-lines-state filter))))) - -;;;;; Command: consult-goto-line - -(defun consult--goto-line-position (str msg) - "Transform input STR to line number. -Print an error message with MSG function." - (save-match-data - (if (and str (string-match "\\`\\([[:digit:]]+\\):?\\([[:digit:]]*\\)\\'" str)) - (let ((line (string-to-number (match-string 1 str))) - (col (string-to-number (match-string 2 str)))) - (save-excursion - (save-restriction - (when consult-line-numbers-widen - (widen)) - (goto-char (point-min)) - (forward-line (1- line)) - (goto-char (min (+ (point) col) (pos-eol))) - (point)))) - (when (and str (not (equal str ""))) - (funcall msg "Please enter a number.")) - nil))) - -;;;###autoload -(defun consult-goto-line (&optional arg) - "Read line number and jump to the line with preview. - -Enter either a line number to jump to the first column of the -given line or line:column in order to jump to a specific column. -Jump directly if a line number is given as prefix ARG. The -command respects narrowing and the settings -`consult-goto-line-numbers' and `consult-line-numbers-widen'." - (interactive "P") - (if arg - (call-interactively #'goto-line) - (consult--forbid-minibuffer) - (consult--local-let ((display-line-numbers consult-goto-line-numbers) - (display-line-numbers-widen consult-line-numbers-widen)) - (while (if-let (pos (consult--goto-line-position - (consult--prompt - :prompt "Go to line: " - :history 'goto-line-history - :state - (let ((preview (consult--jump-preview))) - (lambda (action str) - (funcall preview action - (consult--goto-line-position str #'ignore))))) - #'minibuffer-message)) - (consult--jump pos) - t))))) - -;;;;; Command: consult-recent-file - -(defun consult--file-preview () - "Create preview function for files." - (let ((open (consult--temporary-files)) - (preview (consult--buffer-preview))) - (lambda (action cand) - (unless cand - (funcall open)) - (funcall preview action - (and cand - (eq action 'preview) - (funcall open cand)))))) - -(defun consult--file-action (file) - "Open FILE via `consult--buffer-action'." - (consult--buffer-action (find-file-noselect file))) - -(consult--define-state file) - -;;;###autoload -(defun consult-recent-file () - "Find recent file using `completing-read'." - (interactive) - (find-file - (consult--read - (or - (mapcar #'consult--fast-abbreviate-file-name (bound-and-true-p recentf-list)) - (user-error "No recent files, `recentf-mode' is %s" - (if recentf-mode "enabled" "disabled"))) - :prompt "Find recent file: " - :sort nil - :require-match t - :category 'file - :state (consult--file-preview) - :history 'file-name-history))) - -;;;;; Command: consult-mode-command - -(defun consult--mode-name (mode) - "Return name part of MODE." - (replace-regexp-in-string - "global-\\(.*\\)-mode" "\\1" - (replace-regexp-in-string - "\\(-global\\)?-mode\\'" "" - (if (eq mode 'c-mode) - "cc" - (symbol-name mode)) - 'fixedcase) - 'fixedcase)) - -(defun consult--mode-command-candidates (modes) - "Extract commands from MODES. - -The list of features is searched for files belonging to the modes. -From these files, the commands are extracted." - (let* ((case-fold-search) - (buffer (current-buffer)) - (command-filter (consult--regexp-filter (seq-filter #'stringp consult-mode-command-filter))) - (feature-filter (seq-filter #'symbolp consult-mode-command-filter)) - (minor-hash (consult--string-hash minor-mode-list)) - (minor-local-modes (seq-filter (lambda (m) - (and (gethash m minor-hash) - (local-variable-if-set-p m))) - modes)) - (minor-global-modes (seq-filter (lambda (m) - (and (gethash m minor-hash) - (not (local-variable-if-set-p m)))) - modes)) - (major-modes (seq-remove (lambda (m) - (gethash m minor-hash)) - modes)) - (major-paths-hash (consult--string-hash (mapcar #'symbol-file major-modes))) - (minor-local-paths-hash (consult--string-hash (mapcar #'symbol-file minor-local-modes))) - (minor-global-paths-hash (consult--string-hash (mapcar #'symbol-file minor-global-modes))) - (major-name-regexp (regexp-opt (mapcar #'consult--mode-name major-modes))) - (minor-local-name-regexp (regexp-opt (mapcar #'consult--mode-name minor-local-modes))) - (minor-global-name-regexp (regexp-opt (mapcar #'consult--mode-name minor-global-modes))) - (commands)) - (dolist (feature load-history commands) - (when-let (name (alist-get 'provide feature)) - (let* ((path (car feature)) - (file (file-name-nondirectory path)) - (key (cond - ((memq name feature-filter) nil) - ((or (gethash path major-paths-hash) - (string-match-p major-name-regexp file)) - ?m) - ((or (gethash path minor-local-paths-hash) - (string-match-p minor-local-name-regexp file)) - ?l) - ((or (gethash path minor-global-paths-hash) - (string-match-p minor-global-name-regexp file)) - ?g)))) - (when key - (dolist (cmd (cdr feature)) - (let ((sym (cdr-safe cmd))) - (when (and (consp cmd) - (eq (car cmd) 'defun) - (commandp sym) - (not (get sym 'byte-obsolete-info)) - ;; Emacs 28 has a `read-extended-command-predicate' - (if (bound-and-true-p read-extended-command-predicate) - (funcall read-extended-command-predicate sym buffer) - t)) - (let ((name (symbol-name sym))) - (unless (string-match-p command-filter name) - (push (propertize name - 'consult--candidate sym - 'consult--type key) - commands)))))))))))) - -;;;###autoload -(defun consult-mode-command (&rest modes) - "Run a command from any of the given MODES. - -If no MODES are specified, use currently active major and minor modes." - (interactive) - (unless modes - (setq modes (cons major-mode - (seq-filter (lambda (m) - (and (boundp m) (symbol-value m))) - minor-mode-list)))) - (let ((narrow `((?m . ,(format "Major: %s" major-mode)) - (?l . "Local Minor") - (?g . "Global Minor")))) - (command-execute - (consult--read - (consult--mode-command-candidates modes) - :prompt "Mode command: " - :predicate - (lambda (cand) - (let ((key (get-text-property 0 'consult--type cand))) - (if consult--narrow - (= key consult--narrow) - (/= key ?g)))) - :lookup #'consult--lookup-candidate - :group (consult--type-group narrow) - :narrow narrow - :require-match t - :history 'extended-command-history - :category 'command)))) - -;;;;; Command: consult-yank - -(defun consult--read-from-kill-ring () - "Open kill ring menu and return selected string." - ;; `current-kill' updates `kill-ring' with interprogram paste, see - ;; gh:minad/consult#443. - (current-kill 0) - ;; Do not specify a :lookup function in order to preserve completion-styles - ;; highlighting of the current candidate. We have to perform a final lookup to - ;; obtain the original candidate which may be propertized with yank-specific - ;; properties, like 'yank-handler. - (consult--lookup-member - (consult--read - (consult--remove-dups - (or (if consult-yank-rotate - (append kill-ring-yank-pointer - (butlast kill-ring (length kill-ring-yank-pointer))) - kill-ring) - (user-error "Kill ring is empty"))) - :prompt "Yank from kill-ring: " - :history t ;; disable history - :sort nil - :category 'kill-ring - :require-match t - :state - (consult--insertion-preview - (point) - ;; If previous command is yank, hide previously yanked string - (or (and (eq last-command 'yank) (mark t)) (point)))) - kill-ring)) - -;; Adapted from the Emacs `yank-from-kill-ring' function. -;;;###autoload -(defun consult-yank-from-kill-ring (string &optional arg) - "Select STRING from the kill ring and insert it. -With prefix ARG, put point at beginning, and mark at end, like `yank' does. - -This command behaves like `yank-from-kill-ring' in Emacs 28, which also offers -a `completing-read' interface to the `kill-ring'. Additionally the Consult -version supports preview of the selected string." - (interactive (list (consult--read-from-kill-ring) current-prefix-arg)) - (when string - (setq yank-window-start (window-start)) - (push-mark) - (insert-for-yank string) - (setq this-command 'yank) - (when consult-yank-rotate - (if-let (pos (seq-position kill-ring string)) - (setq kill-ring-yank-pointer (nthcdr pos kill-ring)) - (kill-new string))) - (when (consp arg) - ;; Swap point and mark like in `yank'. - (goto-char (prog1 (mark t) - (set-marker (mark-marker) (point) (current-buffer))))))) - -(put 'consult-yank-replace 'delete-selection 'yank) -(put 'consult-yank-pop 'delete-selection 'yank) -(put 'consult-yank-from-kill-ring 'delete-selection 'yank) - -;;;###autoload -(defun consult-yank-pop (&optional arg) - "If there is a recent yank act like `yank-pop'. - -Otherwise select string from the kill ring and insert it. -See `yank-pop' for the meaning of ARG. - -This command behaves like `yank-pop' in Emacs 28, which also offers a -`completing-read' interface to the `kill-ring'. Additionally the Consult -version supports preview of the selected string." - (interactive "*p") - (if (eq last-command 'yank) - (yank-pop (or arg 1)) - (call-interactively #'consult-yank-from-kill-ring))) - -;; Adapted from the Emacs yank-pop function. -;;;###autoload -(defun consult-yank-replace (string) - "Select STRING from the kill ring. - -If there was no recent yank, insert the string. -Otherwise replace the just-yanked string with the selected string. - -There exists no equivalent of this command in Emacs 28." - (interactive (list (consult--read-from-kill-ring))) - (when string - (if (not (eq last-command 'yank)) - (consult-yank-from-kill-ring string) - (let ((inhibit-read-only t) - (pt (point)) - (mk (mark t))) - (setq this-command 'yank) - (funcall (or yank-undo-function 'delete-region) (min pt mk) (max pt mk)) - (setq yank-undo-function nil) - (set-marker (mark-marker) pt (current-buffer)) - (insert-for-yank string) - (set-window-start (selected-window) yank-window-start t) - (if (< pt mk) - (goto-char (prog1 (mark t) - (set-marker (mark-marker) (point) (current-buffer))))))))) - -;;;;; Command: consult-bookmark - -(defun consult--bookmark-preview () - "Create preview function for bookmarks." - (let ((preview (consult--jump-preview)) - (open (consult--temporary-files))) - (lambda (action cand) - (unless cand - (funcall open)) - (funcall - preview action - ;; Only preview bookmarks with the default handler. - (when-let ((bm (and cand (eq action 'preview) (assoc cand bookmark-alist))) - (handler (or (bookmark-get-handler bm) #'bookmark-default-handler)) - ((eq handler #'bookmark-default-handler)) - (file (bookmark-get-filename bm)) - (pos (bookmark-get-position bm)) - (buf (funcall open file))) - (set-marker (make-marker) pos buf)))))) - -(defun consult--bookmark-action (bm) - "Open BM via `consult--buffer-action'." - (bookmark-jump bm consult--buffer-display)) - -(consult--define-state bookmark) - -(defun consult--bookmark-candidates () - "Return bookmark candidates." - (bookmark-maybe-load-default-file) - (let ((narrow (cl-loop for (y _ . xs) in consult-bookmark-narrow nconc - (cl-loop for x in xs collect (cons x y))))) - (cl-loop for bm in bookmark-alist collect - (propertize (car bm) - 'consult--type - (alist-get - (or (bookmark-get-handler bm) #'bookmark-default-handler) - narrow))))) - -;;;###autoload -(defun consult-bookmark (name) - "If bookmark NAME exists, open it, otherwise create a new bookmark with NAME. - -The command supports preview of file bookmarks and narrowing. See the -variable `consult-bookmark-narrow' for the narrowing configuration." - (interactive - (list - (let ((narrow (cl-loop for (x y . _) in consult-bookmark-narrow collect (cons x y)))) - (consult--read - (consult--bookmark-candidates) - :prompt "Bookmark: " - :state (consult--bookmark-preview) - :category 'bookmark - :history 'bookmark-history - ;; Add default names to future history. - ;; Ignore errors such that `consult-bookmark' can be used in - ;; buffers which are not backed by a file. - :add-history (ignore-errors (bookmark-prop-get (bookmark-make-record) 'defaults)) - :group (consult--type-group narrow) - :narrow (consult--type-narrow narrow))))) - (bookmark-maybe-load-default-file) - (if (assoc name bookmark-alist) - (bookmark-jump name) - (bookmark-set name))) - -;;;;; Command: consult-complex-command - -;;;###autoload -(defun consult-complex-command () - "Select and evaluate command from the command history. - -This command can act as a drop-in replacement for `repeat-complex-command'." - (interactive) - (let* ((history (or (delete-dups (mapcar #'prin1-to-string command-history)) - (user-error "There are no previous complex commands"))) - (cmd (read (consult--read - history - :prompt "Command: " - :default (car history) - :sort nil - :history t ;; disable history - :category 'expression)))) - ;; Taken from `repeat-complex-command' - (add-to-history 'command-history cmd) - (apply #'funcall-interactively - (car cmd) - (mapcar (lambda (e) (eval e t)) (cdr cmd))))) - -;;;;; Command: consult-history - -(declare-function ring-elements "ring") - -(defun consult--current-history () - "Return the history and index variable relevant to the current buffer. -If the minibuffer is active, the minibuffer history is returned, -otherwise the history corresponding to the mode. There is a -special case for `repeat-complex-command', for which the command -history is used." - (cond - ;; In the minibuffer we use the current minibuffer history, - ;; which can be configured by setting `minibuffer-history-variable'. - ((minibufferp) - (when (eq minibuffer-history-variable t) - (user-error "Minibuffer history is disabled for `%s'" this-command)) - (list (mapcar #'consult--tofu-hide - (if (eq minibuffer-history-variable 'command-history) - ;; If pressing "C-x M-:", i.e., `repeat-complex-command', - ;; we are instead querying the `command-history' and get a - ;; full s-expression. Alternatively you might want to use - ;; `consult-complex-command', which can also be bound to - ;; "C-x M-:"! - (mapcar #'prin1-to-string command-history) - (symbol-value minibuffer-history-variable))))) - ;; Otherwise we use a mode-specific history, see `consult-mode-histories'. - (t (let ((found (seq-find (lambda (h) - (and (derived-mode-p (car h)) - (boundp (if (consp (cdr h)) (cadr h) (cdr h))))) - consult-mode-histories))) - (unless found - (user-error "No history configured for `%s', see `consult-mode-histories'" - major-mode)) - (cons (symbol-value (cadr found)) (cddr found)))))) - -;;;###autoload -(defun consult-history (&optional history index bol) - "Insert string from HISTORY of current buffer. -In order to select from a specific HISTORY, pass the history -variable as argument. INDEX is the name of the index variable to -update, if any. BOL is the function which jumps to the beginning -of the prompt. See also `cape-history' from the Cape package." - (interactive) - (pcase-let* ((`(,history ,index ,bol) (if history - (list history index bol) - (consult--current-history))) - (history (if (ring-p history) (ring-elements history) history)) - (`(,beg . ,end) - (if (minibufferp) - (cons (minibuffer-prompt-end) (point-max)) - (if bol - (save-excursion - (funcall bol) - (cons (point) (pos-eol))) - (cons (point) (point))))) - (str (consult--local-let ((enable-recursive-minibuffers t)) - (consult--read - (or (consult--remove-dups history) - (user-error "History is empty")) - :prompt "History: " - :history t ;; disable history - :category ;; Report category depending on history variable - (and (minibufferp) - (pcase minibuffer-history-variable - ('extended-command-history 'command) - ('buffer-name-history 'buffer) - ('face-name-history 'face) - ('read-envvar-name-history 'environment-variable) - ('bookmark-history 'bookmark) - ('file-name-history 'file))) - :sort nil - :initial (buffer-substring-no-properties beg end) - :state (consult--insertion-preview beg end))))) - (delete-region beg end) - (when index - (set index (seq-position history str))) - (insert (substring-no-properties str)))) - -;;;;; Command: consult-isearch-history - -(defun consult-isearch-forward (&optional reverse) - "Continue Isearch forward optionally in REVERSE." - (interactive) - (consult--require-minibuffer) - (setq isearch-new-forward (not reverse) isearch-new-nonincremental nil) - (funcall (or (command-remapping #'exit-minibuffer) #'exit-minibuffer))) - -(defun consult-isearch-backward (&optional reverse) - "Continue Isearch backward optionally in REVERSE." - (interactive) - (consult-isearch-forward (not reverse))) - -;; Emacs 28: hide in M-X -(put #'consult-isearch-backward 'completion-predicate #'ignore) -(put #'consult-isearch-forward 'completion-predicate #'ignore) - -(defvar-keymap consult-isearch-history-map - :doc "Additional keymap used by `consult-isearch-history'." - " " #'consult-isearch-forward - " " #'consult-isearch-backward) - -(defun consult--isearch-history-candidates () - "Return Isearch history candidates." - ;; Do not throw an error on empty history, in order to allow starting a - ;; search. We do not :require-match here. - (let ((history (if (eq t search-default-mode) - (append regexp-search-ring search-ring) - (append search-ring regexp-search-ring)))) - (delete-dups - (mapcar - (lambda (cand) - ;; The search type can be distinguished via text properties. - (let* ((props (plist-member (text-properties-at 0 cand) - 'isearch-regexp-function)) - (type (pcase (cadr props) - ((and 'nil (guard (not props))) ?r) - ('nil ?l) - ('word-search-regexp ?w) - ('isearch-symbol-regexp ?s) - ('char-fold-to-regexp ?c) - (_ ?u)))) - ;; Disambiguate history items. The same string could - ;; occur with different search types. - (consult--tofu-append cand type))) - history)))) - -(defconst consult--isearch-history-narrow - '((?c . "Char") - (?u . "Custom") - (?l . "Literal") - (?r . "Regexp") - (?s . "Symbol") - (?w . "Word"))) - -;;;###autoload -(defun consult-isearch-history () - "Read a search string with completion from the Isearch history. - -This replaces the current search string if Isearch is active, and -starts a new Isearch session otherwise." - (interactive) - (consult--forbid-minibuffer) - (let* ((isearch-message-function #'ignore) - (cursor-in-echo-area t) ;; Avoid cursor flickering - (candidates (consult--isearch-history-candidates))) - (unless isearch-mode (isearch-mode t)) - (with-isearch-suspended - (setq isearch-new-string - (consult--read - candidates - :prompt "I-search: " - :category 'consult-isearch-history - :history t ;; disable history - :sort nil - :initial isearch-string - :keymap consult-isearch-history-map - :annotate - (lambda (cand) - (consult--annotate-align - cand - (alist-get (consult--tofu-get cand) consult--isearch-history-narrow))) - :group - (lambda (cand transform) - (if transform - cand - (alist-get (consult--tofu-get cand) consult--isearch-history-narrow))) - :lookup - (lambda (selected candidates &rest _) - (if-let (found (member selected candidates)) - (substring (car found) 0 -1) - selected)) - :state - (lambda (action cand) - (when (and (eq action 'preview) cand) - (setq isearch-string cand) - (isearch-update-from-string-properties cand) - (isearch-update))) - :narrow - (list :predicate - (lambda (cand) (= (consult--tofu-get cand) consult--narrow)) - :keys consult--isearch-history-narrow)) - isearch-new-message - (mapconcat 'isearch-text-char-description isearch-new-string ""))) - ;; Setting `isearch-regexp' etc only works outside of `with-isearch-suspended'. - (unless (plist-member (text-properties-at 0 isearch-string) 'isearch-regexp-function) - (setq isearch-regexp t - isearch-regexp-function nil)))) - -;;;;; Command: consult-minor-mode-menu - -(defun consult--minor-mode-candidates () - "Return list of minor-mode candidate strings." - (mapcar - (pcase-lambda (`(,name . ,sym)) - (propertize - name - 'consult--candidate sym - 'consult--minor-mode-narrow - (logior - (ash (if (local-variable-if-set-p sym) ?l ?g) 8) - (if (and (boundp sym) (symbol-value sym)) ?i ?o)) - 'consult--minor-mode-group - (concat - (if (local-variable-if-set-p sym) "Local " "Global ") - (if (and (boundp sym) (symbol-value sym)) "On" "Off")))) - (nconc - ;; according to describe-minor-mode-completion-table-for-symbol - ;; the minor-mode-list contains *all* minor modes - (mapcar (lambda (sym) (cons (symbol-name sym) sym)) minor-mode-list) - ;; take the lighters from minor-mode-alist - (delq nil - (mapcar (pcase-lambda (`(,sym ,lighter)) - (when (and lighter (not (equal "" lighter))) - (let (message-log-max) - (setq lighter (string-trim (format-mode-line lighter))) - (unless (string-blank-p lighter) - (cons lighter sym))))) - minor-mode-alist))))) - -(defconst consult--minor-mode-menu-narrow - '((?l . "Local") - (?g . "Global") - (?i . "On") - (?o . "Off"))) - -;;;###autoload -(defun consult-minor-mode-menu () - "Enable or disable minor mode. - -This is an alternative to `minor-mode-menu-from-indicator'." - (interactive) - (call-interactively - (consult--read - (consult--minor-mode-candidates) - :prompt "Minor mode: " - :require-match t - :category 'minor-mode - :group - (lambda (cand transform) - (if transform cand (get-text-property 0 'consult--minor-mode-group cand))) - :narrow - (list :predicate - (lambda (cand) - (let ((narrow (get-text-property 0 'consult--minor-mode-narrow cand))) - (or (= (logand narrow 255) consult--narrow) - (= (ash narrow -8) consult--narrow)))) - :keys - consult--minor-mode-menu-narrow) - :lookup #'consult--lookup-candidate - :history 'consult--minor-mode-menu-history))) - -;;;;; Command: consult-theme - -;;;###autoload -(defun consult-theme (theme) - "Disable current themes and enable THEME from `consult-themes'. - -The command supports previewing the currently selected theme." - (interactive - (list - (let* ((regexp (consult--regexp-filter - (mapcar (lambda (x) (if (stringp x) x (format "\\`%s\\'" x))) - consult-themes))) - (avail-themes (seq-filter - (lambda (x) (string-match-p regexp (symbol-name x))) - (cons 'default (custom-available-themes)))) - (saved-theme (car custom-enabled-themes))) - (consult--read - (mapcar #'symbol-name avail-themes) - :prompt "Theme: " - :require-match t - :category 'theme - :history 'consult--theme-history - :lookup (lambda (selected &rest _) - (setq selected (and selected (intern-soft selected))) - (or (and selected (car (memq selected avail-themes))) - saved-theme)) - :state (lambda (action theme) - (pcase action - ('return (consult-theme (or theme saved-theme))) - ((and 'preview (guard theme)) (consult-theme theme)))) - :default (symbol-name (or saved-theme 'default)))))) - (when (eq theme 'default) (setq theme nil)) - (unless (eq theme (car custom-enabled-themes)) - (mapc #'disable-theme custom-enabled-themes) - (when theme - (if (custom-theme-p theme) - (enable-theme theme) - (load-theme theme :no-confirm))))) - -;;;;; Command: consult-buffer - -(defun consult--buffer-sort-alpha (buffers) - "Sort BUFFERS alphabetically, put starred buffers at the end." - (sort buffers - (lambda (x y) - (setq x (buffer-name x) y (buffer-name y)) - (let ((a (and (length> x 0) (eq (aref x 0) ?*))) - (b (and (length> y 0) (eq (aref y 0) ?*)))) - (if (eq a b) - (string< x y) - (not a)))))) - -(defun consult--buffer-sort-alpha-current (buffers) - "Sort BUFFERS alphabetically, put current at the beginning." - (let ((buffers (consult--buffer-sort-alpha buffers)) - (current (current-buffer))) - (if (memq current buffers) - (cons current (delq current buffers)) - buffers))) - -(defun consult--buffer-sort-visibility (buffers) - "Sort BUFFERS by visibility." - (let ((hidden) - (current (current-buffer))) - (consult--keep! buffers - (unless (eq it current) - (if (get-buffer-window it 'visible) - it - (push it hidden) - nil))) - (nconc (nreverse hidden) buffers (list current)))) - -(defun consult--normalize-directory (dir) - "Normalize directory DIR. -DIR can be project, nil or a path." - (cond - ((eq dir 'project) (consult--project-root)) - (dir (expand-file-name dir)))) - -(defun consult--buffer-query-prompt (prompt query) - "Return a list of buffers and create an appropriate prompt string. -Return a pair of a prompt string and a list of buffers. PROMPT -is the prefix of the prompt string. QUERY specifies the buffers -to search and is passed to `consult--buffer-query'." - (let* ((dir (plist-get query :directory)) - (ndir (consult--normalize-directory dir)) - (buffers (apply #'consult--buffer-query :directory ndir query)) - (count (length buffers))) - (cons (format "%s (%d buffer%s%s): " prompt count - (if (= count 1) "" "s") - (cond - ((and ndir (eq dir 'project)) - (format ", Project %s" (consult--project-name ndir))) - (ndir (concat ", " (consult--left-truncate-file ndir))) - (t ""))) - buffers))) - -(cl-defun consult--buffer-query (&key sort directory mode as predicate (filter t) - include (exclude consult-buffer-filter)) - "Query for a list of matching buffers. -The function supports filtering by various criteria which are -used throughout Consult. In particular it is the backbone of -most `consult-buffer-sources'. -DIRECTORY can either be the symbol project or a file name. -SORT can be visibility, alpha or nil. -FILTER can be either t, nil or invert. -EXCLUDE is a list of regexps. -INCLUDE is a list of regexps. -MODE can be a mode or a list of modes to restrict the returned buffers. -PREDICATE is a predicate function. -AS is a conversion function." - (let ((root (consult--normalize-directory directory)) - (buffers (buffer-list))) - (when sort - (setq buffers (funcall (intern (format "consult--buffer-sort-%s" sort)) buffers))) - (when (or filter mode as root) - (let ((exclude-re (consult--regexp-filter exclude)) - (include-re (consult--regexp-filter include)) - (case-fold-search)) - (consult--keep! buffers - (and - (or (not mode) - (let ((mm (buffer-local-value 'major-mode it))) - (if (consp mode) - (seq-some (lambda (m) (provided-mode-derived-p mm m)) mode) - (provided-mode-derived-p mm mode)))) - (pcase-exhaustive filter - ('nil t) - ((or 't 'invert) - (eq (eq filter t) - (and - (or (not exclude) - (not (string-match-p exclude-re (buffer-name it)))) - (or (not include) - (not (not (string-match-p include-re (buffer-name it))))))))) - (or (not root) - (when-let (dir (buffer-local-value 'default-directory it)) - (string-prefix-p root - (if (and (/= 0 (length dir)) (eq (aref dir 0) ?/)) - dir - (expand-file-name dir))))) - (or (not predicate) (funcall predicate it)) - (if as (funcall as it) it))))) - buffers)) - -(defun consult--buffer-file-hash () - "Return hash table of all buffer file names." - (consult--string-hash (consult--buffer-query :as #'buffer-file-name))) - -(defun consult--buffer-pair (buffer) - "Return a pair of name of BUFFER and BUFFER." - (cons (buffer-name buffer) buffer)) - -(defun consult--buffer-preview () - "Buffer preview function." - (let ((orig-buf (window-buffer (consult--original-window))) - (orig-prev (copy-sequence (window-prev-buffers))) - (orig-next (copy-sequence (window-next-buffers))) - other-win) - (lambda (action cand) - (pcase action - ('exit - (set-window-prev-buffers other-win orig-prev) - (set-window-next-buffers other-win orig-next)) - ('preview - (when (and (eq consult--buffer-display #'switch-to-buffer-other-window) - (not other-win)) - (switch-to-buffer-other-window orig-buf 'norecord) - (setq other-win (selected-window))) - (let ((win (or other-win (selected-window))) - (buf (or (and cand (get-buffer cand)) orig-buf))) - (when (and (window-live-p win) (buffer-live-p buf)) - (with-selected-window win - (unless (or orig-prev orig-next) - (setq orig-prev (copy-sequence (window-prev-buffers)) - orig-next (copy-sequence (window-next-buffers)))) - (switch-to-buffer buf 'norecord))))))))) - -(defun consult--buffer-action (buffer &optional norecord) - "Switch to BUFFER via `consult--buffer-display' function. -If NORECORD is non-nil, do not record the buffer switch in the buffer list." - (funcall consult--buffer-display buffer norecord)) - -(consult--define-state buffer) - -(defvar consult--source-bookmark - `(:name "Bookmark" - :narrow ?m - :category bookmark - :face consult-bookmark - :history bookmark-history - :items ,#'bookmark-all-names - :state ,#'consult--bookmark-state) - "Bookmark candidate source for `consult-buffer'.") - -(defvar consult--source-project-buffer - `(:name "Project Buffer" - :narrow ?b - :category buffer - :face consult-buffer - :history buffer-name-history - :state ,#'consult--buffer-state - :enabled ,(lambda () consult-project-function) - :items - ,(lambda () - (when-let (root (consult--project-root)) - (consult--buffer-query :sort 'visibility - :directory root - :as #'consult--buffer-pair)))) - "Project buffer candidate source for `consult-buffer'.") - -(defvar consult--source-project-recent-file - `(:name "Project File" - :narrow ?f - :category file - :face consult-file - :history file-name-history - :state ,#'consult--file-state - :new - ,(lambda (file) - (consult--file-action - (expand-file-name file (consult--project-root)))) - :enabled - ,(lambda () - (and consult-project-function - recentf-mode)) - :items - ,(lambda () - (when-let (root (consult--project-root)) - (let ((len (length root)) - (ht (consult--buffer-file-hash)) - items) - (dolist (file (bound-and-true-p recentf-list) (nreverse items)) - ;; Emacs 29 abbreviates file paths by default, see - ;; `recentf-filename-handlers'. I recommend to set - ;; `recentf-filename-handlers' to nil to avoid any slow down. - (unless (eq (aref file 0) ?/) - (let (file-name-handler-alist) ;; No Tramp slowdown please. - (setq file (expand-file-name file)))) - (when (and (not (gethash file ht)) (string-prefix-p root file)) - (let ((part (substring file len))) - (when (equal part "") (setq part "./")) - (put-text-property 0 1 'multi-category `(file . ,file) part) - (push part items)))))))) - "Project file candidate source for `consult-buffer'.") - -(defvar consult--source-project-buffer-hidden - `(:hidden t :narrow (?p . "Project") ,@consult--source-project-buffer) - "Like `consult--source-project-buffer' but hidden by default.") - -(defvar consult--source-project-recent-file-hidden - `(:hidden t :narrow (?p . "Project") ,@consult--source-project-recent-file) - "Like `consult--source-project-recent-file' but hidden by default.") - -(defvar consult--source-hidden-buffer - `(:name "Hidden Buffer" - :narrow ?\s - :hidden t - :category buffer - :face consult-buffer - :history buffer-name-history - :action ,#'consult--buffer-action - :items - ,(lambda () (consult--buffer-query :sort 'visibility - :filter 'invert - :as #'consult--buffer-pair))) - "Hidden buffer candidate source for `consult-buffer'.") - -(defvar consult--source-modified-buffer - `(:name "Modified Buffer" - :narrow ?* - :hidden t - :category buffer - :face consult-buffer - :history buffer-name-history - :state ,#'consult--buffer-state - :items - ,(lambda () (consult--buffer-query :sort 'visibility - :as #'consult--buffer-pair - :predicate - (lambda (buf) - (and (buffer-modified-p buf) - (buffer-file-name buf)))))) - "Modified buffer candidate source for `consult-buffer'.") - -(defvar consult--source-buffer - `(:name "Buffer" - :narrow ?b - :category buffer - :face consult-buffer - :history buffer-name-history - :state ,#'consult--buffer-state - :default t - :items - ,(lambda () (consult--buffer-query :sort 'visibility - :as #'consult--buffer-pair))) - "Buffer candidate source for `consult-buffer'.") - -(defun consult--file-register-p (reg) - "Return non-nil if REG is a file register." - (memq (car-safe (cdr reg)) '(file-query file))) - -(autoload 'consult-register--candidates "consult-register") -(defvar consult--source-file-register - `(:name "File Register" - :narrow (?r . "Register") - :category file - :state ,#'consult--file-state - :enabled ,(lambda () (seq-some #'consult--file-register-p register-alist)) - :items ,(lambda () (consult-register--candidates #'consult--file-register-p))) - "File register source.") - -(defvar consult--source-recent-file - `(:name "File" - :narrow ?f - :category file - :face consult-file - :history file-name-history - :state ,#'consult--file-state - :new ,#'consult--file-action - :enabled ,(lambda () recentf-mode) - :items - ,(lambda () - (let ((ht (consult--buffer-file-hash)) - items) - (dolist (file (bound-and-true-p recentf-list) (nreverse items)) - ;; Emacs 29 abbreviates file paths by default, see - ;; `recentf-filename-handlers'. I recommend to set - ;; `recentf-filename-handlers' to nil to avoid any slow down. - (unless (eq (aref file 0) ?/) - (let (file-name-handler-alist) ;; No Tramp slowdown please. - (setq file (expand-file-name file)))) - (unless (gethash file ht) - (push (consult--fast-abbreviate-file-name file) items)))))) - "Recent file candidate source for `consult-buffer'.") - -;;;###autoload -(defun consult-buffer (&optional sources) - "Enhanced `switch-to-buffer' command with support for virtual buffers. - -The command supports recent files, bookmarks, views and project files as -virtual buffers. Buffers are previewed. Narrowing to buffers (b), files (f), -bookmarks (m) and project files (p) is supported via the corresponding -keys. In order to determine the project-specific files and buffers, the -`consult-project-function' is used. The virtual buffer SOURCES -default to `consult-buffer-sources'. See `consult--multi' for the -configuration of the virtual buffer sources." - (interactive) - (let ((selected (consult--multi (or sources consult-buffer-sources) - :require-match - (confirm-nonexistent-file-or-buffer) - :prompt "Switch to: " - :history 'consult--buffer-history - :sort nil))) - ;; For non-matching candidates, fall back to buffer creation. - (unless (plist-get (cdr selected) :match) - (consult--buffer-action (car selected))))) - -(defmacro consult--with-project (&rest body) - "Ensure that BODY is executed with a project root." - ;; We have to work quite hard here to ensure that the project root is - ;; only overridden at the current recursion level. When entering a - ;; recursive minibuffer session, we should be able to still switch the - ;; project. But who does that? Working on the first level on project A - ;; and on the second level on project B and on the third level on project C? - ;; You mustn't be afraid to dream a little bigger, darling. - `(let ((consult-project-function - (let ((root (or (consult--project-root t) (user-error "No project found"))) - (depth (recursion-depth)) - (orig consult-project-function)) - (lambda (may-prompt) - (if (= depth (recursion-depth)) - root - (funcall orig may-prompt)))))) - ,@body)) - -;;;###autoload -(defun consult-project-buffer () - "Enhanced `project-switch-to-buffer' command with support for virtual buffers. -The command may prompt you for a project directory if it is invoked from -outside a project. See `consult-buffer' for more details." - (interactive) - (consult--with-project - (consult-buffer consult-project-buffer-sources))) - -;;;###autoload -(defun consult-buffer-other-window () - "Variant of `consult-buffer', switching to a buffer in another window." - (interactive) - (let ((consult--buffer-display #'switch-to-buffer-other-window)) - (consult-buffer))) - -;;;###autoload -(defun consult-buffer-other-frame () - "Variant of `consult-buffer', switching to a buffer in another frame." - (interactive) - (let ((consult--buffer-display #'switch-to-buffer-other-frame)) - (consult-buffer))) - -;;;###autoload -(defun consult-buffer-other-tab () - "Variant of `consult-buffer', switching to a buffer in another tab." - (interactive) - (let ((consult--buffer-display #'switch-to-buffer-other-tab)) - (consult-buffer))) - -;;;;; Command: consult-grep - -(defun consult--grep-format (async builder) - "Return ASYNC function highlighting grep match results. -BUILDER is the command line builder function." - (let (highlight) - (lambda (action) - (cond - ((stringp action) - (setq highlight (cdr (funcall builder action))) - (funcall async action)) - ((consp action) - (let ((file "") (file-len 0) result) - (save-match-data - (dolist (str action) - (when (and (string-match consult--grep-match-regexp str) - ;; Filter out empty context lines - (or (/= (aref str (match-beginning 3)) ?-) - (/= (match-end 0) (length str)))) - ;; We share the file name across candidates to reduce - ;; the amount of allocated memory. - (unless (and (= file-len (- (match-end 1) (match-beginning 1))) - (eq t (compare-strings - file 0 file-len - str (match-beginning 1) (match-end 1) nil))) - (setq file (match-string 1 str) - file-len (length file))) - (let* ((line (match-string 2 str)) - (ctx (= (aref str (match-beginning 3)) ?-)) - (sep (if ctx "-" ":")) - (content (substring str (match-end 0))) - (line-len (length line))) - (when (length> content consult-grep-max-columns) - (setq content (substring content 0 consult-grep-max-columns))) - (when highlight - (funcall highlight content)) - (setq str (concat file sep line sep content)) - ;; Store file name in order to avoid allocations in `consult--prefix-group' - (add-text-properties 0 file-len `(face consult-file consult--prefix-group ,file) str) - (put-text-property (1+ file-len) (+ 1 file-len line-len) 'face 'consult-line-number str) - (when ctx - (add-face-text-property (+ 2 file-len line-len) (length str) 'consult-grep-context 'append str)) - (push str result))))) - (funcall async (nreverse result)))) - (t (funcall async action)))))) - -(defun consult--grep-position (cand &optional find-file) - "Return the grep position marker for CAND. -FIND-FILE is the file open function, defaulting to `find-file-noselect'." - (when cand - (let* ((file-end (next-single-property-change 0 'face cand)) - (line-end (next-single-property-change (1+ file-end) 'face cand)) - (matches (consult--point-placement cand (1+ line-end) 'consult-grep-context)) - (file (substring-no-properties cand 0 file-end)) - (line (string-to-number (substring-no-properties cand (+ 1 file-end) line-end)))) - (when-let (pos (consult--marker-from-line-column - (funcall (or find-file #'find-file-noselect) file) - line (or (car matches) 0))) - (cons pos (cdr matches)))))) - -(defun consult--grep-state () - "Grep state function." - (let ((open (consult--temporary-files)) - (jump (consult--jump-state))) - (lambda (action cand) - (unless cand - (funcall open)) - (funcall jump action (consult--grep-position - cand - (and (not (eq action 'return)) open)))))) - -(defun consult--grep-exclude-args () - "Produce grep exclude arguments. -Take the variables `grep-find-ignored-directories' and -`grep-find-ignored-files' into account." - (unless (boundp 'grep-find-ignored-files) (require 'grep)) - (nconc (mapcar (lambda (s) (concat "--exclude=" s)) - (bound-and-true-p grep-find-ignored-files)) - (mapcar (lambda (s) (concat "--exclude-dir=" s)) - (bound-and-true-p grep-find-ignored-directories)))) - -(defun consult--grep (prompt make-builder dir initial) - "Run asynchronous grep. - -MAKE-BUILDER is the function that returns the command line -builder function. DIR is a directory or a list of file or -directories. PROMPT is the prompt string. INITIAL is initial -input." - (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt prompt dir)) - (default-directory dir) - (builder (funcall make-builder paths))) - (consult--read - (consult--async-command builder - (consult--grep-format builder) - :file-handler t) ;; allow tramp - :prompt prompt - :lookup #'consult--lookup-member - :state (consult--grep-state) - :initial (consult--async-split-initial initial) - :add-history (consult--async-split-thingatpt 'symbol) - :require-match t - :category 'consult-grep - :group #'consult--prefix-group - :history '(:input consult--grep-history) - :sort nil))) - -(defun consult--grep-lookahead-p (&rest cmd) - "Return t if grep CMD supports look-ahead." - (eq 0 (process-file-shell-command - (concat "echo xaxbx | " - (mapconcat #'shell-quote-argument `(,@cmd "^(?=.*b)(?=.*a)") " "))))) - -(defun consult--grep-make-builder (paths) - "Build grep command line and grep across PATHS." - (let* ((cmd (consult--build-args consult-grep-args)) - (type (if (consult--grep-lookahead-p (car cmd) "-P") 'pcre 'extended))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (flags (append cmd opts)) - (ignore-case (or (member "-i" flags) (member "--ignore-case" flags)))) - (if (or (member "-F" flags) (member "--fixed-strings" flags)) - (cons (append cmd (list "-e" arg) opts paths) - (apply-partially #'consult--highlight-regexps - (list (regexp-quote arg)) ignore-case)) - (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg type ignore-case))) - (when re - (cons (append cmd - (list (if (eq type 'pcre) "-P" "-E") ;; perl or extended - "-e" (consult--join-regexps re type)) - opts paths) - hl)))))))) - -;;;###autoload -(defun consult-grep (&optional dir initial) - "Search with `grep' for files in DIR where the content matches a regexp. - -The initial input is given by the INITIAL argument. DIR can be -nil, a directory string or a list of file/directory paths. If -`consult-grep' is called interactively with a prefix argument, -the user can specify the directories or files to search in. -Multiple directories must be separated by comma in the -minibuffer, since they are read via `completing-read-multiple'. -By default the project directory is used if -`consult-project-function' is defined and returns non-nil. -Otherwise the `default-directory' is searched. - -The input string is split, the first part of the string (grep -input) is passed to the asynchronous grep process and the second -part of the string is passed to the completion-style filtering. - -The input string is split at a punctuation character, which is -given as the first character of the input string. The format is -similar to Perl-style regular expressions, e.g., /regexp/. -Furthermore command line options can be passed to grep, specified -behind --. The overall prompt input has the form -`#async-input -- grep-opts#filter-string'. - -Note that the grep input string is transformed from Emacs regular -expressions to Posix regular expressions. Always enter Emacs -regular expressions at the prompt. `consult-grep' behaves like -builtin Emacs search commands, e.g., Isearch, which take Emacs -regular expressions. Furthermore the asynchronous input split -into words, each word must match separately and in any order. -See `consult--regexp-compiler' for the inner workings. In order -to disable transformations of the grep input, adjust -`consult--regexp-compiler' accordingly. - -Here we give a few example inputs: - -#alpha beta : Search for alpha and beta in any order. -#alpha.*beta : Search for alpha before beta. -#\\(alpha\\|beta\\) : Search for alpha or beta (Note Emacs syntax!) -#word -- -C3 : Search for word, include 3 lines as context -#first#second : Search for first, quick filter for second. - -The symbol at point is added to the future history." - (interactive "P") - (consult--grep "Grep" #'consult--grep-make-builder dir initial)) - -;;;;; Command: consult-git-grep - -(defun consult--git-grep-make-builder (paths) - "Create grep command line builder given PATHS." - (let ((cmd (consult--build-args consult-git-grep-args))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (flags (append cmd opts)) - (ignore-case (or (member "-i" flags) (member "--ignore-case" flags)))) - (if (or (member "-F" flags) (member "--fixed-strings" flags)) - (cons (append cmd (list "-e" arg) opts paths) - (apply-partially #'consult--highlight-regexps - (list (regexp-quote arg)) ignore-case)) - (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg 'extended ignore-case))) - (when re - (cons (append cmd - (cdr (mapcan (lambda (x) (list "--and" "-e" x)) re)) - opts paths) - hl)))))))) - -;;;###autoload -(defun consult-git-grep (&optional dir initial) - "Search with `git grep' for files in DIR with INITIAL input. -See `consult-grep' for details." - (interactive "P") - (consult--grep "Git-grep" #'consult--git-grep-make-builder dir initial)) - -;;;;; Command: consult-ripgrep - -(defun consult--ripgrep-make-builder (paths) - "Create ripgrep command line builder given PATHS." - (let* ((cmd (consult--build-args consult-ripgrep-args)) - (type (if (consult--grep-lookahead-p (car cmd) "-P") 'pcre 'extended))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (flags (append cmd opts)) - (ignore-case - (and (not (or (member "-s" flags) (member "--case-sensitive" flags))) - (or (member "-i" flags) (member "--ignore-case" flags) - (and (or (member "-S" flags) (member "--smart-case" flags)) - (let (case-fold-search) - ;; Case insensitive if there are no uppercase letters - (not (string-match-p "[[:upper:]]" arg)))))))) - (if (or (member "-F" flags) (member "--fixed-strings" flags)) - (cons (append cmd (list "-e" arg) opts paths) - (apply-partially #'consult--highlight-regexps - (list (regexp-quote arg)) ignore-case)) - (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg type ignore-case))) - (when re - (cons (append cmd (and (eq type 'pcre) '("-P")) - (list "-e" (consult--join-regexps re type)) - opts paths) - hl)))))))) - -;;;###autoload -(defun consult-ripgrep (&optional dir initial) - "Search with `rg' for files in DIR with INITIAL input. -See `consult-grep' for details." - (interactive "P") - (consult--grep "Ripgrep" #'consult--ripgrep-make-builder dir initial)) - -;;;;; Command: consult-find - -(defun consult--find (prompt builder initial) - "Run find command in current directory. - -The function returns the selected file. -The filename at point is added to the future history. - -BUILDER is the command line builder function. -PROMPT is the prompt. -INITIAL is initial input." - (consult--read - (consult--async-command builder - (consult--async-map (lambda (x) (string-remove-prefix "./" x))) - (consult--async-highlight builder) - :file-handler t) ;; allow tramp - :prompt prompt - :sort nil - :require-match t - :initial (consult--async-split-initial initial) - :add-history (consult--async-split-thingatpt 'filename) - :category 'file - :history '(:input consult--find-history))) - -(defun consult--find-make-builder (paths) - "Build find command line, finding across PATHS." - (let* ((cmd (seq-mapcat (lambda (x) - (if (equal x ".") paths (list x))) - (consult--build-args consult-find-args))) - (type (if (eq 0 (process-file-shell-command - (concat (car cmd) " -regextype emacs -version"))) - 'emacs 'basic))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - ;; ignore-case=t since -iregex is used below - (`(,re . ,hl) (funcall consult--regexp-compiler arg type t))) - (when re - (cons (append cmd - (cdr (mapcan - (lambda (x) - `("-and" "-iregex" - ,(format ".*%s.*" - ;; Replace non-capturing groups with capturing groups. - ;; GNU find does not support non-capturing groups. - (replace-regexp-in-string - "\\\\(\\?:" "\\(" x 'fixedcase 'literal)))) - re)) - opts) - hl)))))) - -;;;###autoload -(defun consult-find (&optional dir initial) - "Search for files with `find' in DIR. -The file names must match the input regexp. INITIAL is the -initial minibuffer input. See `consult-grep' for details -regarding the asynchronous search and the arguments." - (interactive "P") - (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt "Find" dir)) - (default-directory dir) - (builder (consult--find-make-builder paths))) - (find-file (consult--find prompt builder initial)))) - -;;;;; Command: consult-fd - -(defun consult--fd-make-builder (paths) - "Build find command line, finding across PATHS." - (let ((cmd (consult--build-args consult-fd-args))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (flags (append cmd opts)) - (ignore-case - (and (not (or (member "-s" flags) (member "--case-sensitive" flags))) - (or (member "-i" flags) (member "--ignore-case" flags) - (let (case-fold-search) - ;; Case insensitive if there are no uppercase letters - (not (string-match-p "[[:upper:]]" arg))))))) - (if (or (member "-F" flags) (member "--fixed-strings" flags)) - (cons (append cmd (list arg) opts paths) - (apply-partially #'consult--highlight-regexps - (list (regexp-quote arg)) ignore-case)) - (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg 'pcre ignore-case))) - (when re - (cons (append cmd - (cdr (mapcan (lambda (x) `("--and" ,x)) re)) - opts paths) - hl)))))))) - -;;;###autoload -(defun consult-fd (&optional dir initial) - "Search for files with `fd' in DIR. -The file names must match the input regexp. INITIAL is the -initial minibuffer input. See `consult-grep' for details -regarding the asynchronous search and the arguments." - (interactive "P") - (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt "Fd" dir)) - (default-directory dir) - (builder (consult--fd-make-builder paths))) - (find-file (consult--find prompt builder initial)))) - -;;;;; Command: consult-locate - -(defun consult--locate-builder (input) - "Build command line from INPUT." - (pcase-let ((`(,arg . ,opts) (consult--command-split input))) - (unless (string-blank-p arg) - (cons (append (consult--build-args consult-locate-args) - (consult--split-escaped arg) opts) - (cdr (consult--default-regexp-compiler input 'basic t)))))) - -;;;###autoload -(defun consult-locate (&optional initial) - "Search with `locate' for files which match input given INITIAL input. - -The input is treated literally such that locate can take advantage of -the locate database index. Regular expressions would often force a slow -linear search through the entire database. The locate process is started -asynchronously, similar to `consult-grep'. See `consult-grep' for more -details regarding the asynchronous search." - (interactive) - (find-file (consult--find "Locate: " #'consult--locate-builder initial))) - -;;;;; Command: consult-man - -(defun consult--man-builder (input) - "Build command line from INPUT." - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (`(,re . ,hl) (funcall consult--regexp-compiler arg 'extended t))) - (when re - (cons (append (consult--build-args consult-man-args) - (list (consult--join-regexps re 'extended)) - opts) - hl)))) - -(defun consult--man-format (lines) - "Format man candidates from LINES." - (let ((candidates)) - (save-match-data - (dolist (str lines) - (when (string-match "\\`\\(.*?\\([^ ]+\\) *(\\([^,)]+\\)[^)]*).*?\\) +- +\\(.*\\)\\'" str) - (let* ((names (match-string 1 str)) - (name (match-string 2 str)) - (section (match-string 3 str)) - (desc (match-string 4 str)) - (cand (format "%s - %s" names desc))) - (add-text-properties 0 (length names) - (list 'face 'consult-file - 'consult-man (concat section " " name)) - cand) - (push cand candidates))))) - (nreverse candidates))) - -;;;###autoload -(defun consult-man (&optional initial) - "Search for man page given INITIAL input. - -The input string is not preprocessed and passed literally to the -underlying man commands. The man process is started asynchronously, -similar to `consult-grep'. See `consult-grep' for more details regarding -the asynchronous search." - (interactive) - (man (consult--read - (consult--async-command #'consult--man-builder - (consult--async-transform consult--man-format) - (consult--async-highlight #'consult--man-builder)) - :prompt "Manual entry: " - :require-match t - :category 'consult-man - :lookup (apply-partially #'consult--lookup-prop 'consult-man) - :initial (consult--async-split-initial initial) - :add-history (consult--async-split-thingatpt 'symbol) - :history '(:input consult--man-history)))) - -;;;; Preview at point in completions buffers - -(define-minor-mode consult-preview-at-point-mode - "Preview minor mode for *Completions* buffers. -When moving around in the *Completions* buffer, the candidate at point is -automatically previewed." - :group 'consult - (if consult-preview-at-point-mode - (add-hook 'post-command-hook #'consult-preview-at-point nil 'local) - (remove-hook 'post-command-hook #'consult-preview-at-point 'local))) - -(defun consult-preview-at-point () - "Preview candidate at point in *Completions* buffer." - (interactive) - (when-let ((win (active-minibuffer-window)) - (buf (window-buffer win)) - (fun (buffer-local-value 'consult--preview-function buf))) - (funcall fun))) - -;;;; Integration with completion systems - -;;;;; Integration: Default *Completions* - -(defun consult--default-completion-minibuffer-candidate () - "Return current minibuffer candidate from default completion system or Icomplete." - (when (and (minibufferp) - (eq completing-read-function #'completing-read-default)) - (let ((content (minibuffer-contents-no-properties))) - ;; When the current minibuffer content matches a candidate, return it! - (if (test-completion content - minibuffer-completion-table - minibuffer-completion-predicate) - content - ;; Return the full first candidate of the sorted completion list. - (when-let ((completions (completion-all-sorted-completions))) - (concat - (substring content 0 (or (cdr (last completions)) 0)) - (car completions))))))) - -(defun consult--default-completion-list-candidate () - "Return current candidate at point from completions buffer." - (let (beg end) - (when (and - (derived-mode-p 'completion-list-mode) - ;; Logic taken from `choose-completion'. - ;; TODO Upstream a `completion-list-get-candidate' function. - (cond - ((and (not (eobp)) (get-text-property (point) 'mouse-face)) - (setq end (point) beg (1+ (point)))) - ((and (not (bobp)) (get-text-property (1- (point)) 'mouse-face)) - (setq end (1- (point)) beg (point))))) - (setq beg (previous-single-property-change beg 'mouse-face) - end (or (next-single-property-change end 'mouse-face) (point-max))) - (or (get-text-property beg 'completion--string) - (buffer-substring-no-properties beg end))))) - -;;;;; Integration: Vertico - -(defvar vertico--input) -(declare-function vertico--exhibit "ext:vertico") -(declare-function vertico--candidate "ext:vertico") -(declare-function vertico--filter-completions "ext:vertico") - -(defun consult--vertico-candidate () - "Return current candidate for Consult preview." - (and vertico--input (vertico--candidate 'highlight))) - -(defun consult--vertico-refresh () - "Refresh completion UI." - (when vertico--input - (setq vertico--input t) - (vertico--exhibit))) - -(defun consult--vertico-filter-adv (orig pattern cands category highlight) - "Advice for ORIG `consult--completion-filter' function. -See `consult--completion-filter' for arguments PATTERN, CANDS, CATEGORY -and HIGHLIGHT." - (if (and (not highlight) (bound-and-true-p vertico-mode)) - ;; Optimize `consult--completion-filter' using the deferred highlighting - ;; from Vertico. The advice is not necessary - it is a pure optimization. - (nconc (car (vertico--filter-completions pattern cands nil (length pattern) - `(metadata (category . ,category)))) - nil) - (funcall orig pattern cands category highlight))) - -(with-eval-after-load 'vertico - (advice-add #'consult--completion-filter :around #'consult--vertico-filter-adv) - (add-hook 'consult--completion-candidate-hook #'consult--vertico-candidate) - (add-hook 'consult--completion-refresh-hook #'consult--vertico-refresh) - (define-key consult-async-map [remap vertico-insert] 'vertico-next-group)) - -;;;;; Integration: Mct - -(with-eval-after-load 'mct (add-hook 'consult--completion-refresh-hook - 'mct--live-completions-refresh)) - -;;;;; Integration: Icomplete - -(defvar icomplete-mode) -(declare-function icomplete-exhibit "icomplete") - -(defun consult--icomplete-refresh () - "Refresh icomplete view." - (when icomplete-mode - (let ((top (car completion-all-sorted-completions))) - (completion--flush-all-sorted-completions) - ;; force flushing, otherwise narrowing is broken! - (setq completion-all-sorted-completions nil) - (when top - (let* ((completions (completion-all-sorted-completions)) - (last (last completions)) - (before)) ;; completions before top - ;; warning: completions is an improper list - (while (consp completions) - (if (equal (car completions) top) - (progn - (setcdr last (append (nreverse before) (cdr last))) - (setq completion-all-sorted-completions completions - completions nil)) - (push (car completions) before) - (setq completions (cdr completions))))))) - (icomplete-exhibit))) - -(with-eval-after-load 'icomplete - (add-hook 'consult--completion-refresh-hook #'consult--icomplete-refresh)) - -(provide 'consult) -;;; consult.el ends here blob - c007175cd15c22c3ab8409360172611cfd8d5f30 (mode 644) blob + /dev/null Binary files elpa/consult-1.5/consult.info and /dev/null differ blob - 8f854d786568c1933d455c28482e23663ccb4bec (mode 644) blob + /dev/null --- elpa/consult-1.5/dir +++ /dev/null @@ -1,18 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs misc features -* Consult: (consult). Useful commands built on completing-read. blob - 71f0f4851ae3492275b8be424596025b29adcbe5 (mode 644) blob + /dev/null --- elpa/consult-1.5.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2024-04-19T23:05:03+0200 using EDDSA \ No newline at end of file blob - d09cc55381184ddfc50e21b17470cc2c210b8d07 (mode 644) blob + /dev/null --- elpa/consult-eglot-0.2.0/consult-eglot-autoloads.el +++ /dev/null @@ -1,30 +0,0 @@ -;;; consult-eglot-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from consult-eglot.el - -(autoload 'consult-eglot-symbols "consult-eglot" "\ -Interactively select a symbol from the current workspace." t) -(register-definition-prefixes "consult-eglot" '("consult-eglot-")) - -;;; End of scraped data - -(provide 'consult-eglot-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; consult-eglot-autoloads.el ends here blob - 350e8a0c4a124d4ed30994aab3f91e6481c0be6b (mode 644) blob + /dev/null --- elpa/consult-eglot-0.2.0/consult-eglot-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; Generated package description from consult-eglot.el -*- no-byte-compile: t -*- -(define-package "consult-eglot" "0.2.0" "A consulting-read interface for eglot" '((emacs "27.1") (eglot "1.7") (consult "0.16") (project "0.3.0")) :commit "0da8801dd8435160ce1f62ad8066bd52e38f5cbd" :authors '(("mohsin kaleem" . "mohkale@kisara.moe")) :maintainers '(("Mohsin Kaleem")) :maintainer '("Mohsin Kaleem") :keywords '("tools" "completion" "lsp") :url "https://github.com/mohkale/consult-eglot") blob - aea693f01418413fb6cf80a6008a265275caef2b (mode 644) blob + /dev/null --- elpa/consult-eglot-0.2.0/consult-eglot.el +++ /dev/null @@ -1,218 +0,0 @@ -;;; consult-eglot.el --- A consulting-read interface for eglot -*- lexical-binding: t; -*- - -;; Licence: MIT -;; Keywords: tools, completion, lsp -;; Package-Version: 0.2.0 -;; Package-Commit: 0da8801dd8435160ce1f62ad8066bd52e38f5cbd -;; Author: mohsin kaleem -;; Maintainer: Mohsin Kaleem -;; Version: 0.2 -;; Package-Requires: ((emacs "27.1") (eglot "1.7") (consult "0.16") (project "0.3.0")) -;; Homepage: https://github.com/mohkale/consult-eglot - -;; Copyright (c) 2021 Mohsin Kaleem - -;; Permission is hereby granted, free of charge, to any person obtaining a copy -;; of this software and associated documentation files (the "Software"), to deal -;; in the Software without restriction, including without limitation the rights -;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -;; copies of the Software, and to permit persons to whom the Software is -;; furnished to do so, subject to the following conditions: - -;; The above copyright notice and this permission notice shall be included in all -;; copies or substantial portions of the Software. - -;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -;; SOFTWARE. - -;;; Commentary: - -;; Query workspace symbol from eglot using consult. -;; -;; This package provides a single command `consult-eglot-symbols' that uses the -;; lsp workspace/symbol procedure to get a list of symbols exposed in the current -;; workspace. This differs from the default document/symbols call, that eglot -;; exposes through imenu, in that it can present symbols from multiple open files -;; or even files not indirectly loaded by an open file but still used by your -;; project. -;; -;; This code was partially adapted from the excellent consult-lsp package. - -;;; Code: - -(require 'eglot) -(require 'consult) - -(defgroup consult-eglot nil - "Consulting-read for eglot." - :prefix "consult-eglot" - :group 'completion - :group 'eglot - :group 'consult) - -(defcustom consult-eglot-ignore-column nil - "When true `consult-eglot-symbols' only jumps to start of symbols line. -Otherwise `consult-eglot-symbols' will go to the exact symbol of a matched -candidate." - :type 'boolean) - -(defcustom consult-eglot-narrow - '(;; Lowercase classes - (?c . "Class") - (?f . "Function") - (?e . "Enum") - (?i . "Interface") - (?m . "Module") - (?n . "Namespace") - (?p . "Package") - (?s . "Struct") - (?t . "Type Parameter") - (?v . "Variable") - - ;; Uppercase classes - (?A . "Array") - (?B . "Boolean") - (?C . "Constant") - (?E . "Enum Member") - (?F . "Field") - (?M . "Method") - (?N . "Number") - (?O . "Object") - (?P . "Property") - (?S . "String") - - ;; Other. Any which aren't above are taken from here - (?o . "Other")) - "Narrow key configuration used with `consult-eglot-symbols'. -For the format see `consult--read', for the value types see the -values in `eglot--symbol-kind-names'." - :type '(alist :key-type character :value-type string)) - -(defcustom consult-eglot-show-kind-name t - "When true prefix completion candidates with their type." - :type 'boolean) - -(defun consult-eglot--make-async-source (async server) - "Search for symbols in a consult ASYNC source. -Pipe a `consult--read' compatible async-source ASYNC to search for -symbols in the workspace tied to SERVER." - (lambda (action) - (pcase-exhaustive action - ((or 'setup (pred stringp)) - (let ((query (if (stringp action) action ""))) - (jsonrpc-async-request - server :workspace/symbol - `(:query ,query) - :success-fn - (lambda (resp) - (funcall async 'flush) - (funcall async (append resp nil))) - :error-fn - (eglot--lambda ((ResponseError) code message) - (message "%s: %s" code message)) - :timeout-fn - (lambda () - (message "error: request timed out"))) - (funcall async action))) - (_ (funcall async action))))) - -(defun consult-eglot--transformer (symbol-info) - "Default transformer to produce a completion candidate from SYMBOL-INFO. -The produced candidate follows the same form as `consult--grep' however it -contains the SYMBOL-INFO as the second field instead of the file URI." - (eglot--dbind ((SymbolInformation) name kind location) - symbol-info - (eglot--dbind ((Location) uri range) location - (let* ((line (1+ (plist-get (plist-get range :start) :line))) - (kind-name (alist-get kind eglot--symbol-kind-names)) - (uri-path (eglot--uri-to-path uri))) - (propertize - (concat - (when consult-eglot-show-kind-name - (format "%-7s " kind-name)) - name - " " - (string-remove-suffix ":" - (consult--format-location - ;; If the src is relative to our project directory then use - ;; the path from there, otherwise use the absolute file path. - (let ((relative-uri-path (file-relative-name uri-path))) - (if (string-prefix-p ".." relative-uri-path) - (abbreviate-file-name uri-path) - relative-uri-path)) - line))) - 'consult--type (or (car (rassoc kind-name consult-eglot-narrow)) - (car (rassoc "Other" consult-eglot-narrow))) - 'consult--candidate symbol-info))))) - -(defun consult-eglot--symbol-information-to-grep-params (symbol-info) - "Extract grep parameters from SYMBOL-INFO." - (eglot--dbind ((SymbolInformation) location) symbol-info - (eglot--dbind ((Location) uri range) location - (list - (eglot--uri-to-path uri) ; URI - (1+ (plist-get (plist-get range :start) :line)) ; Line number - ; Column Number - (or - (and (not consult-eglot-ignore-column) - (plist-get (plist-get range :start) :character)) - 0))))) - -(defun consult-eglot--state () - "State function for `consult-eglot-symbols' to preview candidates. -This is mostly just a copy-paste of `consult--grep-state' except it doesn't -rely on regexp matching to extract the relevent file and column fields." - (let ((open (consult--temporary-files)) - (jump (consult--jump-state))) - (lambda (action cand) - (when (eq action 'exit) - (funcall open) - (setq open nil)) - (funcall jump - action - (and cand - (pcase-let - ((`(,file ,line ,col) - (consult-eglot--symbol-information-to-grep-params cand))) - (consult--position-marker - (funcall (or open #'find-file) file) - line col))))))) - -;;;###autoload -(defun consult-eglot-symbols () - "Interactively select a symbol from the current workspace." - (interactive) - ;; Set `default-directory' here so we can show file names - ;; relative to the project root. - (let* ((server (eglot--current-server-or-lose)) - (default-directory (or (project-root (eglot--project server)) - default-directory))) - (if (eglot--server-capable :workspaceSymbolProvider) - (progn - (consult--read - (thread-first - (consult--async-sink) - (consult--async-refresh-immediate) - (consult--async-map #'consult-eglot--transformer) - (consult-eglot--make-async-source server) - (consult--async-throttle) - (consult--async-split)) - :history t - :require-match t - :prompt "LSP Symbols: " - :initial (consult--async-split-initial nil) - :category 'consult-lsp-symbols - :lookup #'consult--lookup-candidate - :group (consult--type-group consult-eglot-narrow) - :narrow (consult--type-narrow consult-eglot-narrow) - :state (consult-eglot--state)) - (run-hooks 'consult-after-jump-hook)) - (user-error "Server doesn't support symbol search")))) - -(provide 'consult-eglot) -;;; consult-eglot.el ends here blob - 3a8395268fbea4cad77bd823d1ffc48edca1d3f1 (mode 644) blob + /dev/null --- elpa/corfu-1.3/CHANGELOG.org +++ /dev/null @@ -1,112 +0,0 @@ -#+title: corfu.el - Changelog -#+author: Daniel Mendler -#+language: en - -* Version 1.3 (2024-04-05) - -- Preserve currently selected candidate on further input. This matters if - candidate preview is disabled (~corfu-preview-current=nil~). -- Add new command ~corfu-expand~ bound to ~M-TAB~ by default. The command expands - the input via ~completion-try-completion~, for example the ~basic~ completion - style expands the common prefix of all candidates. - -* Version 1.2 (2024-01-23) - -- Support the EXWM window manager. -- Optimization: Reduce auto completion timer overhead. -- Use ~internal-border-width~ instead of ~child-frame-border-width~. -- Internal refactoring: Do not use buffer-local variables. -- Internal refactoring: Store ~completion-extra-properties~ as part of - ~completion-in-region--data~. - -* Version 1.1 (2023-12-27) - -- Deduplicate candidates with respect to ~equal-including-properties~, such that - backends can provide equal candidate strings, which only differ in their text - properties and annotations. -- Ensure that the string passed to the ~:exit-function~ retains the candidate - properties, when possible. The properties are guaranteed to exist when a - candidate is selected explicitly, but may be missing when candidates are - completed in a stepwise manner. -- ~corfu-on-exact-match~: Add value ~show~ to the customization option. With this - setting the Corfu popup will be shown even if there is only a single matching - candidate. - -* Version 1.0 (2023-12-01) - -- Bug fixes. -- =corfu-quick=: Use a slightly different scheme to support more candidates. -- =corfu-reset=: Quit immediately if input did not change. -- Support =completion-lazy-hilit=. - -* Version 0.38 (2023-08-14) - -- =corfu-quick=: Bugfix. -- =corfu-mode-map=: Add mode map. -- Replace =corfu-excluded-modes= with =global-corfu-modes=, the Emacs 28 convention - for globalized minor modes. - -* Version 0.37 (2023-07-02) - -- Bugfixes. -- Improve child frame display code, =corfu--popup-show= takes a =posn= argument. -- Ensure that the popup font matches the font of the parent frame. -- Close popup when window selection changes. -- Remove =corfu-history-length=. Instead set the =history-length= property of - =corfu-history= variable. -- =corfu-info-documentation=, =corfu-info-location=: Make buffer and window - persistent if called with prefix argument. - -* Version 0.36 (2023-03-27) - -- Drop obsolete =corfu-preselect-first=. -- =corfu-popupinfo-delay= and =corfu-echo-delay=: Remove support for value =t=. - Instant updates are not recommended. It is still possible to use a small value - for the delay. -- Rename =corfu-excluded-modes= to =corfu-exclude-modes= (Naming convention). -- Remove call to =undo-boundary=, which caused issues with auto completion. - -* Version 0.35 (2023-02-17) - -- =corfu-popupinfo=: Take more text into account when computing popup width. -- =corfu-popupinfo=: Change keybindings, remap =corfu-info-documentation/location=. -- =corfu-popupinfo=: Add commands =corfu-popupinfo-beginning/end=. -- =corfu-popupinfo=: Improve popup placement. -- Add =corfu-prompt-beginning= and =corfu-prompt-end= commands. -- Add =corfu-preselect= option, deprecate =corfu-preselect-first=. -- Use =cl-defgeneric= internally as mechanism to allow extensions to override - functionality, e.g., the candidate formatting and display. - -* Version 0.34 (2022-12-03) - -- Bugfixes -- Popup frame code updated for Emacs 29. Please report any issues. -- =corfu-popupinfo-direction=: Variable must be a list of directions. -- Support height adjustments of =corfu-default= face - -* Version 0.33 (2022-11-21) - -- =corfu-popupinfo=: Bugfixes - -* Version 0.31 (2022-11-20) - -- =corfu-echo=, =corfu-quick=: Bugfixes for interaction issue. - -* Version 0.30 (2022-11-19) - -- =corfu-popupinfo=: Bugfixes and improvements. - -* Version 0.29 (2022-11-19) - -- BREAKING: Extract the =corfu-echo= extension from =corfu.el=. In order to see echo - messages, enable =corfu-echo-mode=. You probably want to enable either - =corfu-echo-mode= or =corfu-popupinfo-mode=. -- BREAKING: Rename =corfu-echo-documentation= to =corfu-echo-delay=. -- Add =corfu-popupinfo= extension to display candidate documentation and location - in a small child frame next to the candidate menu. This extension has been - contributed by Yuwei Tian who assigned copyright to the FSF. The extension - supersedes Yuwei's =corfu-doc= package. - -* Version 0.28 (2022-10-16) - -- Start of changelog. blob - a2c968bd4259cc1626c266d076bade3d87dd9027 (mode 644) blob + /dev/null --- elpa/corfu-1.3/README-elpa +++ /dev/null @@ -1,804 +0,0 @@ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - CORFU.EL - COMPLETION IN REGION FUNCTION - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - -Corfu enhances in-buffer completion with a small completion popup. The -current candidates are shown in a popup below or above the point. The -candidates can be selected by moving up and down. Corfu is the -minimalistic in-buffer completion counterpart of the [Vertico] -minibuffer UI. - -Corfu is a small package, which relies on the Emacs completion -facilities and concentrates on providing a polished completion -UI. In-buffer completion UIs in Emacs can hook into -`completion-in-region', which implements the interaction with the -user. Completions at point are either provided by commands like -`dabbrev-completion' or by pluggable backends -(`completion-at-point-functions', Capfs) and are then passed to -`completion-in-region'. Many programming, text and shell major modes -implement a Capf. Corfu does not include its own completion -backends. The Emacs built-in Capfs and the Capfs provided by third-party -programming language packages are often sufficient. Additional Capfs and -completion utilities are provided by the [Cape] package. - -*NOTE*: Corfu uses child frames to show the popup and falls back to the -default setting of the `completion-in-region-function' on non-graphical -displays. If you want to use Corfu in the terminal, install the package -[corfu-terminal], which provides an alternative overlay-based display. - -Table of Contents -───────────────── - -1. Features -2. Installation -3. Key bindings -4. Configuration -.. 1. Auto completion -.. 2. Completing in the minibuffer -.. 3. Completing in the Eshell or Shell -.. 4. Orderless completion -.. 5. TAB-only completion -.. 6. TAB-and-Go completion -.. 7. Expanding to the common candidate prefix with TAB -.. 8. Transfer completion to the minibuffer -5. Extensions -6. Complementary packages -7. Alternatives -8. Debugging Corfu -9. Contributions - - -[Vertico] - -[Cape] - -[corfu-terminal] - - -1 Features -══════════ - - • Timer-based auto-completions (/off/ by default, set `corfu-auto'). - • Popup display with scrollbar indicator and arrow key navigation. - • The popup can be summoned explicitly by pressing `TAB' at any time. - • The current candidate is inserted with `TAB' and selected with - `RET'. - • Candidate sorting by prefix, string length and alphabetically. - • The selected candidate is previewed (configurable via - `corfu-preview-current'). - • The selected candidate is automatically committed on further input - by default. (configurable via `corfu-preview-current'). - • Supports the [Orderless] completion style. The filter string can - contain arbitrary characters, after inserting a space via `M-SPC' - (configurable via `corfu-quit-at-boundary' and `corfu-separator'). - • Lazy completion candidate highlighting for performance. - • Support for candidate annotations (`annotation-function', - `affixation-function'). - • Deprecated candidates are displayed as crossed out. - • Icons can be provided by an external package via margin formatter - functions. - • Rich set of extensions: Quick keys, Index keys, Sorting by history, - Candidate documentation in echo area, popup or separate buffer. - - -[Orderless] - - -2 Installation -══════════════ - - Corfu is available from [GNU ELPA]. You can install it directly via - `M-x package-install RET corfu RET'. After installation, activate the - global minor mode with `M-x global-corfu-mode RET'. Set the variable - `corfu-auto' to t in order to enable auto completion. For manual - completion press `M-TAB' (or `TAB') within a buffer. - - -[GNU ELPA] - - -3 Key bindings -══════════════ - - Corfu uses a transient keymap `corfu-map' which is active while the - popup is shown. The keymap defines the following remappings of - fundamental commands and bindings: - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - Binding/Remapping Corfu command - ────────────────────────────────────────────────────────── - `move-beginning-of-line' `corfu-prompt-beginning' - `move-end-of-line' `corfu-prompt-end' - `beginning-of-buffer' `corfu-first' - `end-of-buffer' `corfu-last' - `scroll-down-command' `corfu-scroll-down' - `scroll-up-command' `corfu-scroll-up' - `next-line', `down', `M-n' `corfu-next' - `previous-line', `up', `M-p' `corfu-previous' - `completion-at-point', `TAB' `corfu-complete' - `M-TAB' `corfu-expand' - `RET' `corfu-insert' - `M-g' `corfu-info-location' - `M-h' `corfu-info-documentation' - `M-SPC' `corfu-insert-separator' - `C-g' `corfu-quit' - `keyboard-escape-quit' `corfu-reset' - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - -4 Configuration -═══════════════ - - In order to configure Corfu and other packages in your init.el, you - may want to use `use-package'. Corfu is flexibly customizable via - `corfu-*' customization variables, such that you can adapt it - precisely to your requirements. However in order to quickly try out - the Corfu completion package, it should be sufficient to activate - `global-corfu-mode'. You can experiment with manual completion for - example in an Elisp buffer or in an Eshell or Shell buffer. For auto - completion, set `corfu-auto' to t before turning on - `global-corfu-mode'. - - Here is an example configuration: - - ┌──── - │ (use-package corfu - │ ;; Optional customizations - │ ;; :custom - │ ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - │ ;; (corfu-auto t) ;; Enable auto completion - │ ;; (corfu-separator ?\s) ;; Orderless field separator - │ ;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary - │ ;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match - │ ;; (corfu-preview-current nil) ;; Disable current candidate preview - │ ;; (corfu-preselect 'prompt) ;; Preselect the prompt - │ ;; (corfu-on-exact-match nil) ;; Configure handling of exact matches - │ ;; (corfu-scroll-margin 5) ;; Use scroll margin - │ - │ ;; Enable Corfu only for certain modes. - │ ;; :hook ((prog-mode . corfu-mode) - │ ;; (shell-mode . corfu-mode) - │ ;; (eshell-mode . corfu-mode)) - │ - │ ;; Recommended: Enable Corfu globally. This is recommended since Dabbrev can - │ ;; be used globally (M-/). See also the customization variable - │ ;; `global-corfu-modes' to exclude certain modes. - │ :init - │ (global-corfu-mode)) - │ - │ ;; A few more useful configurations... - │ (use-package emacs - │ :init - │ ;; TAB cycle if there are only few candidates - │ ;; (setq completion-cycle-threshold 3) - │ - │ ;; Enable indentation+completion using the TAB key. - │ ;; `completion-at-point' is often bound to M-TAB. - │ (setq tab-always-indent 'complete) - │ - │ ;; Emacs 30 and newer: Disable Ispell completion function. As an alternative, - │ ;; try `cape-dict'. - │ (setq text-mode-ispell-word-completion nil) - │ - │ ;; Emacs 28 and newer: Hide commands in M-x which do not apply to the current - │ ;; mode. Corfu commands are hidden, since they are not used via M-x. This - │ ;; setting is useful beyond Corfu. - │ (setq read-extended-command-predicate #'command-completion-default-include-p)) - └──── - - Dabbrev completion is based on `completion-in-region' and can be used - with Corfu. You may want to swap the `dabbrev-completion' with the - `dabbrev-expand' key for easier access, if you prefer completion. Also - take a look at the `cape-dabbrev' completion at point function - provided by my [Cape] package. - - ┌──── - │ ;; Use Dabbrev with Corfu! - │ (use-package dabbrev - │ ;; Swap M-/ and C-M-/ - │ :bind (("M-/" . dabbrev-completion) - │ ("C-M-/" . dabbrev-expand)) - │ :config - │ (add-to-list 'dabbrev-ignored-buffer-regexps "\\` ") - │ ;; Since 29.1, use `dabbrev-ignored-buffer-regexps' on older. - │ (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode) - │ (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode)) - └──── - - If you start to configure the package more deeply, I recommend to give - the Orderless completion style a try for filtering. Orderless - completion is different from the familiar prefix TAB completion. Corfu - can be used with the default completion styles. The use of Orderless - is not a necessity. - - ┌──── - │ ;; Optionally use the `orderless' completion style. - │ (use-package orderless - │ :init - │ ;; Configure a custom style dispatcher (see the Consult wiki) - │ ;; (setq orderless-style-dispatchers '(+orderless-dispatch) - │ ;; orderless-component-separator #'orderless-escapable-split-on-space) - │ (setq completion-styles '(orderless basic) - │ completion-category-defaults nil - │ completion-category-overrides '((file (styles partial-completion))))) - └──── - - The `basic' completion style is specified as fallback in addition to - `orderless' in order to ensure that completion commands which rely on - dynamic completion tables, e.g., `completion-table-dynamic' or - `completion-table-in-turn', work correctly. See `+orderless-dispatch' - in the [Consult wiki] for an advanced Orderless style - dispatcher. Additionally enable `partial-completion' for file path - expansion. `partial-completion' is important for file wildcard - support. Multiple files can be opened at once with `find-file' if you - enter a wildcard. You may also give the `initials' completion style a - try. - - See also the [Corfu Wiki] and the [Cape manual] for additional Capf - configuration tips. For more general documentation read the chapter - about completion in the [Emacs manual]. If you want to create your own - Capfs, you can find documentation about completion in the [Elisp - manual]. - - -[Cape] - -[Consult wiki] - -[Corfu Wiki] - -[Cape manual] - -[Emacs manual] - - -[Elisp manual] - - -4.1 Auto completion -─────────────────── - - Auto completion is disabled by default, but can be enabled by setting - `corfu-auto' to t. Furthermore you may want to configure Corfu to quit - completion eagerly, such that the completion popup stays out of your - way when it appeared unexpectedly. - - ┌──── - │ ;; Enable auto completion and configure quitting - │ (setq corfu-auto t - │ corfu-quit-no-match 'separator) ;; or t - └──── - - I suggest to experiment with the various settings and key bindings to - find a configuration which works for you. There is no one perfect - configuration which fits all. Some people like auto completion, some - like manual completion, some want to cycle with TAB and some with the - arrow keys. - - In case you like auto completion settings, where the completion popup - appears immediately, better use a cheap completion style like `basic', - which performs prefix filtering. In this case Corfu completion should - still be fast in buffers with efficient completion backends. You can - try the following settings in an Elisp buffer or the Emacs scratch - buffer. Note that such settings can slow down Emacs due to the high - load on the Lisp runtime and garbage collector. - - ┌──── - │ (setq-local corfu-auto t - │ corfu-auto-delay 0 ;; TOO SMALL - NOT RECOMMENDED - │ corfu-auto-prefix 1 ;; TOO SMALL - NOT RECOMMENDED - │ completion-styles '(basic)) - └──── - - If you want to combine fast prefix filtering and Orderless filtering - you can still do that by defining a custom Orderless completion style - via `orderless-define-completion-style'. We use a custom style - dispatcher, which enables efficient prefix filtering for input shorter - than 4 characters. Note that such a setup is advanced. Please refer to - the Orderless documentation and source code for further details. - - ┌──── - │ (defun orderless-fast-dispatch (word index total) - │ (and (= index 0) (= total 1) (length< word 4) - │ (cons 'orderless-literal-prefix word)))) - │ - │ (orderless-define-completion-style orderless-fast - │ (orderless-style-dispatchers '(orderless-fast-dispatch)) - │ (orderless-matching-styles '(orderless-literal orderless-regexp))) - │ - │ (setq-local corfu-auto t - │ corfu-auto-delay 0 ;; TOO SMALL - NOT RECOMMENDED - │ corfu-auto-prefix 1 ;; TOO SMALL - NOT RECOMMENDED - │ completion-styles '(orderless-fast basic)) - └──── - - -4.2 Completing in the minibuffer -──────────────────────────────── - - Corfu can be used for completion in the minibuffer, since it relies on - child frames to display the candidates. The Corfu popup can be shown - even if it doesn't fully fit inside the minibuffer. - - By default, `global-corfu-mode' does not activate `corfu-mode' in the - minibuffer, to avoid interference with specialised minibuffer - completion UIs like Vertico or Mct. However you may still want to - enable Corfu completion for commands like `M-:' (`eval-expression') or - `M-!' (`shell-command'), which read from the minibuffer. In order to - detect minibuffers with completion we check if the variable - `completion-at-point-functions' is set locally. - - ┌──── - │ (defun corfu-enable-in-minibuffer () - │ "Enable Corfu in the minibuffer." - │ (when (local-variable-p 'completion-at-point-functions) - │ ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion - │ (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup - │ corfu-popupinfo-delay nil) - │ (corfu-mode 1))) - │ (add-hook 'minibuffer-setup-hook #'corfu-enable-in-minibuffer) - └──── - - This is not recommended, but one can also enable Corfu more generally - for every minibuffer, as long as no completion UI is active. In the - following example we check for Mct and Vertico. Furthermore we ensure - that Corfu is not enabled if a password is read from the minibuffer. - - ┌──── - │ (defun corfu-enable-always-in-minibuffer () - │ "Enable Corfu in the minibuffer if Vertico/Mct are not active." - │ (unless (or (bound-and-true-p mct--active) - │ (bound-and-true-p vertico--input) - │ (eq (current-local-map) read-passwd-map)) - │ ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion - │ (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup - │ corfu-popupinfo-delay nil) - │ (corfu-mode 1))) - │ (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) - └──── - - -4.3 Completing in the Eshell or Shell -───────────────────────────────────── - - When completing in the Eshell I recommend conservative local settings - without auto completion, such that the completion behavior is similar - to widely used shells like Bash, Zsh or Fish. - - ┌──── - │ (add-hook 'eshell-mode-hook - │ (lambda () - │ (setq-local corfu-auto nil) - │ (corfu-mode))) - └──── - - When pressing `RET' while the Corfu popup is visible, the - `corfu-insert' command will be invoked. This command does inserts the - currently selected candidate, but it does not send the prompt input to - Eshell or the Comint process. Therefore you often have to press `RET' - twice which feels like an unnecessary double confirmation. Fortunately - it is easy to improve this! In my configuration I define the advice - `corfu-send-shell' which sends the candidate after insertion. - - ┌──── - │ (defun corfu-send-shell (&rest _) - │ "Send completion candidate when inside comint/eshell." - │ (cond - │ ((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input)) - │ (eshell-send-input)) - │ ((and (derived-mode-p 'comint-mode) (fboundp 'comint-send-input)) - │ (comint-send-input)))) - │ - │ (advice-add #'corfu-insert :after #'corfu-send-shell) - └──── - - Shell completion uses the flexible Pcomplete mechanism internally, - which allows you to program the completions per shell command. If you - want to know more, look into this [blog post], which shows how to - configure Pcomplete for git commands. Since Emacs 29, Pcomplete - offers the `pcomplete-from-help' function which parses the `--help' - output of a command and produces completions for command line options. - - Pcomplete has a few bugs on Emacs 28 and older. We can work around the - issues with the [Cape] library (Completion at point extensions). Cape - provides wrappers which sanitize the Pcomplete function. If you use - Emacs 28 or older installing these advices is recommended such that - Pcomplete works properly. On Emacs 29 the advices should not be - necessary anymore, since most relevant bugs have been fixed. I - therefore recommend to avoid the advices on Emacs 29 and eventually - report any remaining Pcomplete issues upstream. - - ┌──── - │ ;; The advices are only needed on Emacs 28 and older. - │ (when (< emacs-major-version 29) - │ ;; Silence the pcomplete capf. Hide errors or messages. - │ (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) - │ - │ ;; Ensure that pcomplete does not write to the buffer and behaves as a - │ ;; `completion-at-point-function' without side-effects. - │ (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)) - └──── - - -[blog post] - - -[Cape] - - -4.4 Orderless completion -──────────────────────── - - [Orderless] is an advanced completion style that supports - multi-component search filters separated by a configurable character - (space, by default). Normally, entering characters like space which - lie outside the completion region boundaries (words, typically) causes - Corfu to quit. This behavior is helpful with auto-completion, which - may pop-up when not desired, e.g. on entering a new variable - name. Just keep typing and Corfu will get out of the way. - - But orderless search terms can contain arbitrary characters; they are - also interpreted as regular expressions. To use orderless, set - `corfu-separator' (a space, by default) to the primary character of - your orderless component separator. - - Then, when a new orderless component is desired, use `M-SPC' - (`corfu-insert-separator') to enter the first component separator in - the input, and arbitrary orderless search terms and new separators can - be entered thereafter. - - To treat the entire input as Orderless input, you can set the - customization option `corfu-quit-at-boundary' to nil. This disables - the predicate which checks if the current completion boundary has been - left. In contrast, if you always want to quit at the boundary, set - `corfu-quit-at-boundary' to t. By default `corfu-quit-at-boundary' is - set to `separator' which quits at completion boundaries as long as no - separator has been inserted with `corfu-insert-separator'. - - Finally, there exists the user option `corfu-quit-no-match' which is - set to `separator' by default. With this setting Corfu stays alive as - soon as you start advanced filtering with a `corfu-separator' even if - there are no matches, for example due to a typo. As long as no - separator character has been inserted with `corfu-insert-separator', - Corfu will still quit if there are no matches. This ensures that the - Corfu popup goes away quickly if completion is not possible. - - In the following we show two configurations, one which works best with - auto completion and one which may work better with manual completion - if you prefer to always use `SPC' to separate the Orderless - components. - - ┌──── - │ ;; Auto completion example - │ (use-package corfu - │ :custom - │ (corfu-auto t) ;; Enable auto completion - │ ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space - │ :bind - │ ;; Another key binding can be used, such as S-SPC. - │ ;; (:map corfu-map ("M-SPC" . corfu-insert-separator)) - │ :init - │ (global-corfu-mode)) - │ - │ ;; Manual completion example - │ (use-package corfu - │ :custom - │ ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space - │ :bind - │ ;; Configure SPC for separator insertion - │ (:map corfu-map ("SPC" . corfu-insert-separator)) - │ :init - │ (global-corfu-mode)) - └──── - - -[Orderless] - - -4.5 TAB-only completion -─────────────────────── - - By default, Corfu steals both the `RET' and `TAB' keys, when the Corfu - popup is open. This can feel intrusive, in particular in combination - with auto completion. `RET' may accidentally commit an automatically - selected candidate, while you actually wanted to start a new line. As - an alternative we can unbind the `RET' key completely from `corfu-map' - or reserve the `RET' key only in shell modes. - - ┌──── - │ ;; TAB-only configuration - │ (use-package corfu - │ :custom - │ (corfu-auto t) ;; Enable auto completion - │ (corfu-preselect 'directory) ;; Select the first candidate, except for directories - │ - │ ;; Free the RET key for less intrusive behavior. - │ :bind - │ (:map corfu-map - │ ;; Option 1: Unbind RET completely - │ ;;; ("RET" . nil) - │ ;; Option 2: Use RET only in shell modes - │ ("RET" . (menu-item "" nil :filter corfu-insert-shell-filter))) - │ - │ :init - │ (global-corfu-mode)) - │ - │ (defun corfu-insert-shell-filter (&optional _) - │ "Insert completion candidate and send when inside comint/eshell." - │ (when (or (derived-mode-p 'eshell-mode) (derived-mode-p 'comint-mode)) - │ (lambda () - │ (interactive) - │ (corfu-insert) - │ ;; `corfu-send-shell' was defined above - │ (corfu-send-shell)))) - └──── - - -4.6 TAB-and-Go completion -───────────────────────── - - You may be interested in configuring Corfu in TAB-and-Go - style. Pressing TAB moves to the next candidate and further input will - then commit the selection. Note that further input will not expand - snippets or templates, which may not be desired but which leads - overall to a more predictable behavior. In order to force snippet - expansion, confirm a candidate explicitly with `RET'. - - ┌──── - │ (use-package corfu - │ ;; TAB-and-Go customizations - │ :custom - │ (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - │ (corfu-preselect 'prompt) ;; Always preselect the prompt - │ - │ ;; Use TAB for cycling, default is `corfu-complete'. - │ :bind - │ (:map corfu-map - │ ("TAB" . corfu-next) - │ ([tab] . corfu-next) - │ ("S-TAB" . corfu-previous) - │ ([backtab] . corfu-previous)) - │ - │ :init - │ (global-corfu-mode)) - └──── - - -4.7 Expanding to the common candidate prefix with TAB -───────────────────────────────────────────────────── - - If you leave the default configuration of the completion styles, such - that the `basic' completion style is still present, then pressing - `M-TAB' (`corfu-expand') will expand the current input to the common - prefix of all completion candidates. In contrast, `TAB' - (`corfu-complete') behaves differently and expands input to the - currently selected candidate. - - If you use the `orderless' completion style, then expansion works - differently by default. Orderless only expands to single matching - candidates, since due to its multi-component input, there does not - necessarily exist an expansion to a common candidate prefix. However - it is possible to define a separate `tab' completion style. The `tab' - completion style will only take over `TAB' completion (if prefix - expansion is possible), but besides that won't affect Orderless - candidate filtering. - - ┌──── - │ (add-to-list 'completion-styles-alist - │ '(tab completion-basic-try-completion ignore - │ "Completion style which provides TAB completion only.")) - │ (setq completion-styles '(tab orderless basic))) - └──── - - -4.8 Transfer completion to the minibuffer -───────────────────────────────────────── - - Sometimes it is useful to transfer the Corfu completion session to the - minibuffer, since the minibuffer offers richer interaction - features. In particular, [Embark] is available in the minibuffer, such - that you can act on the candidates or export/collect the candidates to - a separate buffer. We could add Corfu support to Embark in the future, - such that export or collect is possible directly from - Corfu. Nevertheless, the ability to transfer the Corfu completion to - the minibuffer is even more powerful, since further completion is - possible. - - The command `corfu-move-to-minibuffer' is defined here in terms of - `consult-completion-in-region', which uses the minibuffer completion - UI via `completing-read'. - - ┌──── - │ (defun corfu-move-to-minibuffer () - │ (interactive) - │ (pcase completion-in-region--data - │ (`(,beg ,end ,table ,pred ,extras) - │ (let ((completion-extra-properties extras) - │ completion-cycle-threshold completion-cycling) - │ (consult-completion-in-region beg end table pred))))) - │ (keymap-set corfu-map "M-m" #'corfu-move-to-minibuffer) - │ (add-to-list 'corfu-continue-commands #'corfu-move-to-minibuffer) - └──── - - -[Embark] - - -5 Extensions -════════════ - - We maintain small extension packages to Corfu in this repository in - the subdirectory [extensions/]. The extensions are installed together - with Corfu if you pull the package from ELPA. The extensions are - inactive by default and can be enabled manually if - desired. Furthermore it is possible to install all of the files - separately, both `corfu.el' and the `corfu-*.el' extensions. Currently - the following extensions come with the Corfu ELPA package: - - • [corfu-echo]: `corfu-echo-mode' displays a brief candidate - documentation in the echo area. - • [corfu-history]: `corfu-history-mode' remembers selected candidates - and sorts the candidates by their history position. - • [corfu-indexed]: `corfu-indexed-mode' allows you to select indexed - candidates with prefix arguments. - • [corfu-info]: Actions to access the candidate location and - documentation. - • [corfu-popupinfo]: Display candidate documentation or source in a - popup next to the candidate menu. - • [corfu-quick]: Commands to select using Avy-style quick keys. - - See the Commentary of those files for configuration details. - - -[extensions/] - -[corfu-echo] - - -[corfu-history] - - -[corfu-indexed] - - -[corfu-info] - - -[corfu-popupinfo] - - -[corfu-quick] - - - -6 Complementary packages -════════════════════════ - - Corfu works well together with all packages providing code completion - via the `completion-at-point-functions'. Many modes and packages - already provide a Capf out of the box. Nevertheless you may want to - look into complementary packages to enhance your setup. - - • [corfu-terminal]: The corfu-terminal package provides an - overlay-based display for Corfu, such that you can use Corfu in - terminal Emacs. - - • [corfu-candidate-overlay]: Shows as-you-type auto-suggestion - candidate overlay with a visual indication of whether there are many - or exactly one candidate available (works only with `corfu-auto' - disabled). - - • [Orderless]: Corfu supports completion styles, including the - advanced `orderless' completion style, where the filter expressions - are separated by spaces or another character (see - `corfu-separator'). - - • [Cape]: Provides additional Capf backends and `completion-in-region' - commands. Among others, the package supplies the file completion - backend `cape-file' and the Dabbrev backend `cape-dabbrev'. Cape - provides the `cape-company-to-capf' adapter to reuse Company - backends in Corfu. - - • [nerd-icons-corfu], [kind-icon]: Icons are supported by Corfu via - external packages. The nerd-icons-corfu package relies on the Nerd - icon font, which is even supported on terminal, while kind-icon uses - SVGs from monochromatic icon sets. - - • [Tempel]: Tiny template/snippet package with templates in Lisp - syntax, which can be used in conjunction with Corfu. - - • [Vertico]: You may also want to look into my Vertico - package. Vertico is the minibuffer completion counterpart of Corfu. - - -[corfu-terminal] - -[corfu-candidate-overlay] - - -[Orderless] - -[Cape] - -[nerd-icons-corfu] - -[kind-icon] - -[Tempel] - -[Vertico] - - -7 Alternatives -══════════════ - - • [Company]: Company is a widely used and mature completion package, - which implements a similar UI as Corfu. While Corfu relies - exclusively on the standard Emacs completion API (Capfs), Company - defines its own API for the backends. Company includes its own - completion backends, following its own API, which are incompatible - with the Emacs completion infrastructure. Company provides an - adapter `company-capf' to handle Capfs as a Company backend. As a - result of this design, Company is a more complex package than - Corfu. Company by default uses overlays for the popup in contrast to - the child frames used by Corfu. Overall both packages work well, but - Company integrates less tightly with Emacs. The `completion-styles' - support is more limited and the `completion-at-point' command and - the `completion-in-region' function do not invoke Company. - - • [consult-completion-in-region]: The Consult package provides the - function `consult-completion-in-region' which can be set as - `completion-in-region-function' such that it handles - `completion-at-point'. The function works by transferring the - in-buffer completion to the minibuffer. In the minibuffer, the - minibuffer completion UI, for example [Vertico] takes over. If you - prefer to perform all your completions in the minibuffer - `consult-completion-in-region' is your best option. - - -[Company] - -[consult-completion-in-region] - -[Vertico] - - -8 Debugging Corfu -═════════════════ - - When you observe an error in the `corfu--post-command' post command - hook, you should install an advice to enforce debugging. This allows - you to obtain a stack trace in order to narrow down the location of - the error. The reason is that post command hooks are automatically - disabled (and not debugged) by Emacs. Otherwise Emacs would become - unusable, given that the hooks are executed after every command. - - ┌──── - │ (setq debug-on-error t) - │ - │ (defun force-debug (func &rest args) - │ (condition-case e - │ (apply func args) - │ ((debug error) (signal (car e) (cdr e))))) - │ - │ (advice-add #'corfu--post-command :around #'force-debug) - └──── - - When Capfs do not yield the expected result you can use - `cape-capf-debug' to add debug messages to a Capf. The Capf will then - produce a completion log in the messages buffer. - - ┌──── - │ (setq completion-at-point-functions (list (cape-capf-debug #'cape-dict))) - └──── - - -9 Contributions -═══════════════ - - Since this package is part of [GNU ELPA] contributions require a - copyright assignment to the FSF. - - -[GNU ELPA] blob - 8dd1326f4197e4b74f5bb1322fd0d60d583eaa1d (mode 644) blob + /dev/null --- elpa/corfu-1.3/README.org +++ /dev/null @@ -1,644 +0,0 @@ -#+title: corfu.el - COmpletion in Region FUnction -#+author: Daniel Mendler -#+language: en -#+export_file_name: corfu.texi -#+texinfo_dir_category: Emacs misc features -#+texinfo_dir_title: Corfu: (corfu). -#+texinfo_dir_desc: COmpletion in Region FUnction - -#+html: GNU Emacs -#+html: GNU ELPA -#+html: GNU-devel ELPA -#+html: MELPA -#+html: MELPA Stable - -Corfu enhances in-buffer completion with a small completion popup. The current -candidates are shown in a popup below or above the point. The candidates can be -selected by moving up and down. Corfu is the minimalistic in-buffer completion -counterpart of the [[https://github.com/minad/vertico][Vertico]] minibuffer UI. - -Corfu is a small package, which relies on the Emacs completion facilities and -concentrates on providing a polished completion UI. In-buffer completion UIs in -Emacs can hook into ~completion-in-region~, which implements the interaction with -the user. Completions at point are either provided by commands like -~dabbrev-completion~ or by pluggable backends (~completion-at-point-functions~, -Capfs) and are then passed to ~completion-in-region~. Many programming, text and -shell major modes implement a Capf. Corfu does not include its own completion -backends. The Emacs built-in Capfs and the Capfs provided by third-party -programming language packages are often sufficient. Additional Capfs and -completion utilities are provided by the [[https://github.com/minad/cape][Cape]] package. - -*NOTE*: Corfu uses child frames to show the popup and falls back to the default -setting of the ~completion-in-region-function~ on non-graphical displays. If you -want to use Corfu in the terminal, install the package [[https://codeberg.org/akib/emacs-corfu-terminal][corfu-terminal]], which -provides an alternative overlay-based display. - -#+html: - -#+html: - -#+html: - -#+html: - -#+toc: headlines 8 - -* Features - -- Timer-based auto-completions (/off/ by default, set ~corfu-auto~). -- Popup display with scrollbar indicator and arrow key navigation. -- The popup can be summoned explicitly by pressing =TAB= at any time. -- The current candidate is inserted with =TAB= and selected with =RET=. -- Candidate sorting by prefix, string length and alphabetically. -- The selected candidate is previewed (configurable via ~corfu-preview-current~). -- The selected candidate is automatically committed on further input by default. - (configurable via ~corfu-preview-current~). -- Supports the [[https://github.com/oantolin/orderless][Orderless]] completion style. The filter string can contain - arbitrary characters, after inserting a space via =M-SPC= (configurable via - ~corfu-quit-at-boundary~ and ~corfu-separator~). -- Lazy completion candidate highlighting for performance. -- Support for candidate annotations (=annotation-function=, =affixation-function=). -- Deprecated candidates are displayed as crossed out. -- Icons can be provided by an external package via margin formatter functions. -- Rich set of extensions: Quick keys, Index keys, Sorting by history, Candidate - documentation in echo area, popup or separate buffer. - -* Installation - -Corfu is available from [[https://elpa.gnu.org/packages/corfu.html][GNU ELPA]]. You can install it directly via =M-x package-install RET corfu RET=. -After installation, activate the global minor mode with =M-x global-corfu-mode RET=. -Set the variable ~corfu-auto~ to t in order to enable auto completion. For manual -completion press =M-TAB= (or =TAB=) within a buffer. - -* Key bindings - -Corfu uses a transient keymap ~corfu-map~ which is active while the popup is -shown. The keymap defines the following remappings of fundamental commands and -bindings: - -| Binding/Remapping | Corfu command | -|--------------------------+--------------------------| -| ~move-beginning-of-line~ | ~corfu-prompt-beginning~ | -| ~move-end-of-line~ | ~corfu-prompt-end~ | -| ~beginning-of-buffer~ | ~corfu-first~ | -| ~end-of-buffer~ | ~corfu-last~ | -| ~scroll-down-command~ | ~corfu-scroll-down~ | -| ~scroll-up-command~ | ~corfu-scroll-up~ | -| ~next-line~, =down=, =M-n= | ~corfu-next~ | -| ~previous-line~, =up=, =M-p= | ~corfu-previous~ | -| ~completion-at-point~, =TAB= | ~corfu-complete~ | -| =M-TAB= | ~corfu-expand~ | -| =RET= | ~corfu-insert~ | -| =M-g= | ~corfu-info-location~ | -| =M-h= | ~corfu-info-documentation~ | -| =M-SPC= | ~corfu-insert-separator~ | -| =C-g= | ~corfu-quit~ | -| ~keyboard-escape-quit~ | ~corfu-reset~ | - -* Configuration - -In order to configure Corfu and other packages in your init.el, you may want to -use ~use-package~. Corfu is flexibly customizable via ~corfu-*~ customization -variables, such that you can adapt it precisely to your requirements. However in -order to quickly try out the Corfu completion package, it should be sufficient -to activate ~global-corfu-mode~. You can experiment with manual completion for -example in an Elisp buffer or in an Eshell or Shell buffer. For auto completion, -set ~corfu-auto~ to t before turning on ~global-corfu-mode~. - -Here is an example configuration: - -#+begin_src emacs-lisp -(use-package corfu - ;; Optional customizations - ;; :custom - ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - ;; (corfu-auto t) ;; Enable auto completion - ;; (corfu-separator ?\s) ;; Orderless field separator - ;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary - ;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match - ;; (corfu-preview-current nil) ;; Disable current candidate preview - ;; (corfu-preselect 'prompt) ;; Preselect the prompt - ;; (corfu-on-exact-match nil) ;; Configure handling of exact matches - ;; (corfu-scroll-margin 5) ;; Use scroll margin - - ;; Enable Corfu only for certain modes. - ;; :hook ((prog-mode . corfu-mode) - ;; (shell-mode . corfu-mode) - ;; (eshell-mode . corfu-mode)) - - ;; Recommended: Enable Corfu globally. This is recommended since Dabbrev can - ;; be used globally (M-/). See also the customization variable - ;; `global-corfu-modes' to exclude certain modes. - :init - (global-corfu-mode)) - -;; A few more useful configurations... -(use-package emacs - :init - ;; TAB cycle if there are only few candidates - ;; (setq completion-cycle-threshold 3) - - ;; Enable indentation+completion using the TAB key. - ;; `completion-at-point' is often bound to M-TAB. - (setq tab-always-indent 'complete) - - ;; Emacs 30 and newer: Disable Ispell completion function. As an alternative, - ;; try `cape-dict'. - (setq text-mode-ispell-word-completion nil) - - ;; Emacs 28 and newer: Hide commands in M-x which do not apply to the current - ;; mode. Corfu commands are hidden, since they are not used via M-x. This - ;; setting is useful beyond Corfu. - (setq read-extended-command-predicate #'command-completion-default-include-p)) -#+end_src - -Dabbrev completion is based on =completion-in-region= and can be used with Corfu. -You may want to swap the =dabbrev-completion= with the =dabbrev-expand= key for -easier access, if you prefer completion. Also take a look at the =cape-dabbrev= -completion at point function provided by my [[https://github.com/minad/cape][Cape]] package. - -#+begin_src emacs-lisp -;; Use Dabbrev with Corfu! -(use-package dabbrev - ;; Swap M-/ and C-M-/ - :bind (("M-/" . dabbrev-completion) - ("C-M-/" . dabbrev-expand)) - :config - (add-to-list 'dabbrev-ignored-buffer-regexps "\\` ") - ;; Since 29.1, use `dabbrev-ignored-buffer-regexps' on older. - (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode) - (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode)) -#+end_src - -If you start to configure the package more deeply, I recommend to give the -Orderless completion style a try for filtering. Orderless completion is -different from the familiar prefix TAB completion. Corfu can be used with the -default completion styles. The use of Orderless is not a necessity. - -#+begin_src emacs-lisp -;; Optionally use the `orderless' completion style. -(use-package orderless - :init - ;; Configure a custom style dispatcher (see the Consult wiki) - ;; (setq orderless-style-dispatchers '(+orderless-dispatch) - ;; orderless-component-separator #'orderless-escapable-split-on-space) - (setq completion-styles '(orderless basic) - completion-category-defaults nil - completion-category-overrides '((file (styles partial-completion))))) -#+end_src - -The =basic= completion style is specified as fallback in addition to =orderless= in -order to ensure that completion commands which rely on dynamic completion -tables, e.g., ~completion-table-dynamic~ or ~completion-table-in-turn~, work -correctly. See =+orderless-dispatch= in the [[https://github.com/minad/consult/wiki][Consult wiki]] for an advanced Orderless -style dispatcher. Additionally enable =partial-completion= for file path -expansion. =partial-completion= is important for file wildcard support. Multiple -files can be opened at once with =find-file= if you enter a wildcard. You may also -give the =initials= completion style a try. - -See also the [[https://github.com/minad/corfu/wiki][Corfu Wiki]] and the [[https://github.com/minad/cape][Cape manual]] for additional Capf configuration -tips. For more general documentation read the chapter about completion in the -[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Completion.html][Emacs manual]]. If you want to create your own Capfs, you can find documentation -about completion in the [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion.html][Elisp manual]]. - -** Auto completion - -Auto completion is disabled by default, but can be enabled by setting ~corfu-auto~ -to t. Furthermore you may want to configure Corfu to quit completion eagerly, -such that the completion popup stays out of your way when it appeared -unexpectedly. - -#+begin_src emacs-lisp -;; Enable auto completion and configure quitting -(setq corfu-auto t - corfu-quit-no-match 'separator) ;; or t -#+end_src - -I suggest to experiment with the various settings and key bindings to find a -configuration which works for you. There is no one perfect configuration which -fits all. Some people like auto completion, some like manual completion, some -want to cycle with TAB and some with the arrow keys. - -In case you like auto completion settings, where the completion popup appears -immediately, better use a cheap completion style like =basic=, which performs -prefix filtering. In this case Corfu completion should still be fast in buffers -with efficient completion backends. You can try the following settings in an -Elisp buffer or the Emacs scratch buffer. Note that such settings can slow down -Emacs due to the high load on the Lisp runtime and garbage collector. - -#+begin_src emacs-lisp -(setq-local corfu-auto t - corfu-auto-delay 0 ;; TOO SMALL - NOT RECOMMENDED - corfu-auto-prefix 1 ;; TOO SMALL - NOT RECOMMENDED - completion-styles '(basic)) -#+end_src - -If you want to combine fast prefix filtering and Orderless filtering you can -still do that by defining a custom Orderless completion style via -=orderless-define-completion-style=. We use a custom style dispatcher, which -enables efficient prefix filtering for input shorter than 4 characters. Note -that such a setup is advanced. Please refer to the Orderless documentation and -source code for further details. - -#+begin_src emacs-lisp -(defun orderless-fast-dispatch (word index total) - (and (= index 0) (= total 1) (length< word 4) - (cons 'orderless-literal-prefix word)))) - -(orderless-define-completion-style orderless-fast - (orderless-style-dispatchers '(orderless-fast-dispatch)) - (orderless-matching-styles '(orderless-literal orderless-regexp))) - -(setq-local corfu-auto t - corfu-auto-delay 0 ;; TOO SMALL - NOT RECOMMENDED - corfu-auto-prefix 1 ;; TOO SMALL - NOT RECOMMENDED - completion-styles '(orderless-fast basic)) -#+end_src - -** Completing in the minibuffer - -Corfu can be used for completion in the minibuffer, since it relies on child -frames to display the candidates. The Corfu popup can be shown even if it -doesn't fully fit inside the minibuffer. - -By default, ~global-corfu-mode~ does not activate ~corfu-mode~ in the minibuffer, to -avoid interference with specialised minibuffer completion UIs like Vertico or -Mct. However you may still want to enable Corfu completion for commands like ~M-:~ -(~eval-expression~) or ~M-!~ (~shell-command~), which read from the minibuffer. In -order to detect minibuffers with completion we check if the variable -~completion-at-point-functions~ is set locally. - -#+begin_src emacs-lisp -(defun corfu-enable-in-minibuffer () - "Enable Corfu in the minibuffer." - (when (local-variable-p 'completion-at-point-functions) - ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion - (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup - corfu-popupinfo-delay nil) - (corfu-mode 1))) -(add-hook 'minibuffer-setup-hook #'corfu-enable-in-minibuffer) -#+end_src - -This is not recommended, but one can also enable Corfu more generally for every -minibuffer, as long as no completion UI is active. In the following example we -check for Mct and Vertico. Furthermore we ensure that Corfu is not enabled if a -password is read from the minibuffer. - -#+begin_src emacs-lisp -(defun corfu-enable-always-in-minibuffer () - "Enable Corfu in the minibuffer if Vertico/Mct are not active." - (unless (or (bound-and-true-p mct--active) - (bound-and-true-p vertico--input) - (eq (current-local-map) read-passwd-map)) - ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion - (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup - corfu-popupinfo-delay nil) - (corfu-mode 1))) -(add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) -#+end_src - -** Completing in the Eshell or Shell - -When completing in the Eshell I recommend conservative local settings without -auto completion, such that the completion behavior is similar to widely used -shells like Bash, Zsh or Fish. - -#+begin_src emacs-lisp -(add-hook 'eshell-mode-hook - (lambda () - (setq-local corfu-auto nil) - (corfu-mode))) -#+end_src - -When pressing =RET= while the Corfu popup is visible, the ~corfu-insert~ command -will be invoked. This command does inserts the currently selected candidate, but -it does not send the prompt input to Eshell or the Comint process. Therefore you -often have to press =RET= twice which feels like an unnecessary double -confirmation. Fortunately it is easy to improve this! In my configuration I -define the advice ~corfu-send-shell~ which sends the candidate after insertion. - -#+begin_src emacs-lisp -(defun corfu-send-shell (&rest _) - "Send completion candidate when inside comint/eshell." - (cond - ((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input)) - (eshell-send-input)) - ((and (derived-mode-p 'comint-mode) (fboundp 'comint-send-input)) - (comint-send-input)))) - -(advice-add #'corfu-insert :after #'corfu-send-shell) -#+end_src - -Shell completion uses the flexible Pcomplete mechanism internally, which allows -you to program the completions per shell command. If you want to know more, look -into this [[https://www.masteringemacs.org/article/pcomplete-context-sensitive-completion-emacs][blog post]], which shows how to configure Pcomplete for git commands. -Since Emacs 29, Pcomplete offers the =pcomplete-from-help= function which parses -the ~--help~ output of a command and produces completions for command line -options. - -Pcomplete has a few bugs on Emacs 28 and older. We can work around the issues -with the [[https://github.com/minad/cape][Cape]] library (Completion at point extensions). Cape provides wrappers -which sanitize the Pcomplete function. If you use Emacs 28 or older installing -these advices is recommended such that Pcomplete works properly. On Emacs 29 the -advices should not be necessary anymore, since most relevant bugs have been -fixed. I therefore recommend to avoid the advices on Emacs 29 and eventually -report any remaining Pcomplete issues upstream. - -#+begin_src emacs-lisp -;; The advices are only needed on Emacs 28 and older. -(when (< emacs-major-version 29) - ;; Silence the pcomplete capf. Hide errors or messages. - (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) - - ;; Ensure that pcomplete does not write to the buffer and behaves as a - ;; `completion-at-point-function' without side-effects. - (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)) -#+end_src - -** Orderless completion - -[[https://github.com/oantolin/orderless][Orderless]] is an advanced completion style that supports multi-component search -filters separated by a configurable character (space, by default). Normally, -entering characters like space which lie outside the completion region -boundaries (words, typically) causes Corfu to quit. This behavior is helpful -with auto-completion, which may pop-up when not desired, e.g. on entering a new -variable name. Just keep typing and Corfu will get out of the way. - -But orderless search terms can contain arbitrary characters; they are also -interpreted as regular expressions. To use orderless, set ~corfu-separator~ (a -space, by default) to the primary character of your orderless component -separator. - -Then, when a new orderless component is desired, use =M-SPC= -(~corfu-insert-separator~) to enter the first component separator in the input, -and arbitrary orderless search terms and new separators can be entered -thereafter. - -To treat the entire input as Orderless input, you can set the customization -option ~corfu-quit-at-boundary~ to nil. This disables the predicate which checks -if the current completion boundary has been left. In contrast, if you always -want to quit at the boundary, set ~corfu-quit-at-boundary~ to t. By default -~corfu-quit-at-boundary~ is set to ~separator~ which quits at completion boundaries -as long as no separator has been inserted with ~corfu-insert-separator~. - -Finally, there exists the user option ~corfu-quit-no-match~ which is set to -=separator= by default. With this setting Corfu stays alive as soon as you start -advanced filtering with a ~corfu-separator~ even if there are no matches, for -example due to a typo. As long as no separator character has been inserted with -~corfu-insert-separator~, Corfu will still quit if there are no matches. This -ensures that the Corfu popup goes away quickly if completion is not possible. - -In the following we show two configurations, one which works best with auto -completion and one which may work better with manual completion if you prefer to -always use =SPC= to separate the Orderless components. - - #+begin_src emacs-lisp -;; Auto completion example -(use-package corfu - :custom - (corfu-auto t) ;; Enable auto completion - ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space - :bind - ;; Another key binding can be used, such as S-SPC. - ;; (:map corfu-map ("M-SPC" . corfu-insert-separator)) - :init - (global-corfu-mode)) - -;; Manual completion example -(use-package corfu - :custom - ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space - :bind - ;; Configure SPC for separator insertion - (:map corfu-map ("SPC" . corfu-insert-separator)) - :init - (global-corfu-mode)) -#+end_src - -** TAB-only completion - -By default, Corfu steals both the ~RET~ and ~TAB~ keys, when the Corfu popup is -open. This can feel intrusive, in particular in combination with auto -completion. ~RET~ may accidentally commit an automatically selected candidate, -while you actually wanted to start a new line. As an alternative we can unbind -the ~RET~ key completely from ~corfu-map~ or reserve the ~RET~ key only in shell -modes. - -#+begin_src emacs-lisp -;; TAB-only configuration -(use-package corfu - :custom - (corfu-auto t) ;; Enable auto completion - (corfu-preselect 'directory) ;; Select the first candidate, except for directories - - ;; Free the RET key for less intrusive behavior. - :bind - (:map corfu-map - ;; Option 1: Unbind RET completely - ;;; ("RET" . nil) - ;; Option 2: Use RET only in shell modes - ("RET" . (menu-item "" nil :filter corfu-insert-shell-filter))) - - :init - (global-corfu-mode)) - -(defun corfu-insert-shell-filter (&optional _) - "Insert completion candidate and send when inside comint/eshell." - (when (or (derived-mode-p 'eshell-mode) (derived-mode-p 'comint-mode)) - (lambda () - (interactive) - (corfu-insert) - ;; `corfu-send-shell' was defined above - (corfu-send-shell)))) -#+end_src - -** TAB-and-Go completion - -You may be interested in configuring Corfu in TAB-and-Go style. Pressing TAB -moves to the next candidate and further input will then commit the selection. -Note that further input will not expand snippets or templates, which may not be -desired but which leads overall to a more predictable behavior. In order to -force snippet expansion, confirm a candidate explicitly with ~RET~. - -#+begin_src emacs-lisp -(use-package corfu - ;; TAB-and-Go customizations - :custom - (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - (corfu-preselect 'prompt) ;; Always preselect the prompt - - ;; Use TAB for cycling, default is `corfu-complete'. - :bind - (:map corfu-map - ("TAB" . corfu-next) - ([tab] . corfu-next) - ("S-TAB" . corfu-previous) - ([backtab] . corfu-previous)) - - :init - (global-corfu-mode)) -#+end_src - -** Expanding to the common candidate prefix with TAB - -If you leave the default configuration of the completion styles, such that the -~basic~ completion style is still present, then pressing ~M-TAB~ (~corfu-expand~) will -expand the current input to the common prefix of all completion candidates. In -contrast, ~TAB~ (~corfu-complete~) behaves differently and expands input to the -currently selected candidate. - -If you use the ~orderless~ completion style, then expansion works differently by -default. Orderless only expands to single matching candidates, since due to its -multi-component input, there does not necessarily exist an expansion to a common -candidate prefix. However it is possible to define a separate ~tab~ completion -style. The ~tab~ completion style will only take over ~TAB~ completion (if prefix -expansion is possible), but besides that won't affect Orderless candidate -filtering. - -#+begin_src emacs-lisp -(add-to-list 'completion-styles-alist - '(tab completion-basic-try-completion ignore - "Completion style which provides TAB completion only.")) -(setq completion-styles '(tab orderless basic))) -#+end_src - -** Transfer completion to the minibuffer - -Sometimes it is useful to transfer the Corfu completion session to the -minibuffer, since the minibuffer offers richer interaction features. In -particular, [[https://github.com/oantolin/embark][Embark]] is available in the minibuffer, such that you can act on the -candidates or export/collect the candidates to a separate buffer. We could add -Corfu support to Embark in the future, such that export or collect is possible -directly from Corfu. Nevertheless, the ability to transfer the Corfu completion -to the minibuffer is even more powerful, since further completion is possible. - -The command ~corfu-move-to-minibuffer~ is defined here in terms of -~consult-completion-in-region~, which uses the minibuffer completion UI via -~completing-read~. - -#+begin_src emacs-lisp -(defun corfu-move-to-minibuffer () - (interactive) - (pcase completion-in-region--data - (`(,beg ,end ,table ,pred ,extras) - (let ((completion-extra-properties extras) - completion-cycle-threshold completion-cycling) - (consult-completion-in-region beg end table pred))))) -(keymap-set corfu-map "M-m" #'corfu-move-to-minibuffer) -(add-to-list 'corfu-continue-commands #'corfu-move-to-minibuffer) -#+end_src - -* Extensions -:properties: -:custom_id: extensions -:end: - -We maintain small extension packages to Corfu in this repository in the -subdirectory [[https://github.com/minad/corfu/tree/main/extensions][extensions/]]. The extensions are installed together with Corfu if -you pull the package from ELPA. The extensions are inactive by default and can -be enabled manually if desired. Furthermore it is possible to install all of the -files separately, both ~corfu.el~ and the ~corfu-*.el~ extensions. Currently the -following extensions come with the Corfu ELPA package: - -- [[https://github.com/minad/corfu/blob/main/extensions/corfu-echo.el][corfu-echo]]: =corfu-echo-mode= displays a brief candidate documentation in the - echo area. -- [[https://github.com/minad/corfu/blob/main/extensions/corfu-history.el][corfu-history]]: =corfu-history-mode= remembers selected candidates and sorts the - candidates by their history position. -- [[https://github.com/minad/corfu/blob/main/extensions/corfu-indexed.el][corfu-indexed]]: =corfu-indexed-mode= allows you to select indexed candidates with - prefix arguments. -- [[https://github.com/minad/corfu/blob/main/extensions/corfu-info.el][corfu-info]]: Actions to access the candidate location and documentation. -- [[https://github.com/minad/corfu/blob/main/extensions/corfu-popupinfo.el][corfu-popupinfo]]: Display candidate documentation or source in a popup next to - the candidate menu. -- [[https://github.com/minad/corfu/blob/main/extensions/corfu-quick.el][corfu-quick]]: Commands to select using Avy-style quick keys. - -See the Commentary of those files for configuration details. - -* Complementary packages - -Corfu works well together with all packages providing code completion via the -~completion-at-point-functions~. Many modes and packages already provide a Capf -out of the box. Nevertheless you may want to look into complementary packages to -enhance your setup. - -- [[https://codeberg.org/akib/emacs-corfu-terminal][corfu-terminal]]: The corfu-terminal package provides an overlay-based display - for Corfu, such that you can use Corfu in terminal Emacs. - -- [[https://code.bsdgeek.org/adam/corfu-candidate-overlay][corfu-candidate-overlay]]: Shows as-you-type auto-suggestion candidate overlay - with a visual indication of whether there are many or exactly one candidate - available (works only with =corfu-auto= disabled). - -- [[https://github.com/oantolin/orderless][Orderless]]: Corfu supports completion styles, including the advanced =orderless= - completion style, where the filter expressions are separated by spaces or - another character (see ~corfu-separator~). - -- [[https://github.com/minad/cape][Cape]]: Provides additional Capf backends and =completion-in-region= commands. - Among others, the package supplies the file completion backend =cape-file= and - the Dabbrev backend =cape-dabbrev=. Cape provides the ~cape-company-to-capf~ - adapter to reuse Company backends in Corfu. - -- [[https://github.com/LuigiPiucco/nerd-icons-corfu][nerd-icons-corfu]], [[https://github.com/jdtsmith/kind-icon][kind-icon]]: Icons are supported by Corfu via external - packages. The nerd-icons-corfu package relies on the Nerd icon font, which is - even supported on terminal, while kind-icon uses SVGs from monochromatic icon - sets. - -- [[https://github.com/minad/tempel][Tempel]]: Tiny template/snippet package with templates in Lisp syntax, which - can be used in conjunction with Corfu. - -- [[https://github.com/minad/vertico][Vertico]]: You may also want to look into my Vertico package. Vertico is the - minibuffer completion counterpart of Corfu. - -* Alternatives - -- [[https://github.com/company-mode/company-mode][Company]]: Company is a widely used and mature completion package, which - implements a similar UI as Corfu. While Corfu relies exclusively on the - standard Emacs completion API (Capfs), Company defines its own API for the - backends. Company includes its own completion backends, following its own API, - which are incompatible with the Emacs completion infrastructure. Company - provides an adapter ~company-capf~ to handle Capfs as a Company backend. As a - result of this design, Company is a more complex package than Corfu. Company - by default uses overlays for the popup in contrast to the child frames used by - Corfu. Overall both packages work well, but Company integrates less tightly - with Emacs. The ~completion-styles~ support is more limited and the - ~completion-at-point~ command and the ~completion-in-region~ function do not - invoke Company. - -- [[https://github.com/minad/consult][consult-completion-in-region]]: The Consult package provides the function - ~consult-completion-in-region~ which can be set as ~completion-in-region-function~ - such that it handles ~completion-at-point~. The function works by transferring - the in-buffer completion to the minibuffer. In the minibuffer, the minibuffer - completion UI, for example [[https://github.com/minad/vertico][Vertico]] takes over. If you prefer to perform all - your completions in the minibuffer ~consult-completion-in-region~ is your best - option. - -* Debugging Corfu - -When you observe an error in the =corfu--post-command= post command hook, you -should install an advice to enforce debugging. This allows you to obtain a stack -trace in order to narrow down the location of the error. The reason is that post -command hooks are automatically disabled (and not debugged) by Emacs. Otherwise -Emacs would become unusable, given that the hooks are executed after every -command. - -#+begin_src emacs-lisp -(setq debug-on-error t) - -(defun force-debug (func &rest args) - (condition-case e - (apply func args) - ((debug error) (signal (car e) (cdr e))))) - -(advice-add #'corfu--post-command :around #'force-debug) -#+end_src - -When Capfs do not yield the expected result you can use ~cape-capf-debug~ to add -debug messages to a Capf. The Capf will then produce a completion log in the -messages buffer. - -#+begin_src emacs-lisp -(setq completion-at-point-functions (list (cape-capf-debug #'cape-dict))) -#+end_src - -* Contributions - -Since this package is part of [[https://elpa.gnu.org/packages/corfu.html][GNU ELPA]] contributions require a copyright -assignment to the FSF. blob - 18364b5651ce5eea64da2ab9651cb8e99c25363f (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu-autoloads.el +++ /dev/null @@ -1,214 +0,0 @@ -;;; corfu-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from corfu.el - -(autoload 'corfu-mode "corfu" "\ -COmpletion in Region FUnction. - -This is a minor mode. If called interactively, toggle the `Corfu - mode' mode. If the prefix argument is positive, enable the - mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable - the mode if ARG is nil, omitted, or is a positive number. - Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, - evaluate `corfu-mode'. - -The mode's hook is called both when the mode is enabled and when - it is disabled. - -(fn &optional ARG)" t) -(put 'global-corfu-mode 'globalized-minor-mode t) -(defvar global-corfu-mode nil "\ -Non-nil if Global Corfu mode is enabled. -See the `global-corfu-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `global-corfu-mode'.") -(custom-autoload 'global-corfu-mode "corfu" nil) -(autoload 'global-corfu-mode "corfu" "\ -Toggle Corfu mode in all buffers. -With prefix ARG, enable Global Corfu mode if ARG is positive; -otherwise, disable it. - -If called from Lisp, toggle the mode if ARG is `toggle'. -Enable the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -Corfu mode is enabled in all buffers where `corfu--on' would do it. - -See `corfu-mode' for more information on Corfu mode. - -(fn &optional ARG)" t) -(register-definition-prefixes "corfu" '("corfu-" "global-corfu-modes")) - - -;;; Generated autoloads from corfu-echo.el - -(defvar corfu-echo-mode nil "\ -Non-nil if Corfu-Echo mode is enabled. -See the `corfu-echo-mode' command -for a description of this minor mode.") -(custom-autoload 'corfu-echo-mode "corfu-echo" nil) -(autoload 'corfu-echo-mode "corfu-echo" "\ -Show candidate documentation in echo area. - -This is a global minor mode. If called interactively, toggle the - `Corfu-Echo mode' mode. If the prefix argument is positive, - enable the mode, and if it is zero or negative, disable the - mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable - the mode if ARG is nil, omitted, or is a positive number. - Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, - evaluate `(default-value \\='corfu-echo-mode)'. - -The mode's hook is called both when the mode is enabled and when - it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "corfu-echo" '("corfu-echo-")) - - -;;; Generated autoloads from corfu-history.el - -(defvar corfu-history-mode nil "\ -Non-nil if Corfu-History mode is enabled. -See the `corfu-history-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `corfu-history-mode'.") -(custom-autoload 'corfu-history-mode "corfu-history" nil) -(autoload 'corfu-history-mode "corfu-history" "\ -Update Corfu history and sort completions by history. - -This is a global minor mode. If called interactively, toggle the - `Corfu-History mode' mode. If the prefix argument is positive, - enable the mode, and if it is zero or negative, disable the - mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable - the mode if ARG is nil, omitted, or is a positive number. - Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, - evaluate `(default-value \\='corfu-history-mode)'. - -The mode's hook is called both when the mode is enabled and when - it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "corfu-history" '("corfu-history")) - - -;;; Generated autoloads from corfu-indexed.el - -(defvar corfu-indexed-mode nil "\ -Non-nil if Corfu-Indexed mode is enabled. -See the `corfu-indexed-mode' command -for a description of this minor mode.") -(custom-autoload 'corfu-indexed-mode "corfu-indexed" nil) -(autoload 'corfu-indexed-mode "corfu-indexed" "\ -Prefix candidates with indices. - -This is a global minor mode. If called interactively, toggle the - `Corfu-Indexed mode' mode. If the prefix argument is positive, - enable the mode, and if it is zero or negative, disable the - mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable - the mode if ARG is nil, omitted, or is a positive number. - Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, - evaluate `(default-value \\='corfu-indexed-mode)'. - -The mode's hook is called both when the mode is enabled and when - it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "corfu-indexed" '("corfu-indexed-")) - - -;;; Generated autoloads from corfu-info.el - -(autoload 'corfu-info-documentation "corfu-info" "\ -Show documentation of current candidate. -If called with a prefix ARG, the buffer is persistent. - -(fn &optional ARG)" t) -(autoload 'corfu-info-location "corfu-info" "\ -Show location of current candidate. -If called with a prefix ARG, the buffer is persistent. - -(fn &optional ARG)" t) -(register-definition-prefixes "corfu-info" '("corfu-info--")) - - -;;; Generated autoloads from corfu-popupinfo.el - -(defvar corfu-popupinfo-mode nil "\ -Non-nil if Corfu-Popupinfo mode is enabled. -See the `corfu-popupinfo-mode' command -for a description of this minor mode.") -(custom-autoload 'corfu-popupinfo-mode "corfu-popupinfo" nil) -(autoload 'corfu-popupinfo-mode "corfu-popupinfo" "\ -Corfu info popup minor mode. - -This is a global minor mode. If called interactively, toggle the - `Corfu-Popupinfo mode' mode. If the prefix argument is - positive, enable the mode, and if it is zero or negative, - disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable - the mode if ARG is nil, omitted, or is a positive number. - Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, - evaluate `(default-value \\='corfu-popupinfo-mode)'. - -The mode's hook is called both when the mode is enabled and when - it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "corfu-popupinfo" '("corfu-popupinfo-")) - - -;;; Generated autoloads from corfu-quick.el - -(autoload 'corfu-quick-jump "corfu-quick" "\ -Jump to candidate using quick keys." t) -(autoload 'corfu-quick-insert "corfu-quick" "\ -Insert candidate using quick keys." t) -(autoload 'corfu-quick-complete "corfu-quick" "\ -Complete candidate using quick keys." t) -(register-definition-prefixes "corfu-quick" '("corfu-quick")) - -;;; End of scraped data - -(provide 'corfu-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; corfu-autoloads.el ends here blob - 405fa60b7c6a7e8e5c7e051f8f14b0e6fef55bf0 (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu-echo.el +++ /dev/null @@ -1,109 +0,0 @@ -;;; corfu-echo.el --- Show candidate documentation in echo area -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; Author: Daniel Mendler -;; Maintainer: Daniel Mendler -;; Created: 2022 -;; Version: 1.3 -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4") (corfu "1.3")) -;; Homepage: https://github.com/minad/corfu - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Show candidate documentation in echo area. Enable `corfu-echo-mode'. - -;;; Code: - -(require 'corfu) -(eval-when-compile - (require 'subr-x)) - -(defface corfu-echo - '((t :inherit completions-annotations)) - "Face used for echo area messages." - :group 'corfu-faces) - -(defcustom corfu-echo-delay '(2.0 . 1.0) - "Show documentation string in the echo area after that number of seconds. -The value can be a pair of two floats to specify initial and -subsequent delay." - :type '(choice (const :tag "Never" nil) - (number :tag "Delay in seconds") - (cons :tag "Two Delays" - (choice :tag "Initial " number) - (choice :tag "Subsequent" number))) - :group 'corfu) - -(defvar corfu-echo--timer nil - "Echo area message timer.") - -(defvar corfu-echo--message nil - "Last echo message.") - -(defun corfu-echo--cancel (&optional msg) - "Cancel echo timer and refresh MSG." - (when corfu-echo--timer - (cancel-timer corfu-echo--timer) - (setq corfu-echo--timer nil)) - (corfu-echo--show msg) - (unless corfu-echo--message - (setq corfu-echo--timer nil - corfu-echo--message nil))) - -(defun corfu-echo--show (msg) - "Show MSG in echo area." - (when (or msg corfu-echo--message) - (setq msg (or msg "") - corfu-echo--message msg) - (corfu--message "%s" (if (text-property-not-all 0 (length msg) 'face nil msg) - msg - (propertize msg 'face 'corfu-echo))))) - -;;;###autoload -(define-minor-mode corfu-echo-mode - "Show candidate documentation in echo area." - :global t :group 'corfu) - -(cl-defmethod corfu--exhibit :after (&context (corfu-echo-mode (eql t)) &optional _auto) - (if-let ((delay (if (consp corfu-echo-delay) - (funcall (if corfu-echo--message #'cdr #'car) - corfu-echo-delay) - corfu-echo-delay)) - (extra (nth 4 completion-in-region--data)) - (fun (plist-get extra :company-docsig)) - (cand (and (>= corfu--index 0) - (nth corfu--index corfu--candidates)))) - (if (<= delay 0) - (corfu-echo--show (funcall fun cand)) - (corfu-echo--cancel) - (setq corfu-echo--timer - (run-at-time delay nil - (lambda () - (corfu-echo--show (funcall fun cand)))))) - (corfu-echo--cancel))) - -(cl-defmethod corfu--teardown :before (_buf &context (corfu-echo-mode (eql t))) - (corfu-echo--cancel)) - -(cl-defmethod corfu--prepare :before (&context (corfu-echo-mode (eql t))) - ;; The refreshing is needed to prevent flicker if corfu-echo-delay=t. - (corfu-echo--cancel corfu-echo--message)) - -(provide 'corfu-echo) -;;; corfu-echo.el ends here blob - 7059b6864e4b5fb1a2ace1618beba0014f15795e (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu-history.el +++ /dev/null @@ -1,92 +0,0 @@ -;;; corfu-history.el --- Sorting by history for Corfu -*- lexical-binding: t -*- - -;; Copyright (C) 2022-2024 Free Software Foundation, Inc. - -;; Author: Daniel Mendler -;; Maintainer: Daniel Mendler -;; Created: 2022 -;; Version: 1.3 -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4") (corfu "1.3")) -;; Homepage: https://github.com/minad/corfu - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Enable `corfu-history-mode' to sort candidates by their history -;; position. Maintain a list of recently selected candidates. In order -;; to save the history across Emacs sessions, enable `savehist-mode' and -;; add `corfu-history' to `savehist-additional-variables'. -;; -;; (corfu-history-mode 1) -;; (savehist-mode 1) -;; (add-to-list 'savehist-additional-variables 'corfu-history) - -;;; Code: - -(require 'corfu) -(eval-when-compile - (require 'cl-lib)) - -(defvar corfu-history nil - "History of Corfu candidates. -The maximum length is determined by the variable `history-length' -or the property `history-length' of `corfu-history'.") - -(defvar corfu-history--hash nil - "Hash table of Corfu candidates.") - -(defun corfu-history--sort-predicate (x y) - "Sorting predicate which compares X and Y." - (or (< (cdr x) (cdr y)) - (and (= (cdr x) (cdr y)) - (corfu--length-string< (car x) (car y))))) - -(defun corfu-history--sort (cands) - "Sort CANDS by history." - (unless corfu-history--hash - (setq corfu-history--hash (make-hash-table :test #'equal :size (length corfu-history))) - (cl-loop for elem in corfu-history for index from 0 do - (unless (gethash elem corfu-history--hash) - (puthash elem index corfu-history--hash)))) - ;; Decorate each candidate with (index<<13) + length. This way we sort first by index and then by - ;; length. We assume that the candidates are shorter than 2**13 characters and that the history is - ;; shorter than 2**16 entries. - (cl-loop for cand on cands do - (setcar cand (cons (car cand) - (+ (ash (gethash (car cand) corfu-history--hash #xFFFF) 13) - (length (car cand)))))) - (setq cands (sort cands #'corfu-history--sort-predicate)) - (cl-loop for cand on cands do (setcar cand (caar cand))) - cands) - -;;;###autoload -(define-minor-mode corfu-history-mode - "Update Corfu history and sort completions by history." - :global t :group 'corfu - (if corfu-history-mode - (add-function :override corfu-sort-function #'corfu-history--sort) - (remove-function corfu-sort-function #'corfu-history--sort))) - -(cl-defmethod corfu--insert :before (_status &context (corfu-history-mode (eql t))) - (when (>= corfu--index 0) - (add-to-history 'corfu-history - (substring-no-properties - (nth corfu--index corfu--candidates))) - (setq corfu-history--hash nil))) - -(provide 'corfu-history) -;;; corfu-history.el ends here blob - b26b4da6cfe9af7c2a5f603baee5797c513881b3 (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu-indexed.el +++ /dev/null @@ -1,97 +0,0 @@ -;;; corfu-indexed.el --- Select indexed candidates -*- lexical-binding: t -*- - -;; Copyright (C) 2022-2024 Free Software Foundation, Inc. - -;; Author: Luis Henriquez-Perez , Daniel Mendler -;; Maintainer: Daniel Mendler -;; Created: 2022 -;; Version: 1.3 -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4") (corfu "1.3")) -;; Homepage: https://github.com/minad/corfu - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This package is a Corfu extension, which prefixes candidates with indices if -;; enabled via `corfu-indexed-mode'. It allows you to select candidates with -;; prefix arguments. This is designed to be a faster alternative to selecting a -;; candidate with `corfu-next' and `corfu-previous'. - -;;; Code: - -(require 'corfu) -(eval-when-compile - (require 'cl-lib)) - -(defface corfu-indexed - '((default :height 0.75) - (((class color) (min-colors 88) (background dark)) - :foreground "#f4f4f4" :background "#323232") - (((class color) (min-colors 88) (background light)) - :foreground "#404148" :background "#d7d7d7") - (t :background "black")) - "Face used for the candidate index prefix." - :group 'corfu-faces) - -(defcustom corfu-indexed-start 0 - "Start of the indexing." - :group 'corfu - :type 'natnum) - -(defvar corfu-indexed--commands - '(corfu-insert corfu-complete) - "Commands that should be indexed.") - -;;;###autoload -(define-minor-mode corfu-indexed-mode - "Prefix candidates with indices." - :global t :group 'corfu) - -(cl-defmethod corfu--prepare :before (&context (corfu-indexed-mode (eql t))) - (when (and prefix-arg (memq this-command corfu-indexed--commands)) - (let ((index (+ corfu--scroll - (- (prefix-numeric-value prefix-arg) - corfu-indexed-start)))) - (if (and (>= index 0) - (< index corfu--total) - (< index (+ corfu--scroll corfu-count))) - (setq corfu--index index) - (message "Out of range") - (setq this-command #'ignore))))) - -(cl-defmethod corfu--affixate :around (cands &context (corfu-indexed-mode (eql t))) - (setq cands (cdr (cl-call-next-method cands))) - (let* ((space #(" " 0 1 (face (:height 0.5 :inherit corfu-indexed)))) - (width (if (length> cands (- 10 corfu-indexed-start)) 2 1)) - (fmt (concat space - (propertize (format "%%%ds" width) - 'face 'corfu-indexed) - space)) - (align - (propertize (make-string width ?\s) - 'display - `(space :align-to (+ left ,(1+ width)))))) - (cl-loop for cand in cands for index from corfu-indexed-start do - (setf (cadr cand) - (concat - (propertize " " 'display (format fmt index)) - align - (cadr cand)))) - (cons t cands))) - -(provide 'corfu-indexed) -;;; corfu-indexed.el ends here blob - a765655feb781a8ea2cb54b78ff4f56b0b098cfe (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu-info.el +++ /dev/null @@ -1,118 +0,0 @@ -;;; corfu-info.el --- Show candidate information in separate buffer -*- lexical-binding: t -*- - -;; Copyright (C) 2022-2024 Free Software Foundation, Inc. - -;; Author: Daniel Mendler -;; Maintainer: Daniel Mendler -;; Created: 2022 -;; Version: 1.3 -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4") (corfu "1.3")) -;; Homepage: https://github.com/minad/corfu - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This Corfu extension provides commands to show additional information to the -;; candidates in a separate buffer. The commands `corfu-info-location' and -;; `corfu-info-documentation' are bound by default in the `corfu-map' to M-g and -;; M-h respectively. - -;;; Code: - -(require 'corfu) -(eval-when-compile - (require 'subr-x)) - -(defun corfu-info--restore-on-next-command () - "Restore window configuration before next command." - (let ((config (current-window-configuration)) - (other other-window-scroll-buffer) - (restore (make-symbol "corfu--restore"))) - (fset restore - (lambda () - (setq other-window-scroll-buffer other) - (unless (memq this-command '(scroll-other-window scroll-other-window-down)) - (when (memq this-command '(corfu-quit corfu-reset)) - (setq this-command #'ignore)) - (remove-hook 'pre-command-hook restore) - (set-window-configuration config)))) - (add-hook 'pre-command-hook restore))) - -(defun corfu-info--display-buffer (buffer name) - "Display BUFFER and return window displaying the buffer. -Make the buffer persistent with NAME if non-nil." - (if name - (unless (buffer-local-value 'buffer-file-name buffer) - (if-let ((old (get-buffer name))) - (setq buffer (prog1 old (kill-buffer buffer))) - (with-current-buffer buffer - (rename-buffer name)))) - (corfu-info--restore-on-next-command)) - (setq other-window-scroll-buffer buffer) - (display-buffer buffer t)) - -;;;###autoload -(defun corfu-info-documentation (&optional arg) - "Show documentation of current candidate. -If called with a prefix ARG, the buffer is persistent." - (interactive "P") - ;; Company support, taken from `company.el', see `company-show-doc-buffer'. - (when (< corfu--index 0) - (user-error "No candidate selected")) - (let ((cand (nth corfu--index corfu--candidates))) - (if-let ((extra (nth 4 completion-in-region--data)) - (fun (plist-get extra :company-doc-buffer)) - (res (funcall fun cand))) - (set-window-start (corfu-info--display-buffer - (get-buffer (or (car-safe res) res)) - (and arg (format "*corfu doc: %s*" cand))) - (or (cdr-safe res) (point-min))) - (user-error "No documentation available for `%s'" cand)))) - -;;;###autoload -(defun corfu-info-location (&optional arg) - "Show location of current candidate. -If called with a prefix ARG, the buffer is persistent." - (interactive "P") - ;; Company support, taken from `company.el', see `company-show-location'. - (when (< corfu--index 0) - (user-error "No candidate selected")) - (let ((cand (nth corfu--index corfu--candidates))) - (if-let ((extra (nth 4 completion-in-region--data)) - (fun (plist-get extra :company-location)) - ;; BUG: company-location may throw errors if location is not found - (loc (ignore-errors (funcall fun cand)))) - (with-selected-window - (corfu-info--display-buffer - (or (and (bufferp (car loc)) (car loc)) - (find-file-noselect (car loc) t)) - (and arg (format "*corfu loc: %s*" cand))) - (without-restriction - (goto-char (point-min)) - (when-let ((pos (cdr loc))) - (if (bufferp (car loc)) - (goto-char pos) - (forward-line (1- pos)))) - (set-window-start nil (point)))) - (user-error "No location available for `%s'" cand)))) - -;; Emacs 28: Do not show Corfu commands with M-X -(put #'corfu-info-location 'completion-predicate #'ignore) -(put #'corfu-info-documentation 'completion-predicate #'ignore) - -(provide 'corfu-info) -;;; corfu-info.el ends here blob - f80a8940c23886ca39fa039014a8005bd9cf664b (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from corfu.el -*- no-byte-compile: t -*- -(define-package "corfu" "1.3" "COmpletion in Region FUnction" '((emacs "27.1") (compat "29.1.4.4")) :commit "6088f0550dc8f10f5bcf6f24d35ce24159b01b43" :authors '(("Daniel Mendler" . "mail@daniel-mendler.de")) :maintainer '("Daniel Mendler" . "mail@daniel-mendler.de") :keywords '("abbrev" "convenience" "matching" "completion" "text") :url "https://github.com/minad/corfu") blob - 8442259348c55a4d5908df6cf779cba4036b7649 (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu-popupinfo.el +++ /dev/null @@ -1,523 +0,0 @@ -;;; corfu-popupinfo.el --- Candidate information popup for Corfu -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; Author: Yuwei Tian , Daniel Mendler -;; Maintainer: Daniel Mendler -;; Created: 2022 -;; Version: 1.3 -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4") (corfu "1.3")) -;; Homepage: https://github.com/minad/corfu - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Display an information popup for completion candidate when using -;; Corfu. The popup displays either the candidate documentation or the -;; candidate location. The `corfu-popupinfo-mode' must be enabled -;; globally. Set `corfu-popupinfo-delay' to nil if the info popup should -;; not update automatically. If the popup should not appear initially, -;; but update automatically afterwards, use `(setq corfu-popupinfo-delay -;; (cons nil 1.0))'. - -;; For manual toggling the commands `corfu-popupinfo-toggle', -;; `corfu-popupinfo-location' and `corfu-popupinfo-documentation' are -;; bound in the `corfu-popupinfo-map'. - -;;; Code: - -(require 'corfu) -(eval-when-compile - (require 'cl-lib) - (require 'subr-x)) - -(defface corfu-popupinfo - '((t :inherit corfu-default)) - "Face used for the info popup." - :group 'corfu-faces) - -(defcustom corfu-popupinfo-delay '(2.0 . 1.0) - "Automatically update info popup after that number of seconds. - -The value can be a pair of two floats to specify initial and -subsequent delay. If the value is non-nil or the car of the pair -is non-nil, the popup will automatically appear for the -preselected candidate. Otherwise the popup can be requested -manually via `corfu-popupinfo-toggle', -`corfu-popupinfo-documentation' and `corfu-popupinfo-location'. - -It is *not recommended* to use a short delay or even 0, since -this will create high load for Emacs. Retrieving the -documentation from the backend is usually expensive." - :type '(choice (const :tag "Never" nil) - (number :tag "Delay in seconds") - (cons :tag "Two Delays" - (choice :tag "Initial " - (choice (const nil) number)) - (choice :tag "Subsequent" - (choice (const nil) number)))) - :group 'corfu) - -(defcustom corfu-popupinfo-hide t - "Hide the popup during the transition between candidates." - :type 'boolean - :group 'corfu) - -(defcustom corfu-popupinfo-max-width 80 - "The maximum width of the info popup in characters." - :type 'natnum - :group 'corfu) - -(defcustom corfu-popupinfo-min-width 30 - "The minimum width of the info popup in characters." - :type 'natnum - :group 'corfu) - -(defcustom corfu-popupinfo-max-height 10 - "The maximum height of the info popup in characters." - :type 'natnum - :group 'corfu) - -(defcustom corfu-popupinfo-min-height 1 - "The minimum height of the info popup in characters." - :type 'natnum - :group 'corfu) - -(defcustom corfu-popupinfo-resize t - "Resize the info popup automatically if non-nil." - :type 'boolean - :group 'corfu) - -(defcustom corfu-popupinfo-direction '(right left vertical) - "Preferred directions for the popup in order." - :type '(repeat - (choice - (const left) - (const right) - (const vertical) - (const force-left) - (const force-right) - (const force-vertical))) - :group 'corfu) - -(defvar-keymap corfu-popupinfo-map - :doc "Additional keymap activated in popupinfo mode." - "M-t" #'corfu-popupinfo-toggle - " " #'corfu-popupinfo-documentation - " " #'corfu-popupinfo-location - " " #'corfu-popupinfo-scroll-up - " " #'corfu-popupinfo-scroll-down - " " #'corfu-popupinfo-end - " " #'corfu-popupinfo-beginning) - -(defvar corfu-popupinfo--buffer-parameters - '((truncate-partial-width-windows . nil) - (truncate-lines . nil) - (left-margin-width . 1) - (right-margin-width . 1) - (word-wrap . t) - (fringe-indicator-alist (continuation))) - "Buffer parameters.") - -(defvar corfu-popupinfo--frame nil - "Info popup child frame.") - -(defvar corfu-popupinfo--timer nil - "Corfu info popup auto display timer.") - -(defvar corfu-popupinfo--toggle 'init - "Toggle state.") - -(defvar corfu-popupinfo--function - #'corfu-popupinfo--get-documentation - "Function called to obtain documentation string.") - -(defvar corfu-popupinfo--candidate nil - "Completion candidate for the info popup.") - -(defvar corfu-popupinfo--coordinates nil - "Coordinates of the candidate popup. -The coordinates list has the form (LEFT TOP RIGHT BOTTOM) where -all values are in pixels relative to the origin. See -`frame-edges' for details.") - -(defvar corfu-popupinfo--lock-dir nil - "Locked position direction of the info popup.") - -(defconst corfu-popupinfo--initial-state - (mapcar - (lambda (k) (cons k (symbol-value k))) - '(corfu-popupinfo--candidate - corfu-popupinfo--coordinates - corfu-popupinfo--lock-dir - corfu-popupinfo--toggle - corfu-popupinfo--function)) - "Initial state of `corfu-popupinfo-mode'.") - -(defun corfu-popupinfo--visible-p (&optional frame) - "Return non-nil if FRAME is visible." - (setq frame (or frame corfu-popupinfo--frame)) - (and (frame-live-p frame) (frame-visible-p frame))) - -(defun corfu-popupinfo--get-location (candidate) - "Get source at location of CANDIDATE." - (save-excursion - (let ((old-buffers (buffer-list)) (buffer nil)) - (unwind-protect - (when-let - ((extra (nth 4 completion-in-region--data)) - (fun (plist-get extra :company-location)) - ;; BUG: company-location may throw errors if location is not found - (loc (ignore-errors (funcall fun candidate))) - ((setq buffer - (or (and (bufferp (car loc)) (car loc)) - (get-file-buffer (car loc)) - (let ((inhibit-message t) - (message-log-max nil) - (inhibit-redisplay t) - (enable-dir-local-variables nil) - (enable-local-variables :safe) - (non-essential t) - (delay-mode-hooks t) - (find-file-hook '(global-font-lock-mode-check-buffers))) - (find-file-noselect (car loc) t)))))) - (with-current-buffer buffer - (save-excursion - (without-restriction - (goto-char (point-min)) - (when-let ((pos (cdr loc))) - (if (bufferp (car loc)) - (goto-char pos) - (forward-line (1- pos)))) - (let ((beg (point))) - ;; Support a little bit of scrolling. - (forward-line (* 10 corfu-popupinfo-max-height)) - (when jit-lock-mode - (jit-lock-fontify-now beg (point))) - (let ((res (buffer-substring beg (point)))) - (and (not (string-blank-p res)) res))))))) - (when (and buffer (not (memq buffer old-buffers))) - (kill-buffer buffer)))))) - -(defun corfu-popupinfo--get-documentation (candidate) - "Get the documentation for CANDIDATE." - (when-let ((extra (nth 4 completion-in-region--data)) - (fun (plist-get extra :company-doc-buffer)) - (res (save-excursion - (let ((inhibit-message t) - (message-log-max nil) - (inhibit-redisplay t) - ;; Reduce print length for elisp backend (#249) - (print-level 3) - (print-length (* corfu-popupinfo-max-width - corfu-popupinfo-max-height))) - (funcall fun candidate))))) - (with-current-buffer (or (car-safe res) res) - (setq res (string-trim - (replace-regexp-in-string - "[\n\t ]*\\[back\\][\n\t ]*" "" - (buffer-string)))) - (and (not (string-blank-p res)) res)))) - -(defun corfu-popupinfo--size () - "Return popup size as pair." - (let* ((cw (default-font-width)) - (lh (default-line-height)) - (margin - (* cw (+ (alist-get 'left-margin-width corfu-popupinfo--buffer-parameters) - (alist-get 'right-margin-width corfu-popupinfo--buffer-parameters)))) - (max-height (* lh corfu-popupinfo-max-height)) - (max-width (* cw corfu-popupinfo-max-width))) - (or (when corfu-popupinfo-resize - (with-current-buffer " *corfu-popupinfo*" - (cl-letf* (((window-dedicated-p) nil) - ((window-buffer) (current-buffer)) - (size (window-text-pixel-size - nil (point-min) (point-max) - ;; Use 3*max-height as y-limit, to take more text - ;; into account. - max-width (* 3 max-height)))) - ;; Check that width is not exceeded. Otherwise use full height, - ;; since lines will get wrapped. - (when (<= (car size) max-width) - (cons (+ margin (car size)) - ;; XXX HACK: Ensure that popup has at least a height of 1, - ;; which is the minimum frame height (#261). Maybe we - ;; should ask upstream how smaller frames can be created. - ;; I only managed to create smaller frames by setting - ;; `window-safe-min-height' to 0, which feels problematic. - (min (max (cdr size) lh) max-height)))))) - (cons (+ margin max-width) max-height)))) - -(defun corfu-popupinfo--frame-geometry (frame) - "Return position and size geometric attributes of FRAME. - -The geometry represents the position and size in pixels -in the form of (X Y WIDTH HEIGHT)." - (pcase-let ((`(,x . ,y) (frame-position frame))) - (list x y (frame-pixel-width frame) (frame-pixel-height frame)))) - -(defun corfu-popupinfo--fits-p (size area) - "Check if SIZE fits into the AREA. - -SIZE is in the form (WIDTH . HEIGHT). -AREA is in the form (X Y WIDTH HEIGHT DIR)." - (and (>= (nth 2 area) (car size)) (>= (nth 3 area) (cdr size)))) - -(defun corfu-popupinfo--larger-p (area1 area2) - "Check if AREA1 is larger than AREA2. - -AREA1 and AREA2 are both in the form (X Y WIDTH HEIGHT DIR)." - (>= (* (nth 2 area1) (nth 3 area1)) (* (nth 2 area2) (nth 3 area2)))) - -(defun corfu-popupinfo--area (ps) - "Calculate the display area for the info popup. - -PS is the pixel size of the popup. The calculated area is in the -form (X Y WIDTH HEIGHT DIR)." - (pcase-let* - ((cw (default-font-width)) - (lh (default-line-height)) - (border (alist-get 'internal-border-width corfu--frame-parameters)) - (`(,_pfx ,_pfy ,pfw ,pfh) - (corfu-popupinfo--frame-geometry (frame-parent corfu--frame))) - (`(,cfx ,cfy ,cfw ,cfh) (corfu-popupinfo--frame-geometry corfu--frame)) - ;; Candidates popup below input - (below (>= cfy (+ lh (cadr (window-inside-pixel-edges)) - (window-tab-line-height) - (or (cdr (posn-x-y (posn-at-point (point)))) 0)))) - ;; Popups aligned at top - (top-aligned (or below (< (cdr ps) cfh))) - ;; Left display area - (ahy (if top-aligned - cfy - (max 0 (- (+ cfy cfh) border border (cdr ps))))) - (ahh (if top-aligned - (min (- pfh cfy) (cdr ps)) - (min (- (+ cfy cfh) border border) (cdr ps)))) - (al (list (max 0 (- cfx (car ps) border)) ahy - (min (- cfx border) (car ps)) ahh 'left)) - ;; Right display area - (arx (+ cfx cfw (- border))) - (ar (list arx ahy (min (- pfw arx border border) (car ps)) ahh 'right)) - ;; Vertical display area - (avw (min (car ps) (- pfw cfx border border))) - (av (if below - (list cfx (+ cfy cfh (- border)) avw (min (- pfh cfy cfh border) (cdr ps)) 'vertical) - (let ((h (min (- cfy border border) (cdr ps)))) - (list cfx (max 0 (- cfy h border)) avw h 'vertical))))) - (unless (and corfu-popupinfo--lock-dir - (corfu-popupinfo--fits-p - (cons (* cw corfu-popupinfo-min-width) (* lh corfu-popupinfo-min-height)) - (pcase corfu-popupinfo--lock-dir ('left al) ('right ar) ('vertical av)))) - (setq corfu-popupinfo--lock-dir nil)) - (or - (cl-loop for dir in corfu-popupinfo-direction thereis - (pcase dir - ((or 'force-right (guard (eq corfu-popupinfo--lock-dir 'right))) ar) - ((or 'force-left (guard (eq corfu-popupinfo--lock-dir 'left))) al) - ((or 'force-vertical (guard (eq corfu-popupinfo--lock-dir 'vertical))) av) - ((and 'right (guard (corfu-popupinfo--fits-p ps ar))) ar) - ((and 'left (guard (corfu-popupinfo--fits-p ps al))) al) - ((and 'vertical (guard (corfu-popupinfo--fits-p ps av))) av))) - (let ((ah (if (corfu-popupinfo--larger-p ar al) ar al))) - (if (corfu-popupinfo--larger-p av ah) av ah))))) - -(defun corfu-popupinfo--show (candidate) - "Show the info popup for CANDIDATE." - (when corfu-popupinfo--timer - (cancel-timer corfu-popupinfo--timer) - (setq corfu-popupinfo--timer nil)) - (when (and (corfu-popupinfo--visible-p corfu--frame)) - (let* ((cand-changed - (not (and (corfu-popupinfo--visible-p) - (equal candidate corfu-popupinfo--candidate)))) - (new-coords (frame-edges corfu--frame 'inner-edges)) - (coords-changed (not (equal new-coords corfu-popupinfo--coordinates)))) - (when cand-changed - (if-let ((content (funcall corfu-popupinfo--function candidate))) - (with-current-buffer (corfu--make-buffer " *corfu-popupinfo*") - (with-silent-modifications - (erase-buffer) - (insert content) - (goto-char (point-min))) - (dolist (var corfu-popupinfo--buffer-parameters) - (set (make-local-variable (car var)) (cdr var))) - (when-let ((m (memq 'corfu-default (alist-get 'default face-remapping-alist)))) - (setcar m 'corfu-popupinfo))) - (corfu-popupinfo--hide) - (setq cand-changed nil coords-changed nil))) - (when (or cand-changed coords-changed) - (pcase-let* ((border (alist-get 'internal-border-width corfu--frame-parameters)) - (`(,area-x ,area-y ,area-w ,area-h ,area-d) - (corfu-popupinfo--area - (if cand-changed - (corfu-popupinfo--size) - (cons - (- (frame-pixel-width corfu-popupinfo--frame) border border) - (- (frame-pixel-height corfu-popupinfo--frame) border border))))) - (margin-quirk (not corfu-popupinfo--frame))) - (setq corfu-popupinfo--frame - (corfu--make-frame corfu-popupinfo--frame - area-x area-y area-w area-h - " *corfu-popupinfo*") - corfu-popupinfo--toggle t - corfu-popupinfo--lock-dir area-d - corfu-popupinfo--candidate candidate - corfu-popupinfo--coordinates new-coords) - ;; XXX HACK: Force margin update. For some reason, the call to - ;; `set-window-buffer' in `corfu--make-frame' is not effective the - ;; first time. Why does Emacs have all these quirks? - (when margin-quirk - (set-window-buffer - (frame-root-window corfu-popupinfo--frame) - " *corfu-popupinfo*"))))))) - -(defun corfu-popupinfo--hide () - "Clear the info popup buffer content and hide it." - (corfu--hide-frame corfu-popupinfo--frame)) - -(defun corfu-popupinfo-end (&optional n) - "Scroll text of info popup window to its end. - -If arg N is omitted or nil, scroll to end. If a numerical value, -put point N/10 of the way from the end. If the info popup is not -visible, the other window is moved to beginning or end." - (interactive "P") - (if (corfu-popupinfo--visible-p) - (with-selected-frame corfu-popupinfo--frame - (with-current-buffer " *corfu-popupinfo*" - (with-no-warnings - (end-of-buffer n)))) - (end-of-buffer-other-window n))) - -(defun corfu-popupinfo-beginning (&optional n) - "Scroll text of info popup window to beginning of buffer. - -See `corfu-popupinfo-end' for the argument N." - (interactive "P") - (corfu-popupinfo-end (- 10 (if (numberp n) n 0)))) - -(defun corfu-popupinfo-scroll-up (&optional n) - "Scroll text of info popup window upward N lines. - -If ARG is omitted or nil, scroll upward by a near full screen. -See `scroll-up' for details. If the info popup is not visible, -the other window is scrolled." - (interactive "p") - (if (corfu-popupinfo--visible-p) - (with-selected-frame corfu-popupinfo--frame - (with-current-buffer " *corfu-popupinfo*" - (scroll-up n))) - (scroll-other-window n))) - -(defun corfu-popupinfo-scroll-down (&optional n) - "Scroll text of info popup window down N lines. - -See `corfu-popupinfo-scroll-up' for more details." - (interactive "p") - (corfu-popupinfo-scroll-up (- (or n 1)))) - -(defun corfu-popupinfo--toggle (fun) - "Set documentation getter FUN and toggle popup." - (when (< corfu--index 0) - (corfu-popupinfo--hide) - (user-error "No candidate selected")) - (setq corfu-popupinfo--toggle - (not (and (corfu-popupinfo--visible-p) - (eq corfu-popupinfo--function fun)))) - (if (not corfu-popupinfo--toggle) - (corfu-popupinfo--hide) - (setq corfu-popupinfo--function fun - corfu-popupinfo--candidate nil) - (let ((cand (nth corfu--index corfu--candidates))) - (corfu-popupinfo--show cand) - (unless (corfu-popupinfo--visible-p) - (user-error "No %s available for `%s'" - (car (last (split-string (symbol-name fun) "-+"))) - cand))))) - -(defun corfu-popupinfo-documentation () - "Show or hide documentation in popup. -Behaves like `corfu-popupinfo-toggle'." - (interactive) - (corfu-popupinfo--toggle #'corfu-popupinfo--get-documentation)) - -(defun corfu-popupinfo-location () - "Show or hide location in popup. -Behaves like `corfu-popupinfo-toggle'." - (interactive) - (corfu-popupinfo--toggle #'corfu-popupinfo--get-location)) - -(defun corfu-popupinfo-toggle () - "Toggle the info popup display or hide. - -When using this command to manually hide the info popup, it will -not be displayed until this command is called again, even if -`corfu-popupinfo-delay' is non-nil." - (interactive) - (corfu-popupinfo--toggle corfu-popupinfo--function)) - -;;;###autoload -(define-minor-mode corfu-popupinfo-mode - "Corfu info popup minor mode." - :global t :group 'corfu) - -(cl-defmethod corfu--exhibit :after (&context (corfu-popupinfo-mode (eql t)) &optional _auto) - (when completion-in-region-mode - (setf (alist-get #'corfu-popupinfo-mode minor-mode-overriding-map-alist) - corfu-popupinfo-map) - (when corfu-popupinfo--timer - (cancel-timer corfu-popupinfo--timer) - (setq corfu-popupinfo--timer nil)) - (if (and (>= corfu--index 0) (corfu-popupinfo--visible-p corfu--frame)) - (let ((cand (nth corfu--index corfu--candidates))) - (if-let ((delay (if (consp corfu-popupinfo-delay) - (funcall (if (eq corfu-popupinfo--toggle 'init) #'car #'cdr) - corfu-popupinfo-delay) - corfu-popupinfo-delay)) - (corfu-popupinfo--toggle)) - (if (or (<= delay 0) - (and (equal cand corfu-popupinfo--candidate) - (corfu-popupinfo--visible-p))) - (corfu-popupinfo--show cand) - (when (corfu-popupinfo--visible-p) - (cond - (corfu-popupinfo-hide - (corfu-popupinfo--hide)) - (corfu-popupinfo--candidate - (corfu-popupinfo--show corfu-popupinfo--candidate)))) - (setq corfu-popupinfo--timer - (run-at-time delay nil #'corfu-popupinfo--show cand))) - (unless (equal cand corfu-popupinfo--candidate) - (corfu-popupinfo--hide)))) - (corfu-popupinfo--hide)))) - -(cl-defmethod corfu--teardown :before (_buf &context (corfu-popupinfo-mode (eql t))) - (corfu-popupinfo--hide) - (cl-loop for (k . v) in corfu-popupinfo--initial-state do (set k v)) - (cl-callf2 assq-delete-all #'corfu-popupinfo-mode minor-mode-overriding-map-alist)) - -;; Emacs 28: Do not show Corfu commands with M-X -(dolist (sym '(corfu-popupinfo-scroll-down corfu-popupinfo-scroll-up - corfu-popupinfo-documentation corfu-popupinfo-location - corfu-popupinfo-beginning corfu-popupinfo-end - corfu-popupinfo-toggle)) - (put sym 'completion-predicate #'ignore)) - -(provide 'corfu-popupinfo) -;;; corfu-popupinfo.el ends here blob - 35f2ed2f6df828350fc047f6edfe52f872cb919d (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu-quick.el +++ /dev/null @@ -1,154 +0,0 @@ -;;; corfu-quick.el --- Quick keys for Corfu -*- lexical-binding: t -*- - -;; Copyright (C) 2022-2024 Free Software Foundation, Inc. - -;; Author: Luis Henriquez-Perez , Daniel Mendler -;; Maintainer: Daniel Mendler -;; Created: 2022 -;; Version: 1.3 -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4") (corfu "1.3")) -;; Homepage: https://github.com/minad/corfu - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This package is a Corfu extension, which prefixes candidates with -;; quick keys. Typing these quick keys allows you to select the -;; candidate in front of them. This is designed to be a faster -;; alternative to selecting a candidate with `corfu-next' and -;; `corfu-previous'. -;; (keymap-set corfu-map "M-q" #'corfu-quick-complete) -;; (keymap-set corfu-map "C-q" #'corfu-quick-insert) - -;;; Code: - -(require 'corfu) -(eval-when-compile - (require 'cl-lib)) - -(defcustom corfu-quick1 "asdfgh" - "First level quick keys." - :type 'string - :group 'corfu) - -(defcustom corfu-quick2 "jkluionm" - "Second level quick keys." - :type 'string - :group 'corfu) - -(defface corfu-quick1 - '((((class color) (min-colors 88) (background dark)) - :background "#0050af" :foreground "white" :inherit bold) - (((class color) (min-colors 88) (background light)) - :background "#7feaff" :foreground "black" :inherit bold) - (t :background "blue" :foreground "white" :inherit bold)) - "Face used for the first quick key." - :group 'corfu-faces) - -(defface corfu-quick2 - '((((class color) (min-colors 88) (background dark)) - :background "#7f1f7f" :foreground "white" :inherit bold) - (((class color) (min-colors 88) (background light)) - :background "#ffaaff" :foreground "black" :inherit bold) - (t :background "magenta" :foreground "white" :inherit bold)) - "Face used for the second quick key." - :group 'corfu-faces) - -(defun corfu-quick--keys (two idx) ;; See vertico-quick--keys - "Format quick keys prefix. -IDX is the current candidate index. -TWO is non-nil if two keys should be displayed." - (let ((fst (length corfu-quick1)) - (snd (length corfu-quick2))) - (if (>= idx fst) - (let ((first (elt corfu-quick2 (mod (/ (- idx fst) fst) snd))) - (second (elt corfu-quick1 (mod (- idx fst) fst)))) - (cond - ((eq first two) - (list - (concat " " (propertize (char-to-string second) 'face 'corfu-quick1)) - (cons second (+ corfu--scroll idx)))) - (two - (list " ")) - (t - (list - (concat (propertize (char-to-string first) 'face 'corfu-quick1) - (propertize (char-to-string second) 'face 'corfu-quick2)) - (cons first (list first)))))) - (let ((first (elt corfu-quick1 (mod idx fst)))) - (if two - (list " ") - (list - (concat (propertize (char-to-string first) 'face 'corfu-quick1) " ") - (cons first (+ corfu--scroll idx)))))))) - -(defun corfu-quick--read (&optional first) - "Read quick key given FIRST pressed key." - (cl-letf* ((list nil) - (space1 (propertize " " 'display - `(space :width - (+ 0.5 (,(alist-get - 'child-frame-border-width - corfu--frame-parameters)))))) - (space2 #(" " 0 1 (display (space :width 0.5)))) - (orig (symbol-function #'corfu--affixate)) - ((symbol-function #'corfu--affixate) - (lambda (cands) - (setq cands (cdr (funcall orig cands))) - (cl-loop for cand in cands for index from 0 do - (pcase-let ((`(,keys . ,events) (corfu-quick--keys first index))) - (setq list (nconc events list)) - (setf (cadr cand) (concat space1 (propertize " " 'display keys) space2)))) - (cons t cands))) - ;; Increase minimum width to avoid odd jumping - (corfu-min-width (+ 3 corfu-min-width))) - (corfu--candidates-popup - (posn-at-point (+ (car completion-in-region--data) (length corfu--base)))) - (alist-get (read-key) list))) - -;;;###autoload -(defun corfu-quick-jump () - "Jump to candidate using quick keys." - (interactive) - (when (fboundp 'corfu-echo--cancel) - (corfu-echo--cancel)) - (if (= corfu--total 0) - (and (message "No match") nil) - (let ((idx (corfu-quick--read))) - (when (consp idx) (setq idx (corfu-quick--read (car idx)))) - (when idx (setq corfu--index idx))))) - -;;;###autoload -(defun corfu-quick-insert () - "Insert candidate using quick keys." - (interactive) - (when (corfu-quick-jump) - (corfu-insert))) - -;;;###autoload -(defun corfu-quick-complete () - "Complete candidate using quick keys." - (interactive) - (when (corfu-quick-jump) - (corfu-complete))) - -;; Emacs 28: Do not show Corfu commands in M-X -(dolist (sym '(corfu-quick-jump corfu-quick-insert corfu-quick-complete)) - (put sym 'completion-predicate #'ignore)) - -(provide 'corfu-quick) -;;; corfu-quick.el ends here blob - 52dcaf201441dc665ee95fa11190799d9d34256a (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu.el +++ /dev/null @@ -1,1398 +0,0 @@ -;;; corfu.el --- COmpletion in Region FUnction -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; Author: Daniel Mendler -;; Maintainer: Daniel Mendler -;; Created: 2021 -;; Version: 1.3 -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4")) -;; Homepage: https://github.com/minad/corfu -;; Keywords: abbrev, convenience, matching, completion, text - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Corfu enhances in-buffer completion with a small completion popup. -;; The current candidates are shown in a popup below or above the -;; point. The candidates can be selected by moving up and down. -;; Corfu is the minimalistic in-buffer completion counterpart of the -;; Vertico minibuffer UI. - -;;; Code: - -(require 'compat) -(eval-when-compile - (require 'cl-lib) - (require 'subr-x)) - -(defgroup corfu nil - "COmpletion in Region FUnction." - :link '(info-link :tag "Info Manual" "(corfu)") - :link '(url-link :tag "Homepage" "https://github.com/minad/corfu") - :link '(emacs-library-link :tag "Library Source" "corfu.el") - :group 'convenience - :group 'tools - :group 'matching - :prefix "corfu-") - -(defcustom corfu-count 10 - "Maximal number of candidates to show." - :type 'natnum) - -(defcustom corfu-scroll-margin 2 - "Number of lines at the top and bottom when scrolling. -The value should lie between 0 and corfu-count/2." - :type 'natnum) - -(defcustom corfu-min-width 15 - "Popup minimum width in characters." - :type 'natnum) - -(defcustom corfu-max-width 100 - "Popup maximum width in characters." - :type 'natnum) - -(defcustom corfu-cycle nil - "Enable cycling for `corfu-next' and `corfu-previous'." - :type 'boolean) - -(defcustom corfu-on-exact-match 'insert - "Configure how a single exact match should be handled. -- nil: No special handling, continue completion. -- insert: Insert candidate, quit and call the `:exit-function'. -- quit: Quit completion without further action. -- show: Initiate completion even for a single match only." - :type '(choice (const insert) (const show) (const quit) (const nil))) - -(defcustom corfu-continue-commands - ;; nil is undefined command - '(nil ignore universal-argument universal-argument-more digit-argument - "\\`corfu-" "\\`scroll-other-window") - "Continue Corfu completion after executing these commands. -The list can container either command symbols or regular expressions." - :type '(repeat (choice regexp symbol))) - -(defcustom corfu-preview-current 'insert - "Preview currently selected candidate. -If the variable has the value `insert', the candidate is automatically -inserted on further input." - :type '(choice boolean (const insert))) - -(defcustom corfu-preselect 'valid - "Configure if the prompt or first candidate is preselected. -- prompt: Always select the prompt. -- first: Always select the first candidate. -- valid: Only select the prompt if valid and not equal to the first candidate. -- directory: Like first, but select the prompt if it is a directory." - :type '(choice (const prompt) (const valid) (const first) (const directory))) - -(defcustom corfu-separator ?\s - "Component separator character. -The character used for separating components in the input. The presence -of this separator character will inhibit quitting at completion -boundaries, so that any further characters can be entered. To enter the -first separator character, call `corfu-insert-separator' (bound to M-SPC -by default). Useful for multi-component completion styles such as -Orderless." - :type 'character) - -(defcustom corfu-quit-at-boundary 'separator - "Automatically quit at completion boundary. -nil: Never quit at completion boundary. -t: Always quit at completion boundary. -separator: Quit at boundary if no `corfu-separator' has been inserted." - :type '(choice boolean (const separator))) - -(defcustom corfu-quit-no-match 'separator - "Automatically quit if no matching candidate is found. -When staying alive even if there is no match a warning message is -shown in the popup. -nil: Stay alive even if there is no match. -t: Quit if there is no match. -separator: Only stay alive if there is no match and -`corfu-separator' has been inserted." - :type '(choice boolean (const separator))) - -(defcustom corfu-left-margin-width 0.5 - "Width of the left margin in units of the character width." - :type 'float) - -(defcustom corfu-right-margin-width 0.5 - "Width of the right margin in units of the character width." - :type 'float) - -(defcustom corfu-bar-width 0.2 - "Width of the bar in units of the character width." - :type 'float) - -(defcustom corfu-margin-formatters nil - "Registry for margin formatter functions. -Each function of the list is called with the completion metadata as -argument until an appropriate formatter is found. The function should -return a formatter function, which takes the candidate string and must -return a string, possibly an icon." - :type 'hook) - -(defcustom corfu-sort-function #'corfu-sort-length-alpha - "Default sorting function. -This function is used if the completion table does not specify a -`display-sort-function'." - :type `(choice - (const :tag "No sorting" nil) - (const :tag "By length and alpha" ,#'corfu-sort-length-alpha) - (function :tag "Custom function"))) - -(defcustom corfu-sort-override-function nil - "Override sort function which overrides the `display-sort-function'. -This function is used even if a completion table specifies its -own sort function." - :type '(choice (const nil) function)) - -(defcustom corfu-auto-prefix 3 - "Minimum length of prefix for auto completion. -The completion backend can override this with -:company-prefix-length. It is *not recommended* to use a small -prefix length (below 2), since this will create high load for -Emacs. See also `corfu-auto-delay'." - :type 'natnum) - -(defcustom corfu-auto-delay 0.2 - "Delay for auto completion. -It is *not recommended* to use a short delay or even 0, since -this will create high load for Emacs, in particular if executing -the completion backend is costly." - :type 'float) - -(defcustom corfu-auto-commands - '("self-insert-command\\'" - c-electric-colon c-electric-lt-gt c-electric-slash c-scope-operator) - "Commands which initiate auto completion. -The list can container either command symbols or regular expressions." - :type '(repeat (choice regexp symbol))) - -(defcustom corfu-auto nil - "Enable auto completion. -See also the settings `corfu-auto-delay', `corfu-auto-prefix' and -`corfu-auto-commands'." - :type 'boolean) - -(defgroup corfu-faces nil - "Faces used by Corfu." - :group 'corfu - :group 'faces) - -(defface corfu-default - '((((class color) (min-colors 88) (background dark)) :background "#191a1b") - (((class color) (min-colors 88) (background light)) :background "#f0f0f0") - (t :background "gray")) - "Default face, foreground and background colors used for the popup.") - -(defface corfu-current - '((((class color) (min-colors 88) (background dark)) - :background "#00415e" :foreground "white") - (((class color) (min-colors 88) (background light)) - :background "#c0efff" :foreground "black") - (t :background "blue" :foreground "white")) - "Face used to highlight the currently selected candidate.") - -(defface corfu-bar - '((((class color) (min-colors 88) (background dark)) :background "#a8a8a8") - (((class color) (min-colors 88) (background light)) :background "#505050") - (t :background "gray")) - "The background color is used for the scrollbar indicator.") - -(defface corfu-border - '((((class color) (min-colors 88) (background dark)) :background "#323232") - (((class color) (min-colors 88) (background light)) :background "#d7d7d7") - (t :background "gray")) - "The background color used for the thin border.") - -(defface corfu-annotations - '((t :inherit completions-annotations)) - "Face used for annotations.") - -(defface corfu-deprecated - '((t :inherit shadow :strike-through t)) - "Face used for deprecated candidates.") - -(defvar-keymap corfu-mode-map - :doc "Keymap used when `corfu-mode' is active.") - -(defvar-keymap corfu-map - :doc "Keymap used when popup is shown." - " " #'corfu-prompt-beginning - " " #'corfu-prompt-end - " " #'corfu-first - " " #'corfu-last - " " #'corfu-scroll-down - " " #'corfu-scroll-up - " " #'corfu-next - " " #'corfu-previous - " " #'corfu-complete - " " #'corfu-reset - "" #'corfu-next - "" #'corfu-previous - ;; XXX C-a is bound because of Eshell. - ;; Ideally Eshell would remap move-beginning-of-line. - "C-a" #'corfu-prompt-beginning - ;; XXX [tab] is bound because of org-mode and orgtbl-mode. - ;; The binding should be removed from org-mode-map. - "" #'corfu-complete - "M-n" #'corfu-next - "M-p" #'corfu-previous - "C-g" #'corfu-quit - "RET" #'corfu-insert - "TAB" #'corfu-complete - "M-TAB" #'corfu-expand - "M-g" 'corfu-info-location - "M-h" 'corfu-info-documentation - "M-SPC" #'corfu-insert-separator) - -(defvar corfu--auto-timer (timer-create) - "Auto completion timer.") - -(defvar corfu--candidates nil - "List of candidates.") - -(defvar corfu--metadata nil - "Completion metadata.") - -(defvar corfu--base "" - "Base string, which is concatenated with the candidate.") - -(defvar corfu--total 0 - "Length of the candidate list `corfu--candidates'.") - -(defvar corfu--hilit #'identity - "Lazy candidate highlighting function.") - -(defvar corfu--index -1 - "Index of current candidate or negative for prompt selection.") - -(defvar corfu--preselect -1 - "Index of preselected candidate, negative for prompt selection.") - -(defvar corfu--scroll 0 - "Scroll position.") - -(defvar corfu--input nil - "Cons of last prompt contents and point.") - -(defvar corfu--preview-ov nil - "Current candidate overlay.") - -(defvar corfu--change-group nil - "Undo change group.") - -(defvar corfu--frame nil - "Popup frame.") - -(defconst corfu--initial-state - (mapcar - (lambda (k) (cons k (symbol-value k))) - '(corfu--base - corfu--candidates - corfu--hilit - corfu--index - corfu--preselect - corfu--scroll - corfu--input - corfu--total - corfu--preview-ov - corfu--change-group - corfu--metadata)) - "Initial Corfu state.") - -(defvar corfu--frame-parameters - '((no-accept-focus . t) - (no-focus-on-map . t) - (min-width . t) - (min-height . t) - (border-width . 0) - (outer-border-width . 0) - (internal-border-width . 1) - (child-frame-border-width . 1) - (left-fringe . 0) - (right-fringe . 0) - (vertical-scroll-bars . nil) - (horizontal-scroll-bars . nil) - (menu-bar-lines . 0) - (tool-bar-lines . 0) - (tab-bar-lines . 0) - (no-other-frame . t) - (unsplittable . t) - (undecorated . t) - (cursor-type . nil) - (no-special-glyphs . t) - (desktop-dont-save . t)) - "Default child frame parameters.") - -(defvar corfu--buffer-parameters - '((mode-line-format . nil) - (header-line-format . nil) - (tab-line-format . nil) - (tab-bar-format . nil) ;; Emacs 28 tab-bar-format - (frame-title-format . "") - (truncate-lines . t) - (cursor-in-non-selected-windows . nil) - (cursor-type . nil) - (show-trailing-whitespace . nil) - (display-line-numbers . nil) - (left-fringe-width . nil) - (right-fringe-width . nil) - (left-margin-width . 0) - (right-margin-width . 0) - (fringes-outside-margins . 0) - (fringe-indicator-alist . nil) - (indicate-empty-lines . nil) - (indicate-buffer-boundaries . nil) - (buffer-read-only . t)) - "Default child frame buffer parameters.") - -(defvar corfu--mouse-ignore-map - (let ((map (make-sparse-keymap))) - (dotimes (i 7) - (dolist (k '(mouse down-mouse drag-mouse double-mouse triple-mouse)) - (keymap-set map (format "<%s-%s>" k (1+ i)) #'ignore))) - map) - "Ignore all mouse clicks.") - -(defun corfu--replace (beg end str) - "Replace range between BEG and END with STR." - (unless (equal str (buffer-substring-no-properties beg end)) - ;; bug#55205: completion--replace removed properties as an unwanted - ;; side-effect. We also don't want to leave text properties. - (completion--replace beg end (substring-no-properties str)))) - -(defun corfu--capf-wrapper (fun &optional prefix) - "Wrapper for `completion-at-point' FUN. -The wrapper determines if the Capf is applicable at the current -position and performs sanity checking on the returned result. -For non-exclusive Capfs wrapper additionally checks if the -current input can be completed successfully. PREFIX is a prefix -length override, set to t for manual completion." - (pcase (funcall fun) - ((and res `(,beg ,end ,table . ,plist)) - (and (integer-or-marker-p beg) ;; Valid Capf result - (<= beg (point) end) ;; Sanity checking - ;; When auto completing, check the prefix length! - (let ((len (or prefix - (plist-get plist :company-prefix-length) - (- (point) beg)))) - (or (eq len t) (>= len corfu-auto-prefix))) - ;; For non-exclusive Capfs, check for valid completion. - (or (not (eq 'no (plist-get plist :exclusive))) - (let* ((str (buffer-substring-no-properties beg end)) - (pt (- (point) beg)) - (pred (plist-get plist :predicate)) - (md (completion-metadata (substring str 0 pt) table pred))) - ;; We use `completion-try-completion' to check if there are - ;; completions. The upstream `completion--capf-wrapper' uses - ;; `try-completion' which is incorrect since it only checks for - ;; prefix completions. - (completion-try-completion str table pred pt md))) - (cons fun res))))) - -(defun corfu--make-buffer (name) - "Create buffer with NAME." - (let ((fr face-remapping-alist) - (ls line-spacing) - (buffer (get-buffer-create name))) - (with-current-buffer buffer - ;;; XXX HACK install mouse ignore map - (use-local-map corfu--mouse-ignore-map) - (dolist (var corfu--buffer-parameters) - (set (make-local-variable (car var)) (cdr var))) - (setq-local face-remapping-alist (copy-tree fr) - line-spacing ls) - (cl-pushnew 'corfu-default (alist-get 'default face-remapping-alist)) - buffer))) - -(defvar x-gtk-resize-child-frames) ;; Not present on non-gtk builds -(defvar corfu--gtk-resize-child-frames - (let ((case-fold-search t)) - ;; XXX HACK to fix resizing on gtk3/gnome taken from posframe.el - ;; More information: - ;; * https://github.com/minad/corfu/issues/17 - ;; * https://gitlab.gnome.org/GNOME/mutter/-/issues/840 - ;; * https://lists.gnu.org/archive/html/emacs-devel/2020-02/msg00001.html - (and (string-match-p "gtk3" system-configuration-features) - (string-match-p "gnome\\|cinnamon" - (or (getenv "XDG_CURRENT_DESKTOP") - (getenv "DESKTOP_SESSION") "")) - 'resize-mode))) - -;; Function adapted from posframe.el by tumashu -(defun corfu--make-frame (frame x y width height buffer) - "Show BUFFER in child frame at X/Y with WIDTH/HEIGHT. -FRAME is the existing frame." - (when-let (((frame-live-p frame)) - (timer (frame-parameter frame 'corfu--hide-timer))) - (cancel-timer timer) - (set-frame-parameter frame 'corfu--hide-timer nil)) - (let* ((window-min-height 1) - (window-min-width 1) - (inhibit-redisplay t) - (x-gtk-resize-child-frames corfu--gtk-resize-child-frames) - (before-make-frame-hook) - (after-make-frame-functions) - (parent (window-frame))) - (unless (and (frame-live-p frame) - (eq (frame-parent frame) - (and (not (bound-and-true-p exwm--connection)) parent)) - ;; If there is more than one window, `frame-root-window' may - ;; return nil. Recreate the frame in this case. - (window-live-p (frame-root-window frame))) - (when frame (delete-frame frame)) - (setq frame (make-frame - `((parent-frame . ,parent) - (minibuffer . ,(minibuffer-window parent)) - (width . 0) (height . 0) (visibility . nil) - ,@corfu--frame-parameters)))) - ;; XXX HACK Setting the same frame-parameter/face-background is not a nop. - ;; Check before applying the setting. Without the check, the frame flickers - ;; on Mac. We have to apply the face background before adjusting the frame - ;; parameter, otherwise the border is not updated. - (let ((new (face-attribute 'corfu-border :background nil 'default))) - (unless (equal (face-attribute 'internal-border :background frame 'default) new) - (set-face-background 'internal-border new frame)) - ;; XXX The Emacs Mac Port does not support `internal-border', we also have - ;; to set `child-frame-border'. - (unless (or (not (facep 'child-frame-border)) - (equal (face-attribute 'child-frame-border :background frame 'default) new)) - (set-face-background 'child-frame-border new frame))) - ;; Reset frame parameters if they changed. For example `tool-bar-mode' - ;; overrides the parameter `tool-bar-lines' for every frame, including child - ;; frames. The child frame API is a pleasure to work with. It is full of - ;; lovely surprises. - (let* ((is (frame-parameters frame)) - (should `((background-color - . ,(face-attribute 'corfu-default :background nil 'default)) - (font . ,(frame-parameter parent 'font)) - ,@corfu--frame-parameters)) - (diff (cl-loop for p in should for (k . v) = p - unless (equal (alist-get k is) v) collect p))) - (when diff (modify-frame-parameters frame diff))) - (let ((win (frame-root-window frame))) - (unless (eq (window-buffer win) buffer) - (set-window-buffer win buffer)) - ;; Disallow selection of root window (gh:minad/corfu#63) - (set-window-parameter win 'no-delete-other-windows t) - (set-window-parameter win 'no-other-window t) - ;; Mark window as dedicated to prevent frame reuse (gh:minad/corfu#60) - (set-window-dedicated-p win t)) - (redirect-frame-focus frame parent) - (set-frame-size frame width height t) - (pcase-let ((`(,px . ,py) (frame-position frame))) - (unless (and (= x px) (= y py)) - (set-frame-position frame x y)))) - (make-frame-visible frame) - ;; Unparent child frame if EXWM is used, otherwise EXWM buffers are drawn on - ;; top of the Corfu child frame. - (when (and (bound-and-true-p exwm--connection) (frame-parent frame)) - (set-frame-parameter frame 'parent-frame nil)) - frame) - -(defun corfu--hide-frame-deferred (frame) - "Deferred hiding of child FRAME." - (when (and (frame-live-p frame) (frame-visible-p frame)) - (set-frame-parameter frame 'corfu--hide-timer nil) - (make-frame-invisible frame) - (with-current-buffer (window-buffer (frame-root-window frame)) - (with-silent-modifications - (erase-buffer))))) - -(defun corfu--hide-frame (frame) - "Hide child FRAME." - (when (and (frame-live-p frame) (frame-visible-p frame) - (not (frame-parameter frame 'corfu--hide-timer))) - (set-frame-parameter frame 'corfu--hide-timer - (run-at-time 0 nil #'corfu--hide-frame-deferred frame)))) - -(defun corfu--move-to-front (elem list) - "Move ELEM to front of LIST." - ;; In contrast to Vertico, this function handles duplicates. See also the - ;; special deduplication function `corfu--delete-dups' based on - ;; `equal-including-properties' - (nconc (cl-loop for x in list if (equal x elem) collect x) - (delete elem list))) - -(defun corfu--filter-completions (&rest args) - "Compute all completions for ARGS with lazy highlighting." - (defvar completion-lazy-hilit) - (defvar completion-lazy-hilit-fn) - (cl-letf* ((completion-lazy-hilit t) - (completion-lazy-hilit-fn nil) - ((symbol-function #'completion-hilit-commonality) - (lambda (cands prefix &optional base) - (setq completion-lazy-hilit-fn - (lambda (x) (car (completion-hilit-commonality (list x) prefix base)))) - (and cands (nconc cands base))))) - (if (eval-when-compile (>= emacs-major-version 30)) - (cons (apply #'completion-all-completions args) completion-lazy-hilit-fn) - (cl-letf* ((orig-pcm (symbol-function #'completion-pcm--hilit-commonality)) - (orig-flex (symbol-function #'completion-flex-all-completions)) - ((symbol-function #'completion-flex-all-completions) - (lambda (&rest args) - ;; Unfortunately for flex we have to undo the lazy highlighting, since flex uses - ;; the completion-score for sorting, which is applied during highlighting. - (cl-letf (((symbol-function #'completion-pcm--hilit-commonality) orig-pcm)) - (apply orig-flex args)))) - ((symbol-function #'completion-pcm--hilit-commonality) - (lambda (pattern cands) - (setq completion-lazy-hilit-fn - (lambda (x) - ;; `completion-pcm--hilit-commonality' sometimes throws an internal error - ;; for example when entering "/sudo:://u". - (condition-case nil - (car (completion-pcm--hilit-commonality pattern (list x))) - (t x)))) - cands))) - (cons (apply #'completion-all-completions args) completion-lazy-hilit-fn))))) - -(defsubst corfu--length-string< (x y) - "Sorting predicate which compares X and Y first by length then by `string<'." - (or (< (length x) (length y)) (and (= (length x) (length y)) (string< x y)))) - -(defmacro corfu--partition! (list form) - "Evaluate FORM for every element and partition LIST." - (cl-with-gensyms (head1 head2 tail1 tail2) - `(let* ((,head1 (cons nil nil)) - (,head2 (cons nil nil)) - (,tail1 ,head1) - (,tail2 ,head2)) - (while ,list - (if (let ((it (car ,list))) ,form) - (progn - (setcdr ,tail1 ,list) - (pop ,tail1)) - (setcdr ,tail2 ,list) - (pop ,tail2)) - (pop ,list)) - (setcdr ,tail1 (cdr ,head2)) - (setcdr ,tail2 nil) - (setq ,list (cdr ,head1))))) - -(defun corfu--move-prefix-candidates-to-front (field cands) - "Move CANDS which match prefix of FIELD to the beginning." - (let* ((word (substring field 0 - (seq-position field corfu-separator))) - (len (length word))) - (corfu--partition! - cands - (and (>= (length it) len) - (eq t (compare-strings word 0 len it 0 len - completion-ignore-case)))))) - -(defun corfu--delete-dups (list) - "Delete `equal-including-properties' consecutive duplicates from LIST." - (let ((beg list)) - (while (cdr beg) - (let ((end (cdr beg))) - (while (equal (car beg) (car end)) (pop end)) - ;; The deduplication is quadratic in the number of duplicates. We can - ;; avoid the quadratic complexity with a hash table which takes - ;; properties into account (available since Emacs 28). - (while (not (eq beg end)) - (let ((dup beg)) - (while (not (eq (cdr dup) end)) - ;; bug#6581: `equal-including-properties' uses `eq' to compare - ;; properties until 29.1. Approximate by comparing - ;; `text-properties-at' position 0. - (if (if (eval-when-compile (< emacs-major-version 29)) - (equal (text-properties-at 0 (car beg)) - (text-properties-at 0 (cadr dup))) - (equal-including-properties (car beg) (cadr dup))) - (setcdr dup (cddr dup)) - (pop dup)))) - (pop beg))))) - list) - -(defun corfu--sort-function () - "Return the sorting function." - (or corfu-sort-override-function - (corfu--metadata-get 'display-sort-function) - corfu-sort-function)) - -(defun corfu--recompute (str pt table pred) - "Recompute state from STR, PT, TABLE and PRED." - (pcase-let* ((before (substring str 0 pt)) - (after (substring str pt)) - (corfu--metadata (completion-metadata before table pred)) - ;; bug#47678: `completion-boundaries' fails for `partial-completion' - ;; if the cursor is moved between the slashes of "~//". - ;; See also vertico.el which has the same issue. - (bounds (condition-case nil - (completion-boundaries before table pred after) - (t (cons 0 (length after))))) - (field (substring str (car bounds) (+ pt (cdr bounds)))) - (completing-file (eq (corfu--metadata-get 'category) 'file)) - (`(,all . ,hl) (corfu--filter-completions str table pred pt corfu--metadata)) - (base (or (when-let ((z (last all))) (prog1 (cdr z) (setcdr z nil))) 0)) - (corfu--base (substring str 0 base)) - (pre nil)) - ;; Filter the ignored file extensions. We cannot use modified predicate for - ;; this filtering, since this breaks the special casing in the - ;; `completion-file-name-table' for `file-exists-p' and `file-directory-p'. - (when completing-file (setq all (completion-pcm--filename-try-filter all))) - ;; Sort using the `display-sort-function' or the Corfu sort functions, and - ;; delete duplicates with respect to `equal-including-properties'. This is - ;; a deviation from the Vertico completion UI with more aggressive - ;; deduplication, where candidates are compared with `equal'. Corfu - ;; preserves candidates which differ in their text properties. Corfu tries - ;; to preserve text properties as much as possible, when calling the - ;; `:exit-function' to help Capfs with candidate disambiguation. This - ;; matters in particular for Lsp backends, which produce duplicates for - ;; overloaded methods. - (setq all (corfu--delete-dups (funcall (or (corfu--sort-function) #'identity) all)) - all (corfu--move-prefix-candidates-to-front field all)) - (when (and completing-file (not (string-suffix-p "/" field))) - (setq all (corfu--move-to-front (concat field "/") all))) - (setq all (corfu--move-to-front field all) - pre (if (or (eq corfu-preselect 'prompt) (not all) - (and completing-file (eq corfu-preselect 'directory) - (= (length corfu--base) (length str)) - (test-completion str table pred)) - (and (eq corfu-preselect 'valid) - (not (equal field (car all))) - (not (and completing-file (equal (concat field "/") (car all)))) - (test-completion str table pred))) - -1 0)) - `((corfu--base . ,corfu--base) - (corfu--metadata . ,corfu--metadata) - (corfu--candidates . ,all) - (corfu--total . ,(length all)) - (corfu--hilit . ,(or hl #'identity)) - (corfu--preselect . ,pre) - (corfu--index . ,(or (and (>= corfu--index 0) (/= corfu--index corfu--preselect) - (seq-position all (nth corfu--index corfu--candidates))) - pre))))) - -(defun corfu--update (&optional interruptible) - "Update state, optionally INTERRUPTIBLE." - (pcase-let* ((`(,beg ,end ,table ,pred . ,_) completion-in-region--data) - (pt (- (point) beg)) - (str (buffer-substring-no-properties beg end)) - (input (cons str pt))) - (unless (equal corfu--input input) - ;; Redisplay such that the input is immediately shown before the expensive - ;; candidate recomputation (gh:minad/corfu#48). See also corresponding - ;; issue gh:minad/vertico#89. - (when interruptible (redisplay)) - ;; Bind non-essential=t to prevent Tramp from opening new connections, - ;; without the user explicitly requesting it via M-TAB. - (pcase (let ((non-essential t)) - ;; XXX Guard against errors during candidate generation. - ;; bug#61274: `dabbrev-capf' signals errors. - (condition-case err - (if interruptible - (while-no-input (corfu--recompute str pt table pred)) - (corfu--recompute str pt table pred)) - (error - (message "Corfu completion error: %s" (error-message-string err)) - t))) - ('nil (keyboard-quit)) - ((and state (pred consp)) - (setq corfu--input input) - (dolist (s state) (set (car s) (cdr s)))))) - input)) - -(defun corfu--match-symbol-p (pattern sym) - "Return non-nil if SYM is matching an element of the PATTERN list." - (cl-loop with case-fold-search = nil - for x in (and (symbolp sym) pattern) - thereis (if (symbolp x) - (eq sym x) - (string-match-p x (symbol-name sym))))) - -(defun corfu--metadata-get (prop) - "Return PROP from completion metadata." - ;; Note: Do not use `completion-metadata-get' in order to avoid Marginalia. - ;; The Marginalia annotators are too heavy for the Corfu popup! - (cdr (assq prop corfu--metadata))) - -(defun corfu--format-candidates (cands) - "Format annotated CANDS." - (setq cands - (cl-loop for c in cands collect - (cl-loop for s in c collect - (replace-regexp-in-string "[ \t]*\n[ \t]*" " " s)))) - (let* ((cw (cl-loop for x in cands maximize (string-width (car x)))) - (pw (cl-loop for x in cands maximize (string-width (cadr x)))) - (sw (cl-loop for x in cands maximize (string-width (caddr x)))) - (width (+ pw cw sw)) - ;; -4 because of margins and some additional safety - (max-width (min corfu-max-width (- (frame-width) 4)))) - (when (> width max-width) - (setq sw (max 0 (- max-width pw cw)) - width (+ pw cw sw))) - (when (< width corfu-min-width) - (setq cw (+ cw (- corfu-min-width width)) - width corfu-min-width)) - (setq width (min width max-width)) - (list pw width - (cl-loop for (cand prefix suffix) in cands collect - (truncate-string-to-width - (concat - prefix (make-string (max 0 (- pw (string-width prefix))) ?\s) - cand - (when (/= sw 0) - (make-string (+ (max 0 (- cw (string-width cand))) - (max 0 (- sw (string-width suffix)))) - ?\s)) - suffix) - width))))) - -(defun corfu--compute-scroll () - "Compute new scroll position." - (let ((off (max (min corfu-scroll-margin (/ corfu-count 2)) 0)) - (corr (if (= corfu-scroll-margin (/ corfu-count 2)) (1- (mod corfu-count 2)) 0))) - (setq corfu--scroll (min (max 0 (- corfu--total corfu-count)) - (max 0 (+ corfu--index off 1 (- corfu-count)) - (min (- corfu--index off corr) corfu--scroll)))))) - -(defun corfu--candidates-popup (pos) - "Show candidates popup at POS." - (corfu--compute-scroll) - (pcase-let* ((last (min (+ corfu--scroll corfu-count) corfu--total)) - (bar (ceiling (* corfu-count corfu-count) corfu--total)) - (lo (min (- corfu-count bar 1) (floor (* corfu-count corfu--scroll) corfu--total))) - (`(,mf . ,acands) (corfu--affixate - (cl-loop repeat corfu-count - for c in (nthcdr corfu--scroll corfu--candidates) - collect (funcall corfu--hilit (substring c))))) - (`(,pw ,width ,fcands) (corfu--format-candidates acands)) - ;; Disable the left margin if a margin formatter is active. - (corfu-left-margin-width (if mf 0 corfu-left-margin-width))) - ;; Nonlinearity at the end and the beginning - (when (/= corfu--scroll 0) - (setq lo (max 1 lo))) - (when (/= last corfu--total) - (setq lo (min (- corfu-count bar 2) lo))) - (corfu--popup-show pos pw width fcands (- corfu--index corfu--scroll) - (and (> corfu--total corfu-count) lo) bar))) - -(defun corfu--range-valid-p () - "Check the completion range, return non-nil if valid." - (pcase-let ((buf (current-buffer)) - (pt (point)) - (`(,beg ,end . ,_) completion-in-region--data)) - (and beg end - (eq buf (marker-buffer beg)) (eq buf (window-buffer)) - (<= beg pt end) - (save-excursion (goto-char beg) (<= (pos-bol) pt (pos-eol)))))) - -(defun corfu--continue-p () - "Check if completion should continue after a command. -Corfu bails out if the current buffer changed unexpectedly or if -point moved out of range, see `corfu--range-valid-p'. Also the -input must satisfy the `completion-in-region-mode--predicate' and -the last command must be listed in `corfu-continue-commands'." - (and (corfu--range-valid-p) - ;; We keep Corfu alive if a `overriding-terminal-local-map' is - ;; installed, e.g., the `universal-argument-map'. It would be good to - ;; think about a better criterion instead. Unfortunately relying on - ;; `this-command' alone is insufficient, since the value of - ;; `this-command' gets clobbered in the case of transient keymaps. - (or overriding-terminal-local-map - ;; Check if it is an explicitly listed continue command - (corfu--match-symbol-p corfu-continue-commands this-command) - (pcase-let ((`(,beg ,end . ,_) completion-in-region--data)) - (and (or (not corfu--input) (< beg end)) ;; Check for empty input - (or (not corfu-quit-at-boundary) ;; Check separator or predicate - (and (eq corfu-quit-at-boundary 'separator) - (or (eq this-command #'corfu-insert-separator) - ;; with separator, any further chars allowed - (seq-contains-p (car corfu--input) corfu-separator))) - (funcall completion-in-region-mode--predicate))))))) - -(defun corfu--preview-current-p () - "Return t if the selected candidate is previewed." - (and corfu-preview-current (>= corfu--index 0) (/= corfu--index corfu--preselect))) - -(defun corfu--preview-current (beg end) - "Show current candidate as overlay given BEG and END." - (when (corfu--preview-current-p) - (setq beg (+ beg (length corfu--base)) - corfu--preview-ov (make-overlay beg end nil)) - (overlay-put corfu--preview-ov 'priority 1000) - (overlay-put corfu--preview-ov 'window (selected-window)) - (overlay-put corfu--preview-ov (if (= beg end) 'after-string 'display) - (nth corfu--index corfu--candidates)))) - -(defun corfu--window-change (_) - "Window and buffer change hook which quits Corfu." - (unless (corfu--range-valid-p) - (corfu-quit))) - -(defun corfu--post-command () - "Refresh Corfu after last command." - (if (corfu--continue-p) - (corfu--exhibit) - (corfu-quit)) - (when corfu-auto - (corfu--auto-post-command))) - -(defun corfu--goto (index) - "Go to candidate with INDEX." - (setq corfu--index (max corfu--preselect (min index (1- corfu--total))))) - -(defun corfu--exit-function (str status cands) - "Call the `:exit-function' with STR and STATUS. -Lookup STR in CANDS to restore text properties." - (when-let ((exit (plist-get completion-extra-properties :exit-function))) - (funcall exit (or (car (member str cands)) str) status))) - -(defun corfu--done (str status cands) - "Exit completion and call the exit function with STR and STATUS. -Lookup STR in CANDS to restore text properties." - (let ((completion-extra-properties (nth 4 completion-in-region--data))) - ;; For successful completions, amalgamate undo operations, - ;; such that completion can be undone in a single step. - (undo-amalgamate-change-group corfu--change-group) - (corfu-quit) - (corfu--exit-function str status cands))) - -(defun corfu--setup (beg end table pred) - "Setup Corfu completion state. -See `completion-in-region' for the arguments BEG, END, TABLE, PRED." - (setq beg (if (markerp beg) beg (copy-marker beg)) - end (if (and (markerp end) (marker-insertion-type end)) end (copy-marker end t)) - completion-in-region--data (list beg end table pred completion-extra-properties)) - (completion-in-region-mode 1) - (activate-change-group (setq corfu--change-group (prepare-change-group))) - (setcdr (assq #'completion-in-region-mode minor-mode-overriding-map-alist) corfu-map) - (add-hook 'pre-command-hook #'corfu--prepare nil 'local) - (add-hook 'window-selection-change-functions #'corfu--window-change nil 'local) - (add-hook 'window-buffer-change-functions #'corfu--window-change nil 'local) - (add-hook 'post-command-hook #'corfu--post-command) - ;; Disable default post-command handling, since we have our own - ;; checks in `corfu--post-command'. - (remove-hook 'post-command-hook #'completion-in-region--postch) - (let ((sym (make-symbol "corfu--teardown")) - (buf (current-buffer))) - (fset sym (lambda () - ;; Ensure that the tear-down runs in the correct buffer, if still alive. - (unless completion-in-region-mode - (remove-hook 'completion-in-region-mode-hook sym) - (corfu--teardown buf)))) - (add-hook 'completion-in-region-mode-hook sym))) - -(defun corfu--in-region (&rest args) - "Corfu completion in region function called with ARGS." - ;; XXX We can get an endless loop when `completion-in-region-function' is set - ;; globally to `corfu--in-region'. This should never happen. - (apply (if (corfu--popup-support-p) #'corfu--in-region-1 - (default-value 'completion-in-region-function)) - args)) - -(defun corfu--in-region-1 (beg end table &optional pred) - "Complete in region, see `completion-in-region' for BEG, END, TABLE, PRED." - (barf-if-buffer-read-only) - ;; Restart the completion. This can happen for example if C-M-/ - ;; (`dabbrev-completion') is pressed while the Corfu popup is already open. - (when completion-in-region-mode (corfu-quit)) - (let* ((pt (max 0 (- (point) beg))) - (str (buffer-substring-no-properties beg end)) - (metadata (completion-metadata (substring str 0 pt) table pred)) - (threshold (completion--cycle-threshold metadata)) - (completion-in-region-mode-predicate - (or completion-in-region-mode-predicate #'always))) - (pcase (completion-try-completion str table pred pt metadata) - ('nil (corfu--message "No match") nil) - ('t (goto-char end) - (corfu--message "Sole match") - (if (eq corfu-on-exact-match 'show) - (corfu--setup beg end table pred) - (corfu--exit-function - str 'finished - (alist-get 'corfu--candidates (corfu--recompute str pt table pred)))) - t) - (`(,newstr . ,newpt) - (setq beg (if (markerp beg) beg (copy-marker beg)) - end (copy-marker end t)) - (corfu--replace beg end newstr) - (goto-char (+ beg newpt)) - (let* ((state (corfu--recompute newstr newpt table pred)) - (base (alist-get 'corfu--base state)) - (total (alist-get 'corfu--total state)) - (candidates (alist-get 'corfu--candidates state))) - (if (= total 1) - ;; If completion is finished and cannot be further completed, and - ;; the value of `corfu-on-exact-match' is not 'show, return - ;; 'finished. Otherwise setup the Corfu popup. - (if (or (eq corfu-on-exact-match 'show) - (consp (completion-try-completion - newstr table pred newpt - (completion-metadata newstr table pred)))) - (corfu--setup beg end table pred) - (corfu--exit-function newstr 'finished candidates)) - (if (or (= total 0) (not threshold) - (and (not (eq threshold t)) (< threshold total))) - (corfu--setup beg end table pred) - (corfu--cycle-candidates total candidates (+ (length base) beg) end) - ;; Do not show Corfu when "trivially" cycling, i.e., - ;; when the completion is finished after the candidate. - (unless (equal (completion-boundaries (car candidates) table pred "") - '(0 . 0)) - (corfu--setup beg end table pred))))) - t)))) - -(defun corfu--message (&rest msg) - "Show completion MSG." - (let (message-log-max) (apply #'message msg))) - -(defun corfu--cycle-candidates (total cands beg end) - "Cycle between TOTAL number of CANDS. -See `completion-in-region' for the arguments BEG, END, TABLE, PRED." - (let* ((idx 0) - (map (make-sparse-keymap)) - (replace (lambda () - (interactive) - (corfu--replace beg end (nth idx cands)) - (corfu--message "Cycling %d/%d..." (1+ idx) total) - (setq idx (mod (1+ idx) total)) - (set-transient-map map)))) - (define-key map [remap completion-at-point] replace) - (define-key map [remap corfu-complete] replace) - (define-key map (vector last-command-event) replace) - (funcall replace))) - -(defun corfu--auto-complete-deferred (&optional tick) - "Initiate auto completion if TICK did not change." - (when (and (not completion-in-region-mode) - (or (not tick) (equal tick (corfu--auto-tick)))) - (pcase (while-no-input ;; Interruptible Capf query - (run-hook-wrapped 'completion-at-point-functions #'corfu--capf-wrapper)) - (`(,fun ,beg ,end ,table . ,plist) - (let ((completion-in-region-mode-predicate - (lambda () - (when-let ((newbeg (car-safe (funcall fun)))) - (= newbeg beg)))) - (completion-extra-properties plist)) - (corfu--setup beg end table (plist-get plist :predicate)) - (corfu--exhibit 'auto)))))) - -(defun corfu--auto-post-command () - "Post command hook which initiates auto completion." - (cancel-timer corfu--auto-timer) - (if (and (not completion-in-region-mode) - (not defining-kbd-macro) - (not buffer-read-only) - (corfu--match-symbol-p corfu-auto-commands this-command) - (corfu--popup-support-p)) - (if (<= corfu-auto-delay 0) - (corfu--auto-complete-deferred) - ;; Do not use `timer-set-idle-time' since this leads to - ;; unpredictable pauses, in particular with `flyspell-mode'. - (timer-set-time corfu--auto-timer - (timer-relative-time nil corfu-auto-delay)) - (timer-set-function corfu--auto-timer #'corfu--auto-complete-deferred - (list (corfu--auto-tick))) - (timer-activate corfu--auto-timer)))) - -(defun corfu--auto-tick () - "Return the current tick/status of the buffer. -Auto completion is only performed if the tick did not change." - (list (selected-window) (current-buffer) (buffer-chars-modified-tick) (point))) - -(cl-defgeneric corfu--popup-show (pos off width lines &optional curr lo bar) - "Show LINES as popup at POS - OFF. -WIDTH is the width of the popup. -The current candidate CURR is highlighted. -A scroll bar is displayed from LO to LO+BAR." - (let ((lh (default-line-height))) - (with-current-buffer (corfu--make-buffer " *corfu*") - (let* ((ch (default-line-height)) - (cw (default-font-width)) - (ml (ceiling (* cw corfu-left-margin-width))) - (mr (ceiling (* cw corfu-right-margin-width))) - (bw (ceiling (min mr (* cw corfu-bar-width)))) - (marginl (and (> ml 0) (propertize " " 'display `(space :width (,ml))))) - (marginr (and (> mr 0) (propertize " " 'display `(space :align-to right)))) - (sbar (when (> bw 0) - (concat (propertize " " 'display `(space :align-to (- right (,mr)))) - (propertize " " 'display `(space :width (,(- mr bw)))) - (propertize " " 'face 'corfu-bar 'display `(space :width (,bw)))))) - (pos (posn-x-y pos)) - (width (+ (* width cw) ml mr)) - ;; XXX HACK: Minimum popup height must be at least 1 line of the - ;; parent frame (gh:minad/corfu#261). - (height (max lh (* (length lines) ch))) - (edge (window-inside-pixel-edges)) - (border (alist-get 'internal-border-width corfu--frame-parameters)) - (x (max 0 (min (+ (car edge) (- (or (car pos) 0) ml (* cw off) border)) - (- (frame-pixel-width) width)))) - (yb (+ (cadr edge) (window-tab-line-height) (or (cdr pos) 0) lh)) - (y (if (> (+ yb (* corfu-count ch) lh lh) (frame-pixel-height)) - (- yb height lh border border) - yb)) - (row 0)) - (with-silent-modifications - (erase-buffer) - (insert (mapconcat (lambda (line) - (let ((str (concat marginl line - (if (and lo (<= lo row (+ lo bar))) - sbar - marginr)))) - (when (eq row curr) - (add-face-text-property - 0 (length str) 'corfu-current 'append str)) - (cl-incf row) - str)) - lines "\n")) - (goto-char (point-min))) - (setq corfu--frame (corfu--make-frame corfu--frame x y - width height (current-buffer))))))) - -(cl-defgeneric corfu--popup-hide () - "Hide Corfu popup." - (corfu--hide-frame corfu--frame)) - -(cl-defgeneric corfu--popup-support-p () - "Return non-nil if child frames are supported." - (display-graphic-p)) - -(cl-defgeneric corfu--insert (status) - "Insert current candidate, exit with STATUS if non-nil." - ;; XXX There is a small bug here, depending on interpretation. - ;; When completing "~/emacs/master/li|/calc" where "|" is the - ;; cursor, then the candidate only includes the prefix - ;; "~/emacs/master/lisp/", but not the suffix "/calc". Default - ;; completion has the same problem when selecting in the - ;; *Completions* buffer. See bug#48356. - (pcase-let* ((`(,beg ,end . ,_) completion-in-region--data) - (str (concat corfu--base (nth corfu--index corfu--candidates)))) - (corfu--replace beg end str) - (corfu--goto -1) ;; Reset selection, completion may continue. - (when status (corfu--done str status nil)) - str)) - -(cl-defgeneric corfu--affixate (cands) - "Annotate CANDS with annotation function." - (let* ((completion-extra-properties (nth 4 completion-in-region--data)) - (dep (plist-get completion-extra-properties :company-deprecated)) - (mf (run-hook-with-args-until-success 'corfu-margin-formatters corfu--metadata))) - (setq cands - (if-let ((aff (or (corfu--metadata-get 'affixation-function) - (plist-get completion-extra-properties :affixation-function)))) - (funcall aff cands) - (if-let ((ann (or (corfu--metadata-get 'annotation-function) - (plist-get completion-extra-properties :annotation-function)))) - (cl-loop for cand in cands collect - (let ((suff (or (funcall ann cand) ""))) - ;; The default completion UI adds the - ;; `completions-annotations' face if no other faces are - ;; present. We use a custom `corfu-annotations' face to - ;; allow further styling which fits better for popups. - (unless (text-property-not-all 0 (length suff) 'face nil suff) - (setq suff (propertize suff 'face 'corfu-annotations))) - (list cand "" suff))) - (cl-loop for cand in cands collect (list cand "" ""))))) - (cl-loop for x in cands for (c . _) = x do - (when mf - (setf (cadr x) (funcall mf c))) - (when (and dep (funcall dep c)) - (setcar x (setq c (substring c))) - (add-face-text-property 0 (length c) 'corfu-deprecated 'append c))) - (cons mf cands))) - -(cl-defgeneric corfu--prepare () - "Insert selected candidate unless command is marked to continue completion." - (when corfu--preview-ov - (delete-overlay corfu--preview-ov) - (setq corfu--preview-ov nil)) - ;; Ensure that state is initialized before next Corfu command - (when (and (symbolp this-command) (string-prefix-p "corfu-" (symbol-name this-command))) - (corfu--update)) - ;; If the next command is not listed in `corfu-continue-commands', insert the - ;; currently selected candidate and bail out of completion. This way you can - ;; continue typing after selecting a candidate. The candidate will be inserted - ;; and your new input will be appended. - (and (corfu--preview-current-p) (eq corfu-preview-current 'insert) - ;; See the comment about `overriding-local-map' in `corfu--post-command'. - (not (or overriding-terminal-local-map - (corfu--match-symbol-p corfu-continue-commands this-command))) - (corfu--insert 'exact))) - -(cl-defgeneric corfu--exhibit (&optional auto) - "Exhibit Corfu UI. -AUTO is non-nil when initializing auto completion." - (pcase-let ((`(,beg ,end ,table ,pred . ,_) completion-in-region--data) - (`(,str . ,pt) (corfu--update 'interruptible))) - (cond - ;; 1) Single exactly matching candidate and no further completion is possible. - ((and (not (equal str "")) - (equal (car corfu--candidates) str) (not (cdr corfu--candidates)) - (not (eq corfu-on-exact-match 'show)) - (or auto corfu-on-exact-match) - (not (consp (completion-try-completion str table pred pt corfu--metadata)))) - ;; Quit directly when initializing auto completion. - (if (or auto (eq corfu-on-exact-match 'quit)) - (corfu-quit) - (corfu--done (car corfu--candidates) 'finished nil))) - ;; 2) There exist candidates => Show candidates popup. - (corfu--candidates - (let ((pos (posn-at-point (+ beg (length corfu--base))))) - (corfu--preview-current beg end) - (corfu--candidates-popup pos))) - ;; 3) No candidates & `corfu-quit-no-match' & initialized => Confirmation popup. - ((pcase-exhaustive corfu-quit-no-match - ('t nil) - ('nil corfu--input) - ('separator (seq-contains-p (car corfu--input) corfu-separator))) - (corfu--popup-show (posn-at-point beg) 0 8 '(#("No match" 0 8 (face italic))))) - ;; 4) No candidates & auto completing or initialized => Quit. - ((or auto corfu--input) (corfu-quit))))) - -(cl-defgeneric corfu--teardown (buffer) - "Tear-down Corfu in BUFFER, which might be dead at this point." - (corfu--popup-hide) - (when corfu--preview-ov (delete-overlay corfu--preview-ov)) - (remove-hook 'post-command-hook #'corfu--post-command) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (remove-hook 'window-selection-change-functions #'corfu--window-change 'local) - (remove-hook 'window-buffer-change-functions #'corfu--window-change 'local) - (remove-hook 'pre-command-hook #'corfu--prepare 'local) - (accept-change-group corfu--change-group))) - (cl-loop for (k . v) in corfu--initial-state do (set k v))) - -(defun corfu-sort-length-alpha (list) - "Sort LIST by length and alphabetically." - (sort list #'corfu--length-string<)) - -(defun corfu-quit () - "Quit Corfu completion." - (interactive) - (completion-in-region-mode -1)) - -(defun corfu-reset () - "Reset Corfu completion. -This command can be executed multiple times by hammering the ESC key. If a -candidate is selected, unselect the candidate. Otherwise reset the input. If -there hasn't been any input, then quit." - (interactive) - (if (/= corfu--index corfu--preselect) - (progn - (corfu--goto -1) - (setq this-command #'corfu-first)) - ;; Cancel all changes and start new change group. - (pcase-let* ((`(,beg ,end . ,_) completion-in-region--data) - (str (buffer-substring-no-properties beg end))) - (cancel-change-group corfu--change-group) - (activate-change-group (setq corfu--change-group (prepare-change-group))) - ;; Quit when resetting, when input did not change. - (when (equal str (buffer-substring-no-properties beg end)) - (corfu-quit))))) - -(defun corfu-insert-separator () - "Insert a separator character, inhibiting quit on completion boundary. -See `corfu-separator' for more details." - (interactive) - (insert corfu-separator)) - -(defun corfu-next (&optional n) - "Go forward N candidates." - (interactive "p") - (let ((index (+ corfu--index (or n 1)))) - (corfu--goto - (cond - ((not corfu-cycle) index) - ((= corfu--total 0) -1) - ((< corfu--preselect 0) (1- (mod (1+ index) (1+ corfu--total)))) - (t (mod index corfu--total)))))) - -(defun corfu-previous (&optional n) - "Go backward N candidates." - (interactive "p") - (corfu-next (- (or n 1)))) - -(defun corfu-scroll-down (&optional n) - "Go back by N pages." - (interactive "p") - (corfu--goto (max 0 (- corfu--index (* (or n 1) corfu-count))))) - -(defun corfu-scroll-up (&optional n) - "Go forward by N pages." - (interactive "p") - (corfu-scroll-down (- (or n 1)))) - -(defun corfu-first () - "Go to first candidate. -If the first candidate is already selected, go to the prompt." - (interactive) - (corfu--goto (if (> corfu--index 0) 0 -1))) - -(defun corfu-last () - "Go to last candidate." - (interactive) - (corfu--goto (1- corfu--total))) - -(defun corfu-prompt-beginning (arg) - "Move to beginning of the prompt line. -If the point is already the beginning of the prompt move to the -beginning of the line. If ARG is not 1 or nil, move backward ARG - 1 -lines first." - (interactive "^p") - (let ((beg (car completion-in-region--data))) - (if (or (not (eq arg 1)) - (and (= corfu--preselect corfu--index) (= (point) beg))) - (move-beginning-of-line arg) - (corfu--goto -1) - (goto-char beg)))) - -(defun corfu-prompt-end (arg) - "Move to end of the prompt line. -If the point is already the end of the prompt move to the end of -the line. If ARG is not 1 or nil, move forward ARG - 1 lines -first." - (interactive "^p") - (let ((end (cadr completion-in-region--data))) - (if (or (not (eq arg 1)) - (and (= corfu--preselect corfu--index) (= (point) end))) - (move-end-of-line arg) - (corfu--goto -1) - (goto-char end)))) - -(defun corfu-complete () - "Complete current input. -If a candidate is selected, insert it. Otherwise invoke -`corfu-expand'. Return non-nil if the input has been expanded." - (interactive) - (if (< corfu--index 0) - (corfu-expand) - ;; Continue completion with selected candidate. Exit with status 'finished - ;; if input is a valid match and no further completion is - ;; possible. Additionally treat completion as finished if at the end of a - ;; boundary, even if other longer candidates would still match, since the - ;; user invoked `corfu-complete' with an explicitly selected candidate! - (pcase-let ((`(,_beg ,_end ,table ,pred . ,_) completion-in-region--data) - (newstr (corfu--insert nil))) - (and (test-completion newstr table pred) - (or (not (consp (completion-try-completion - newstr table pred (length newstr) - (completion-metadata newstr table pred)))) - (equal (completion-boundaries newstr table pred "") '(0 . 0))) - (corfu--done newstr 'finished nil)) - t))) - -(defun corfu-expand () - "Expands the common prefix of all candidates. -If the currently selected candidate is previewed, invoke -`corfu-complete' instead. Expansion relies on the completion -styles via `completion-try-completion'. Return non-nil if the -input has been expanded." - (interactive) - (if (corfu--preview-current-p) - (corfu-complete) - (pcase-let* ((`(,beg ,end ,table ,pred . ,_) completion-in-region--data) - (pt (max 0 (- (point) beg))) - (str (buffer-substring-no-properties beg end)) - (metadata (completion-metadata (substring str 0 pt) table pred))) - (pcase (completion-try-completion str table pred pt metadata) - ('t - (goto-char end) - (corfu--done str 'finished corfu--candidates) - t) - ((and `(,newstr . ,newpt) (guard (not (and (= pt newpt) (equal newstr str))))) - (corfu--replace beg end newstr) - (goto-char (+ beg newpt)) - ;; Exit with status 'finished if input is a valid match - ;; and no further completion is possible. - (and (test-completion newstr table pred) - (not (consp (completion-try-completion - newstr table pred newpt - (completion-metadata (substring newstr 0 newpt) table pred)))) - (corfu--done newstr 'finished corfu--candidates)) - t))))) - -(defun corfu-insert () - "Insert current candidate. -Quit if no candidate is selected." - (interactive) - (if (>= corfu--index 0) - (corfu--insert 'finished) - (corfu-quit))) - -;;;###autoload -(define-minor-mode corfu-mode - "COmpletion in Region FUnction." - :group 'corfu :keymap corfu-mode-map - (cond - (corfu-mode - (and corfu-auto (add-hook 'post-command-hook #'corfu--auto-post-command nil 'local)) - (setq-local completion-in-region-function #'corfu--in-region)) - (t - (remove-hook 'post-command-hook #'corfu--auto-post-command 'local) - (kill-local-variable 'completion-in-region-function)))) - -(defcustom global-corfu-modes t - "List of modes where Corfu should be enabled. -The variable can either be t, nil or a list of t, nil, mode -symbols or elements of the form (not modes). Examples: - - Enable everywhere, except in Org: ((not org-mode) t). - - Enable in programming modes except Python: ((not python-mode) prog-mode). - - Enable only in text modes: (text-mode)." - :type '(choice (const t) (repeat sexp))) - -;;;###autoload -(define-globalized-minor-mode global-corfu-mode - corfu-mode corfu--on - :group 'corfu) - -(defun corfu--on () - "Turn `corfu-mode' on." - (when (and (not (or noninteractive (eq (aref (buffer-name) 0) ?\s))) - ;; TODO backport `easy-mmode--globalized-predicate-p' - (or (eq t global-corfu-modes) - (eq t (cl-loop for p in global-corfu-modes thereis - (pcase-exhaustive p - ('t t) - ('nil 0) - ((pred symbolp) (and (derived-mode-p p) t)) - (`(not . ,m) (and (seq-some #'derived-mode-p m) 0))))))) - (corfu-mode 1))) - -;; Emacs 28: Do not show Corfu commands with M-X -(dolist (sym '(corfu-next corfu-previous corfu-first corfu-last corfu-quit corfu-reset - corfu-complete corfu-insert corfu-scroll-up corfu-scroll-down - corfu-insert-separator corfu-prompt-beginning corfu-prompt-end)) - (put sym 'completion-predicate #'ignore)) - -(defun corfu--capf-wrapper-advice (orig fun which) - "Around advice for `completion--capf-wrapper'. -The ORIG function takes the FUN and WHICH arguments." - (if corfu-mode (corfu--capf-wrapper fun t) (funcall orig fun which))) - -(defun corfu--eldoc-advice () - "Return non-nil if Corfu is currently not active." - (not (and corfu-mode completion-in-region-mode))) - -;; Install advice which fixes `completion--capf-wrapper', such that it respects -;; the completion styles for non-exclusive Capfs. See also the fixme comment in -;; the `completion--capf-wrapper' function in minibuffer.el. -(advice-add #'completion--capf-wrapper :around #'corfu--capf-wrapper-advice) - -;; Register Corfu with ElDoc -(advice-add #'eldoc-display-message-no-interference-p - :before-while #'corfu--eldoc-advice) -(eldoc-add-command #'corfu-complete #'corfu-insert) - -(provide 'corfu) -;;; corfu.el ends here blob - a0c270c659299fd723bb67cfe80fdadf9a5e39a6 (mode 644) blob + /dev/null --- elpa/corfu-1.3/corfu.info +++ /dev/null @@ -1,795 +0,0 @@ -This is docxDqOT0.info, produced by makeinfo version 6.8 from -corfu.texi. - -INFO-DIR-SECTION Emacs misc features -START-INFO-DIR-ENTRY -* Corfu: (corfu). COmpletion in Region FUnction. -END-INFO-DIR-ENTRY - - -File: docxDqOT0.info, Node: Top, Next: Features, Up: (dir) - -corfu.el - COmpletion in Region FUnction -**************************************** - -Corfu enhances in-buffer completion with a small completion popup. The -current candidates are shown in a popup below or above the point. The -candidates can be selected by moving up and down. Corfu is the -minimalistic in-buffer completion counterpart of the Vertico -(https://github.com/minad/vertico) minibuffer UI. - - Corfu is a small package, which relies on the Emacs completion -facilities and concentrates on providing a polished completion UI. -In-buffer completion UIs in Emacs can hook into ‘completion-in-region’, -which implements the interaction with the user. Completions at point -are either provided by commands like ‘dabbrev-completion’ or by -pluggable backends (‘completion-at-point-functions’, Capfs) and are then -passed to ‘completion-in-region’. Many programming, text and shell -major modes implement a Capf. Corfu does not include its own completion -backends. The Emacs built-in Capfs and the Capfs provided by -third-party programming language packages are often sufficient. -Additional Capfs and completion utilities are provided by the Cape -(https://github.com/minad/cape) package. - - *NOTE*: Corfu uses child frames to show the popup and falls back to -the default setting of the ‘completion-in-region-function’ on -non-graphical displays. If you want to use Corfu in the terminal, -install the package corfu-terminal -(https://codeberg.org/akib/emacs-corfu-terminal), which provides an -alternative overlay-based display. - -* Menu: - -* Features:: -* Installation:: -* Key bindings:: -* Configuration:: -* Extensions:: -* Complementary packages:: -* Alternatives:: -* Debugging Corfu:: -* Contributions:: - -— The Detailed Node Listing — - -Configuration - -* Auto completion:: -* Completing in the minibuffer:: -* Completing in the Eshell or Shell:: -* Orderless completion:: -* TAB-only completion:: -* TAB-and-Go completion:: -* Expanding to the common candidate prefix with TAB:: -* Transfer completion to the minibuffer:: - - - -File: docxDqOT0.info, Node: Features, Next: Installation, Prev: Top, Up: Top - -1 Features -********** - - • Timer-based auto-completions (_off_ by default, set ‘corfu-auto’). - • Popup display with scrollbar indicator and arrow key navigation. - • The popup can be summoned explicitly by pressing ‘TAB’ at any time. - • The current candidate is inserted with ‘TAB’ and selected with - ‘RET’. - • Candidate sorting by prefix, string length and alphabetically. - • The selected candidate is previewed (configurable via - ‘corfu-preview-current’). - • The selected candidate is automatically committed on further input - by default. (configurable via ‘corfu-preview-current’). - • Supports the Orderless (https://github.com/oantolin/orderless) - completion style. The filter string can contain arbitrary - characters, after inserting a space via ‘M-SPC’ (configurable via - ‘corfu-quit-at-boundary’ and ‘corfu-separator’). - • Lazy completion candidate highlighting for performance. - • Support for candidate annotations (‘annotation-function’, - ‘affixation-function’). - • Deprecated candidates are displayed as crossed out. - • Icons can be provided by an external package via margin formatter - functions. - • Rich set of extensions: Quick keys, Index keys, Sorting by history, - Candidate documentation in echo area, popup or separate buffer. - - -File: docxDqOT0.info, Node: Installation, Next: Key bindings, Prev: Features, Up: Top - -2 Installation -************** - -Corfu is available from GNU ELPA -(https://elpa.gnu.org/packages/corfu.html). You can install it directly -via ‘M-x package-install RET corfu RET’. After installation, activate -the global minor mode with ‘M-x global-corfu-mode RET’. Set the -variable ‘corfu-auto’ to t in order to enable auto completion. For -manual completion press ‘M-TAB’ (or ‘TAB’) within a buffer. - - -File: docxDqOT0.info, Node: Key bindings, Next: Configuration, Prev: Installation, Up: Top - -3 Key bindings -************** - -Corfu uses a transient keymap ‘corfu-map’ which is active while the -popup is shown. The keymap defines the following remappings of -fundamental commands and bindings: - -Binding/Remapping Corfu command ------------------------------------------------------------- -‘move-beginning-of-line’ ‘corfu-prompt-beginning’ -‘move-end-of-line’ ‘corfu-prompt-end’ -‘beginning-of-buffer’ ‘corfu-first’ -‘end-of-buffer’ ‘corfu-last’ -‘scroll-down-command’ ‘corfu-scroll-down’ -‘scroll-up-command’ ‘corfu-scroll-up’ -‘next-line’, ‘down’, ‘M-n’ ‘corfu-next’ -‘previous-line’, ‘up’, ‘M-p’ ‘corfu-previous’ -‘completion-at-point’, ‘TAB’ ‘corfu-complete’ -‘M-TAB’ ‘corfu-expand’ -‘RET’ ‘corfu-insert’ -‘M-g’ ‘corfu-info-location’ -‘M-h’ ‘corfu-info-documentation’ -‘M-SPC’ ‘corfu-insert-separator’ -‘C-g’ ‘corfu-quit’ -‘keyboard-escape-quit’ ‘corfu-reset’ - - -File: docxDqOT0.info, Node: Configuration, Next: Extensions, Prev: Key bindings, Up: Top - -4 Configuration -*************** - -In order to configure Corfu and other packages in your init.el, you may -want to use ‘use-package’. Corfu is flexibly customizable via ‘corfu-*’ -customization variables, such that you can adapt it precisely to your -requirements. However in order to quickly try out the Corfu completion -package, it should be sufficient to activate ‘global-corfu-mode’. You -can experiment with manual completion for example in an Elisp buffer or -in an Eshell or Shell buffer. For auto completion, set ‘corfu-auto’ to -t before turning on ‘global-corfu-mode’. - - Here is an example configuration: - - (use-package corfu - ;; Optional customizations - ;; :custom - ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - ;; (corfu-auto t) ;; Enable auto completion - ;; (corfu-separator ?\s) ;; Orderless field separator - ;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary - ;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match - ;; (corfu-preview-current nil) ;; Disable current candidate preview - ;; (corfu-preselect 'prompt) ;; Preselect the prompt - ;; (corfu-on-exact-match nil) ;; Configure handling of exact matches - ;; (corfu-scroll-margin 5) ;; Use scroll margin - - ;; Enable Corfu only for certain modes. - ;; :hook ((prog-mode . corfu-mode) - ;; (shell-mode . corfu-mode) - ;; (eshell-mode . corfu-mode)) - - ;; Recommended: Enable Corfu globally. This is recommended since Dabbrev can - ;; be used globally (M-/). See also the customization variable - ;; `global-corfu-modes' to exclude certain modes. - :init - (global-corfu-mode)) - - ;; A few more useful configurations... - (use-package emacs - :init - ;; TAB cycle if there are only few candidates - ;; (setq completion-cycle-threshold 3) - - ;; Enable indentation+completion using the TAB key. - ;; `completion-at-point' is often bound to M-TAB. - (setq tab-always-indent 'complete) - - ;; Emacs 30 and newer: Disable Ispell completion function. As an alternative, - ;; try `cape-dict'. - (setq text-mode-ispell-word-completion nil) - - ;; Emacs 28 and newer: Hide commands in M-x which do not apply to the current - ;; mode. Corfu commands are hidden, since they are not used via M-x. This - ;; setting is useful beyond Corfu. - (setq read-extended-command-predicate #'command-completion-default-include-p)) - - Dabbrev completion is based on ‘completion-in-region’ and can be used -with Corfu. You may want to swap the ‘dabbrev-completion’ with the -‘dabbrev-expand’ key for easier access, if you prefer completion. Also -take a look at the ‘cape-dabbrev’ completion at point function provided -by my Cape (https://github.com/minad/cape) package. - - ;; Use Dabbrev with Corfu! - (use-package dabbrev - ;; Swap M-/ and C-M-/ - :bind (("M-/" . dabbrev-completion) - ("C-M-/" . dabbrev-expand)) - :config - (add-to-list 'dabbrev-ignored-buffer-regexps "\\` ") - ;; Since 29.1, use `dabbrev-ignored-buffer-regexps' on older. - (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode) - (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode)) - - If you start to configure the package more deeply, I recommend to -give the Orderless completion style a try for filtering. Orderless -completion is different from the familiar prefix TAB completion. Corfu -can be used with the default completion styles. The use of Orderless is -not a necessity. - - ;; Optionally use the `orderless' completion style. - (use-package orderless - :init - ;; Configure a custom style dispatcher (see the Consult wiki) - ;; (setq orderless-style-dispatchers '(+orderless-dispatch) - ;; orderless-component-separator #'orderless-escapable-split-on-space) - (setq completion-styles '(orderless basic) - completion-category-defaults nil - completion-category-overrides '((file (styles partial-completion))))) - - The ‘basic’ completion style is specified as fallback in addition to -‘orderless’ in order to ensure that completion commands which rely on -dynamic completion tables, e.g., ‘completion-table-dynamic’ or -‘completion-table-in-turn’, work correctly. See ‘+orderless-dispatch’ -in the Consult wiki (https://github.com/minad/consult/wiki) for an -advanced Orderless style dispatcher. Additionally enable -‘partial-completion’ for file path expansion. ‘partial-completion’ is -important for file wildcard support. Multiple files can be opened at -once with ‘find-file’ if you enter a wildcard. You may also give the -‘initials’ completion style a try. - - See also the Corfu Wiki (https://github.com/minad/corfu/wiki) and the -Cape manual (https://github.com/minad/cape) for additional Capf -configuration tips. For more general documentation read the chapter -about completion in the Emacs manual -(https://www.gnu.org/software/emacs/manual/html_node/emacs/Completion.html). -If you want to create your own Capfs, you can find documentation about -completion in the Elisp manual -(https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion.html). - -* Menu: - -* Auto completion:: -* Completing in the minibuffer:: -* Completing in the Eshell or Shell:: -* Orderless completion:: -* TAB-only completion:: -* TAB-and-Go completion:: -* Expanding to the common candidate prefix with TAB:: -* Transfer completion to the minibuffer:: - - -File: docxDqOT0.info, Node: Auto completion, Next: Completing in the minibuffer, Up: Configuration - -4.1 Auto completion -=================== - -Auto completion is disabled by default, but can be enabled by setting -‘corfu-auto’ to t. Furthermore you may want to configure Corfu to quit -completion eagerly, such that the completion popup stays out of your way -when it appeared unexpectedly. - - ;; Enable auto completion and configure quitting - (setq corfu-auto t - corfu-quit-no-match 'separator) ;; or t - - I suggest to experiment with the various settings and key bindings to -find a configuration which works for you. There is no one perfect -configuration which fits all. Some people like auto completion, some -like manual completion, some want to cycle with TAB and some with the -arrow keys. - - In case you like auto completion settings, where the completion popup -appears immediately, better use a cheap completion style like ‘basic’, -which performs prefix filtering. In this case Corfu completion should -still be fast in buffers with efficient completion backends. You can -try the following settings in an Elisp buffer or the Emacs scratch -buffer. Note that such settings can slow down Emacs due to the high -load on the Lisp runtime and garbage collector. - - (setq-local corfu-auto t - corfu-auto-delay 0 ;; TOO SMALL - NOT RECOMMENDED - corfu-auto-prefix 1 ;; TOO SMALL - NOT RECOMMENDED - completion-styles '(basic)) - - If you want to combine fast prefix filtering and Orderless filtering -you can still do that by defining a custom Orderless completion style -via ‘orderless-define-completion-style’. We use a custom style -dispatcher, which enables efficient prefix filtering for input shorter -than 4 characters. Note that such a setup is advanced. Please refer to -the Orderless documentation and source code for further details. - - (defun orderless-fast-dispatch (word index total) - (and (= index 0) (= total 1) (length< word 4) - (cons 'orderless-literal-prefix word)))) - - (orderless-define-completion-style orderless-fast - (orderless-style-dispatchers '(orderless-fast-dispatch)) - (orderless-matching-styles '(orderless-literal orderless-regexp))) - - (setq-local corfu-auto t - corfu-auto-delay 0 ;; TOO SMALL - NOT RECOMMENDED - corfu-auto-prefix 1 ;; TOO SMALL - NOT RECOMMENDED - completion-styles '(orderless-fast basic)) - - -File: docxDqOT0.info, Node: Completing in the minibuffer, Next: Completing in the Eshell or Shell, Prev: Auto completion, Up: Configuration - -4.2 Completing in the minibuffer -================================ - -Corfu can be used for completion in the minibuffer, since it relies on -child frames to display the candidates. The Corfu popup can be shown -even if it doesn’t fully fit inside the minibuffer. - - By default, ‘global-corfu-mode’ does not activate ‘corfu-mode’ in the -minibuffer, to avoid interference with specialised minibuffer completion -UIs like Vertico or Mct. However you may still want to enable Corfu -completion for commands like ‘M-:’ (‘eval-expression’) or ‘M-!’ -(‘shell-command’), which read from the minibuffer. In order to detect -minibuffers with completion we check if the variable -‘completion-at-point-functions’ is set locally. - - (defun corfu-enable-in-minibuffer () - "Enable Corfu in the minibuffer." - (when (local-variable-p 'completion-at-point-functions) - ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion - (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup - corfu-popupinfo-delay nil) - (corfu-mode 1))) - (add-hook 'minibuffer-setup-hook #'corfu-enable-in-minibuffer) - - This is not recommended, but one can also enable Corfu more generally -for every minibuffer, as long as no completion UI is active. In the -following example we check for Mct and Vertico. Furthermore we ensure -that Corfu is not enabled if a password is read from the minibuffer. - - (defun corfu-enable-always-in-minibuffer () - "Enable Corfu in the minibuffer if Vertico/Mct are not active." - (unless (or (bound-and-true-p mct--active) - (bound-and-true-p vertico--input) - (eq (current-local-map) read-passwd-map)) - ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion - (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup - corfu-popupinfo-delay nil) - (corfu-mode 1))) - (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) - - -File: docxDqOT0.info, Node: Completing in the Eshell or Shell, Next: Orderless completion, Prev: Completing in the minibuffer, Up: Configuration - -4.3 Completing in the Eshell or Shell -===================================== - -When completing in the Eshell I recommend conservative local settings -without auto completion, such that the completion behavior is similar to -widely used shells like Bash, Zsh or Fish. - - (add-hook 'eshell-mode-hook - (lambda () - (setq-local corfu-auto nil) - (corfu-mode))) - - When pressing ‘RET’ while the Corfu popup is visible, the -‘corfu-insert’ command will be invoked. This command does inserts the -currently selected candidate, but it does not send the prompt input to -Eshell or the Comint process. Therefore you often have to press ‘RET’ -twice which feels like an unnecessary double confirmation. Fortunately -it is easy to improve this! In my configuration I define the advice -‘corfu-send-shell’ which sends the candidate after insertion. - - (defun corfu-send-shell (&rest _) - "Send completion candidate when inside comint/eshell." - (cond - ((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input)) - (eshell-send-input)) - ((and (derived-mode-p 'comint-mode) (fboundp 'comint-send-input)) - (comint-send-input)))) - - (advice-add #'corfu-insert :after #'corfu-send-shell) - - Shell completion uses the flexible Pcomplete mechanism internally, -which allows you to program the completions per shell command. If you -want to know more, look into this blog post -(https://www.masteringemacs.org/article/pcomplete-context-sensitive-completion-emacs), -which shows how to configure Pcomplete for git commands. Since Emacs -29, Pcomplete offers the ‘pcomplete-from-help’ function which parses the -‘--help’ output of a command and produces completions for command line -options. - - Pcomplete has a few bugs on Emacs 28 and older. We can work around -the issues with the Cape (https://github.com/minad/cape) library -(Completion at point extensions). Cape provides wrappers which sanitize -the Pcomplete function. If you use Emacs 28 or older installing these -advices is recommended such that Pcomplete works properly. On Emacs 29 -the advices should not be necessary anymore, since most relevant bugs -have been fixed. I therefore recommend to avoid the advices on Emacs 29 -and eventually report any remaining Pcomplete issues upstream. - - ;; The advices are only needed on Emacs 28 and older. - (when (< emacs-major-version 29) - ;; Silence the pcomplete capf. Hide errors or messages. - (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) - - ;; Ensure that pcomplete does not write to the buffer and behaves as a - ;; `completion-at-point-function' without side-effects. - (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)) - - -File: docxDqOT0.info, Node: Orderless completion, Next: TAB-only completion, Prev: Completing in the Eshell or Shell, Up: Configuration - -4.4 Orderless completion -======================== - -Orderless (https://github.com/oantolin/orderless) is an advanced -completion style that supports multi-component search filters separated -by a configurable character (space, by default). Normally, entering -characters like space which lie outside the completion region boundaries -(words, typically) causes Corfu to quit. This behavior is helpful with -auto-completion, which may pop-up when not desired, e.g. on entering a -new variable name. Just keep typing and Corfu will get out of the way. - - But orderless search terms can contain arbitrary characters; they are -also interpreted as regular expressions. To use orderless, set -‘corfu-separator’ (a space, by default) to the primary character of your -orderless component separator. - - Then, when a new orderless component is desired, use ‘M-SPC’ -(‘corfu-insert-separator’) to enter the first component separator in the -input, and arbitrary orderless search terms and new separators can be -entered thereafter. - - To treat the entire input as Orderless input, you can set the -customization option ‘corfu-quit-at-boundary’ to nil. This disables the -predicate which checks if the current completion boundary has been left. -In contrast, if you always want to quit at the boundary, set -‘corfu-quit-at-boundary’ to t. By default ‘corfu-quit-at-boundary’ is -set to ‘separator’ which quits at completion boundaries as long as no -separator has been inserted with ‘corfu-insert-separator’. - - Finally, there exists the user option ‘corfu-quit-no-match’ which is -set to ‘separator’ by default. With this setting Corfu stays alive as -soon as you start advanced filtering with a ‘corfu-separator’ even if -there are no matches, for example due to a typo. As long as no -separator character has been inserted with ‘corfu-insert-separator’, -Corfu will still quit if there are no matches. This ensures that the -Corfu popup goes away quickly if completion is not possible. - - In the following we show two configurations, one which works best -with auto completion and one which may work better with manual -completion if you prefer to always use ‘SPC’ to separate the Orderless -components. - - ;; Auto completion example - (use-package corfu - :custom - (corfu-auto t) ;; Enable auto completion - ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space - :bind - ;; Another key binding can be used, such as S-SPC. - ;; (:map corfu-map ("M-SPC" . corfu-insert-separator)) - :init - (global-corfu-mode)) - - ;; Manual completion example - (use-package corfu - :custom - ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space - :bind - ;; Configure SPC for separator insertion - (:map corfu-map ("SPC" . corfu-insert-separator)) - :init - (global-corfu-mode)) - - -File: docxDqOT0.info, Node: TAB-only completion, Next: TAB-and-Go completion, Prev: Orderless completion, Up: Configuration - -4.5 TAB-only completion -======================= - -By default, Corfu steals both the ‘RET’ and ‘TAB’ keys, when the Corfu -popup is open. This can feel intrusive, in particular in combination -with auto completion. ‘RET’ may accidentally commit an automatically -selected candidate, while you actually wanted to start a new line. As -an alternative we can unbind the ‘RET’ key completely from ‘corfu-map’ -or reserve the ‘RET’ key only in shell modes. - - ;; TAB-only configuration - (use-package corfu - :custom - (corfu-auto t) ;; Enable auto completion - (corfu-preselect 'directory) ;; Select the first candidate, except for directories - - ;; Free the RET key for less intrusive behavior. - :bind - (:map corfu-map - ;; Option 1: Unbind RET completely - ;;; ("RET" . nil) - ;; Option 2: Use RET only in shell modes - ("RET" . (menu-item "" nil :filter corfu-insert-shell-filter))) - - :init - (global-corfu-mode)) - - (defun corfu-insert-shell-filter (&optional _) - "Insert completion candidate and send when inside comint/eshell." - (when (or (derived-mode-p 'eshell-mode) (derived-mode-p 'comint-mode)) - (lambda () - (interactive) - (corfu-insert) - ;; `corfu-send-shell' was defined above - (corfu-send-shell)))) - - -File: docxDqOT0.info, Node: TAB-and-Go completion, Next: Expanding to the common candidate prefix with TAB, Prev: TAB-only completion, Up: Configuration - -4.6 TAB-and-Go completion -========================= - -You may be interested in configuring Corfu in TAB-and-Go style. -Pressing TAB moves to the next candidate and further input will then -commit the selection. Note that further input will not expand snippets -or templates, which may not be desired but which leads overall to a more -predictable behavior. In order to force snippet expansion, confirm a -candidate explicitly with ‘RET’. - - (use-package corfu - ;; TAB-and-Go customizations - :custom - (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' - (corfu-preselect 'prompt) ;; Always preselect the prompt - - ;; Use TAB for cycling, default is `corfu-complete'. - :bind - (:map corfu-map - ("TAB" . corfu-next) - ([tab] . corfu-next) - ("S-TAB" . corfu-previous) - ([backtab] . corfu-previous)) - - :init - (global-corfu-mode)) - - -File: docxDqOT0.info, Node: Expanding to the common candidate prefix with TAB, Next: Transfer completion to the minibuffer, Prev: TAB-and-Go completion, Up: Configuration - -4.7 Expanding to the common candidate prefix with TAB -===================================================== - -If you leave the default configuration of the completion styles, such -that the ‘basic’ completion style is still present, then pressing -‘M-TAB’ (‘corfu-expand’) will expand the current input to the common -prefix of all completion candidates. In contrast, ‘TAB’ -(‘corfu-complete’) behaves differently and expands input to the -currently selected candidate. - - If you use the ‘orderless’ completion style, then expansion works -differently by default. Orderless only expands to single matching -candidates, since due to its multi-component input, there does not -necessarily exist an expansion to a common candidate prefix. However it -is possible to define a separate ‘tab’ completion style. The ‘tab’ -completion style will only take over ‘TAB’ completion (if prefix -expansion is possible), but besides that won’t affect Orderless -candidate filtering. - - (add-to-list 'completion-styles-alist - '(tab completion-basic-try-completion ignore - "Completion style which provides TAB completion only.")) - (setq completion-styles '(tab orderless basic))) - - -File: docxDqOT0.info, Node: Transfer completion to the minibuffer, Prev: Expanding to the common candidate prefix with TAB, Up: Configuration - -4.8 Transfer completion to the minibuffer -========================================= - -Sometimes it is useful to transfer the Corfu completion session to the -minibuffer, since the minibuffer offers richer interaction features. In -particular, Embark (https://github.com/oantolin/embark) is available in -the minibuffer, such that you can act on the candidates or -export/collect the candidates to a separate buffer. We could add Corfu -support to Embark in the future, such that export or collect is possible -directly from Corfu. Nevertheless, the ability to transfer the Corfu -completion to the minibuffer is even more powerful, since further -completion is possible. - - The command ‘corfu-move-to-minibuffer’ is defined here in terms of -‘consult-completion-in-region’, which uses the minibuffer completion UI -via ‘completing-read’. - - (defun corfu-move-to-minibuffer () - (interactive) - (pcase completion-in-region--data - (`(,beg ,end ,table ,pred ,extras) - (let ((completion-extra-properties extras) - completion-cycle-threshold completion-cycling) - (consult-completion-in-region beg end table pred))))) - (keymap-set corfu-map "M-m" #'corfu-move-to-minibuffer) - (add-to-list 'corfu-continue-commands #'corfu-move-to-minibuffer) - - -File: docxDqOT0.info, Node: Extensions, Next: Complementary packages, Prev: Configuration, Up: Top - -5 Extensions -************ - -We maintain small extension packages to Corfu in this repository in the -subdirectory extensions/ -(https://github.com/minad/corfu/tree/main/extensions). The extensions -are installed together with Corfu if you pull the package from ELPA. -The extensions are inactive by default and can be enabled manually if -desired. Furthermore it is possible to install all of the files -separately, both ‘corfu.el’ and the ‘corfu-*.el’ extensions. Currently -the following extensions come with the Corfu ELPA package: - - • corfu-echo - (https://github.com/minad/corfu/blob/main/extensions/corfu-echo.el): - ‘corfu-echo-mode’ displays a brief candidate documentation in the - echo area. - • corfu-history - (https://github.com/minad/corfu/blob/main/extensions/corfu-history.el): - ‘corfu-history-mode’ remembers selected candidates and sorts the - candidates by their history position. - • corfu-indexed - (https://github.com/minad/corfu/blob/main/extensions/corfu-indexed.el): - ‘corfu-indexed-mode’ allows you to select indexed candidates with - prefix arguments. - • corfu-info - (https://github.com/minad/corfu/blob/main/extensions/corfu-info.el): - Actions to access the candidate location and documentation. - • corfu-popupinfo - (https://github.com/minad/corfu/blob/main/extensions/corfu-popupinfo.el): - Display candidate documentation or source in a popup next to the - candidate menu. - • corfu-quick - (https://github.com/minad/corfu/blob/main/extensions/corfu-quick.el): - Commands to select using Avy-style quick keys. - - See the Commentary of those files for configuration details. - - -File: docxDqOT0.info, Node: Complementary packages, Next: Alternatives, Prev: Extensions, Up: Top - -6 Complementary packages -************************ - -Corfu works well together with all packages providing code completion -via the ‘completion-at-point-functions’. Many modes and packages -already provide a Capf out of the box. Nevertheless you may want to -look into complementary packages to enhance your setup. - - • corfu-terminal (https://codeberg.org/akib/emacs-corfu-terminal): - The corfu-terminal package provides an overlay-based display for - Corfu, such that you can use Corfu in terminal Emacs. - - • corfu-candidate-overlay - (https://code.bsdgeek.org/adam/corfu-candidate-overlay): Shows - as-you-type auto-suggestion candidate overlay with a visual - indication of whether there are many or exactly one candidate - available (works only with ‘corfu-auto’ disabled). - - • Orderless (https://github.com/oantolin/orderless): Corfu supports - completion styles, including the advanced ‘orderless’ completion - style, where the filter expressions are separated by spaces or - another character (see ‘corfu-separator’). - - • Cape (https://github.com/minad/cape): Provides additional Capf - backends and ‘completion-in-region’ commands. Among others, the - package supplies the file completion backend ‘cape-file’ and the - Dabbrev backend ‘cape-dabbrev’. Cape provides the - ‘cape-company-to-capf’ adapter to reuse Company backends in Corfu. - - • nerd-icons-corfu (https://github.com/LuigiPiucco/nerd-icons-corfu), - kind-icon (https://github.com/jdtsmith/kind-icon): Icons are - supported by Corfu via external packages. The nerd-icons-corfu - package relies on the Nerd icon font, which is even supported on - terminal, while kind-icon uses SVGs from monochromatic icon sets. - - • Tempel (https://github.com/minad/tempel): Tiny template/snippet - package with templates in Lisp syntax, which can be used in - conjunction with Corfu. - - • Vertico (https://github.com/minad/vertico): You may also want to - look into my Vertico package. Vertico is the minibuffer completion - counterpart of Corfu. - - -File: docxDqOT0.info, Node: Alternatives, Next: Debugging Corfu, Prev: Complementary packages, Up: Top - -7 Alternatives -************** - - • Company (https://github.com/company-mode/company-mode): Company is - a widely used and mature completion package, which implements a - similar UI as Corfu. While Corfu relies exclusively on the - standard Emacs completion API (Capfs), Company defines its own API - for the backends. Company includes its own completion backends, - following its own API, which are incompatible with the Emacs - completion infrastructure. Company provides an adapter - ‘company-capf’ to handle Capfs as a Company backend. As a result - of this design, Company is a more complex package than Corfu. - Company by default uses overlays for the popup in contrast to the - child frames used by Corfu. Overall both packages work well, but - Company integrates less tightly with Emacs. The - ‘completion-styles’ support is more limited and the - ‘completion-at-point’ command and the ‘completion-in-region’ - function do not invoke Company. - - • consult-completion-in-region (https://github.com/minad/consult): - The Consult package provides the function - ‘consult-completion-in-region’ which can be set as - ‘completion-in-region-function’ such that it handles - ‘completion-at-point’. The function works by transferring the - in-buffer completion to the minibuffer. In the minibuffer, the - minibuffer completion UI, for example Vertico - (https://github.com/minad/vertico) takes over. If you prefer to - perform all your completions in the minibuffer - ‘consult-completion-in-region’ is your best option. - - -File: docxDqOT0.info, Node: Debugging Corfu, Next: Contributions, Prev: Alternatives, Up: Top - -8 Debugging Corfu -***************** - -When you observe an error in the ‘corfu--post-command’ post command -hook, you should install an advice to enforce debugging. This allows -you to obtain a stack trace in order to narrow down the location of the -error. The reason is that post command hooks are automatically disabled -(and not debugged) by Emacs. Otherwise Emacs would become unusable, -given that the hooks are executed after every command. - - (setq debug-on-error t) - - (defun force-debug (func &rest args) - (condition-case e - (apply func args) - ((debug error) (signal (car e) (cdr e))))) - - (advice-add #'corfu--post-command :around #'force-debug) - - When Capfs do not yield the expected result you can use -‘cape-capf-debug’ to add debug messages to a Capf. The Capf will then -produce a completion log in the messages buffer. - - (setq completion-at-point-functions (list (cape-capf-debug #'cape-dict))) - - -File: docxDqOT0.info, Node: Contributions, Prev: Debugging Corfu, Up: Top - -9 Contributions -*************** - -Since this package is part of GNU ELPA -(https://elpa.gnu.org/packages/corfu.html) contributions require a -copyright assignment to the FSF. - - - -Tag Table: -Node: Top208 -Node: Features2308 -Node: Installation3769 -Node: Key bindings4284 -Node: Configuration5623 -Node: Auto completion11389 -Node: Completing in the minibuffer13912 -Node: Completing in the Eshell or Shell16114 -Node: Orderless completion19072 -Node: TAB-only completion22149 -Node: TAB-and-Go completion23680 -Node: Expanding to the common candidate prefix with TAB24786 -Node: Transfer completion to the minibuffer26201 -Node: Extensions27654 -Node: Complementary packages29455 -Node: Alternatives31685 -Node: Debugging Corfu33431 -Node: Contributions34487 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: blob - 4beade6613ba6680bb648a016ad4ddea46e1ac7a (mode 644) blob + /dev/null --- elpa/corfu-1.3/dir +++ /dev/null @@ -1,18 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs misc features -* Corfu: (corfu). COmpletion in Region FUnction. blob - e0a16048ff80c31bdf0b67f402ded3d340669dfd (mode 644) blob + /dev/null --- elpa/corfu-1.3.signed +++ /dev/null @@ -1,2 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2024-04-05T11:05:05+0200 using RSA -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2024-04-05T11:05:05+0200 using EDDSA \ No newline at end of file blob - 6ae674232865e7cb37d138e51070e97d6d53f57c (mode 644) blob + /dev/null --- elpa/dash-2.19.1/.dir-locals.el +++ /dev/null @@ -1,14 +0,0 @@ -((nil - (bug-reference-bug-regexp . "\\(\\(?:issue\\|pr\\) ?\\)?#\\([[:digit:]]+\\)") - (bug-reference-url-format . "https://github.com/magnars/dash.el/issues/%s") - (fill-column . 70) - (sentence-end-double-space . t) - (tab-width . 8)) - (emacs-lisp-mode - (indent-tabs-mode . nil) - (mode . bug-reference-prog)) - (sh-mode - (sh-basic-offset . 4)) - (texinfo-mode - (indent-tabs-mode . nil) - (mode . bug-reference-prog))) blob - 8d343580aebf5bdba2dc8bdcdcd96bc8af3cd484 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/.elpaignore +++ /dev/null @@ -1 +0,0 @@ -dev/* blob - ffc79e2fd9e750ec0e4e1d2e674fe19e694230a4 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/.github/workflows/test.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: CI -on: [push, pull_request] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - emacs_version: - - '24.1' - - '24.2' - - '24.3' - - '24.4' - - '24.5' - - '25.1' - - '25.2' - - '25.3' - - '26.1' - - '26.2' - - '26.3' - - '27.1' - - 'snapshot' - include: - - emacs_version: 'snapshot' - allow_failure: true - steps: - - uses: actions/checkout@v2 - - uses: purcell/setup-emacs@master - with: - version: ${{ matrix.emacs_version }} - - - name: Run tests - if: matrix.allow_failure != true - run: 'make check' - - - name: Run tests (allow failure) - if: matrix.allow_failure == true - run: 'make check || true' blob - 4aff8cb682fddb3853750a49d5a93173dfcf7d35 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/Cask +++ /dev/null @@ -1 +0,0 @@ -(package-file "dash.el") blob - f288702d2fa16d3cdf0035b15a9fcbc552cd88e7 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program 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 of the License, or - (at your option) any later version. - - This program 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 this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. blob - 5192cba790fb8966e9a63e3c487b869d9d1c9595 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/Makefile +++ /dev/null @@ -1,72 +0,0 @@ -# Makefile for Dash. - -# Copyright (C) 2021 Free Software Foundation, Inc. -# -# This program 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 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -# Variables. - -EMACS ?= emacs -batch := $(EMACS) -Q -batch -L . -els := dash.el dev/dash-defs.el -elcs := $(addsuffix c,$(els)) -docs := README.md dash.texi -tmpls := readme-template.md dash-template.texi $(wildcard doc/*.texi) - -# Targets. - -lisp: $(elcs) -.PHONY: lisp - -docs: $(docs) -.PHONY: docs - -force-docs: maintainer-clean docs -.PHONY: force-docs - -# ERT_SELECTOR is a Lisp expression determining which tests to run. -# Its format is described in (info "(ert) Test Selectors"). It -# defaults to selecting all tests. Note that in batch mode, a nil -# selector is the same as t. -check: ERT_SELECTOR ?= t -check: run := '(ert-run-tests-batch-and-exit (quote $(ERT_SELECTOR)))' -check: lisp - EMACS_TEST_VERBOSE= $(batch) -l dev/examples.el -eval $(run) -.PHONY: check - -all: lisp docs check -.PHONY: all - -force-all: maintainer-clean lisp docs check -.PHONY: force-all - -clean: - $(RM) $(elcs) -.PHONY: clean - -maintainer-clean: ver := 26 -maintainer-clean: msg := Doc regeneration requires $(ver)+ -maintainer-clean: clean - $(batch) -eval '(if (< emacs-major-version $(ver)) (error "$(msg)"))' - $(RM) $(docs) -.PHONY: maintainer-clean - -# Files. - -%.elc: WERROR := '(setq byte-compile-error-on-warn t)' -%.elc: %.el - $(batch) -eval $(WERROR) -f batch-byte-compile $< - -$(docs) &: dev/examples.el $(elcs) $(tmpls) - $(batch) -l $< -f dash-make-docs blob - 2a813f2843f54791906b98611c0873930a77c426 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/NEWS.md +++ /dev/null @@ -1,302 +0,0 @@ -# Dash NEWS -- history of user-visible changes - -Copyright (C) 2012-2021 Free Software Foundation, Inc. - -See the end of the file for license conditions. - -## Change log - -### From 2.19.0 to 2.19.1 - -#### Fixes - -- Fixed a regression from `2.18` in `-is-suffix-p` which led to false - negatives when parts of the suffix appeared multiple times in the - list being searched (Bennett Rennier, #384). - -### From 2.18.1 to 2.19.0 - -#### Fixes - -- Reverted a breaking change introduced in `2.18.0` that caused the - threading macro `-->` to be indented differently from `->` and `->>` - (#375). -- Added and fixed Edebug specifications for many Dash macros (Philipp - Stephani, #380, #381). - -#### New features - -- The combinators `-on`, `-flip`, `-not`, `-andfn`, and `-orfn` now - return variadic functions that take any number of arguments (#308). -- New combinator `-rotate-args` similar to `-flip`, but for arbitrary - arglist rotations (suggested by @vapniks, #72). -- New function `-every` and its anaphoric macro counterpart `--every`. - They are like the existing `-every-p` and `--every-p`, respectively, - but return the last non-`nil` result instead of just `t`. -- New macro `--partition-after-pred` which affords - `-partition-after-pred` better performance (Per Weijnitz, #362). - -### From 2.18.0 to 2.18.1 - -- Fixed a regression from `2.17` as well as a long-standing bug in - `--iterate`, which evaluated its arguments one too many times. This - in turn could lead to errors in `-flatten-n` when it tried - flattening certain structures too far (#373). - -### From 2.17 to 2.18 - -This release absorbs the now obsolete `dash-functional` version -`1.3.0` into `dash`, and brings the very old version of `dash` on GNU -ELPA up to date. - -Package maintainers should replace all uses of `dash-functional`, -which will eventually be deleted, with `dash` version `2.18.0`. For -more information on this, see: -https://github.com/magnars/dash.el/wiki/Obsoletion-of-dash-functional.el - -- New function `-iota` for generating arithmetic sequences - (@holomorph, #215). - -- Calling `-list` with more than one argument is now deprecated. - -- `-lambda` now accepts an empty argument list. - -- New anaphoric macros `--reductions-from`, `--reductions`, - `--reductions-r-from`, and `--reductions-r` corresponding to the - analogous non-anaphoric functions. - -- `-doto` threading now works as with `->`. - -- New buffer-local minor mode `dash-fontify-mode` and globalized - counterpart `global-dash-fontify-mode` for fontifying special Dash - variables such as `it`, `it-index`, `acc`, etc. The minor mode also - fontifies calls to Dash macros in older Emacs versions which did not - dynamically detect macro calls. - - This obsoletes the user option `dash-enable-fontlock` and the - function `dash-enable-font-lock`, which is now an alias of - `global-dash-fontify-mode`. - -- New command `dash-register-info-lookup` for integration with `C-h S` - (`info-lookup-symbol`). This command allows Dash symbols to be - looked up in the Dash manual just like Elisp symbols are looked up - in the Elisp manual. The command can be called directly when - needed, or automatically from your `user-init-file`. For example: - - ```el - (with-eval-after-load 'info-look - (dash-register-info-lookup)) - ``` - -- Dash is now listed under the standard [Customization - groups](https://gnu.org/software/emacs/manual/html_node/emacs/Customization-Groups.html) - and [Finder - keywords](https://gnu.org/software/emacs/manual/html_node/emacs/Package-Keywords.html) - `extensions` and `lisp`. - -- The Dash manual is now licensed under the GNU Free Documentation - License version 1.3. - -- Various other bug fix, performance, byte-compilation, and - documentation improvements. - -### From 2.16 to 2.17 - -- Sped up `-uniq` by using hash-tables when possible (@cireu, #305). -- Fixed `-inits` to be non-destructive (@SwiftLawnGnome, #313). -- Fixed indent rules for `-some->` and family (@wbolster, #321). -- Added `-zip-lists` which always returns a list of proper lists, even for two - input lists (see issue #135). - -### From 2.15 to 2.16 - -- Added `--doto`, anaphoric version of `-doto` (#282). -- Aliased `-cons-pair-p` to `-cons-pair?` (#288). -- Generalized `-rotate` for `|N|` greater than the length of the list (@leungbk, - #290). -- Added a mechanism to extend destructuring with custom matchers (@yyoncho, - #277). - -### From 2.14 to 2.15 - -This release brings new destructuring features, some new control flow -functions and performance optimizations. - -- Added `-setq` with destructuring binding support similar to the `-let` family - (#116). -- Added smarter key destructuring in `-let` and friends where variables are - auto-derived from keys (#111). -- Allowed `-let` bindings without a source value form (#256). -- Added `-each-r` and `-each-r-while` (@doublep, #159). -- Added `-common-suffix` (@basil-conto, #263). -- Improved performance of folds (`-reduce` and friends) (@basil-conto, #264). - -### From 2.13 to 2.14 - -This release retired Emacs 23 support. - -- Added Edebug support for threading macros (@Wilfred). -- Added `-unzip`. -- Added support for `-first-item` and `-last-item` as [place - forms](https://gnu.org/software/emacs/manual/html_node/elisp/Generalized-Variables.html). -- Added `-powerset` and `-permutations` (@holomorph). -- Added `-as->` for threading a named variable (@zck). -- Added `-partition-after-pred`, `-partition-before-pred`, - `-partition-after-item`, and `-partition-before-item` (@zck). -- Fixed a bug in `-any-p` and friends testing for `null` on lists containing - `nil` (#239). -- Fixed infinite loop bug in `-zip` and `-interleave` when called with empty - input. -- Added `-second-item` through `-fifth-item` as alternatives to `nth` - (@Wilfred). -- Added `-tails` and `-inits`. -- Added `-running-sum` and `-running-product`. -- Added the `-reductions[-r][-from]` family of functions (like `-reduce` but - collecting intermediate results). -- Added `-common-prefix` (@basil-conto). - -### From 2.12 to 2.13 - -- `-let` now supports `&alist` destructuring. -- Various performance improvements. -- `-zip` might change in a future release to always return a list of proper - lists. Added `-zip-pair` for users who explicitly want the old behavior. -- Enabled lexical binding in `dash.el` for Emacs versions 24 or newer (#130). -- Added `-select-column` and `-select-columns`. -- Fixed `-map-last` and `--remove-last` to be non-destructive (#158). -- Added `-each-indexed` and `--each-indexed`. -- Added `-take-last` and `-drop-last`. -- Added the `-doto` macro. -- `-cut <>` is now treated as a function, consistent with [SRFI - 26](https://srfi.schemers.org/srfi-26/srfi-26.html) (#185). - -### From 2.11 to 2.12 - -- Added GNU ELPA support (Phillip Lord). -- Added `-some->`, `-some->>`, and `-some-->` macros (Cam Saul). -- `-is-suffix?` is now non-destructive. -- Faster hash table implementation for `-union`. -- Improvements to docstrings and examples. - -### From 2.10 to 2.11 - -- Lots of clean up w.r.t. byte compilation, debug macros, and tests. - -### From 2.9 to 2.10 - -- Added `-let` destructuring to `-if-let` and `-when-let` (Fredrik Bergroth). - -### From 2.8 to 2.9 - -- Added `-let`, `-let*`, and `-lambda` with destructuring. -- Added `-tree-seq` and `-tree-map-nodes`. -- Added `-non-nil`. -- Added `-fix`. -- Added `-fixfn` (`dash-functional` version `1.2`). -- Added `-copy` (Wilfred Hughes). - -### From 2.7 to 2.8 - -- Added `-butlast`. - -### From 2.6 to 2.7 - -- `-zip` now supports more than two lists (Steve Lamb). -- Added `-cycle`, `-pad`, `-annotate`, and `-zip-fill` (Steve Lamb). -- Added `-table`, `-table-flat` (finite Cartesian product). -- Added `-flatten-n`. -- `-slice` now supports a "step" argument. -- Added functional combinators `-iteratefn` and `-prodfn`. -- Added `-replace`, `-splice`, and `-splice-list` which generalize `-replace-at` - and `-insert-at`. -- Added `-compose`, `-iteratefn`, and `-prodfn` (`dash-functional` version - `1.1`). - -### From 2.5 to 2.6 - -- Added `-is-prefix-p`, `-is-suffix-p`, and `-is-infix-p` (Matus Goljer). -- Added `-iterate` and `-unfold` (Matus Goljer). -- Added `-split-on` and `-split-when` (Matus Goljer). -- Added `-find-last-index` (Matus Goljer). -- Added `-list` (Johan Andersson). - -### From 2.4 to 2.5 - -- Added `-same-items?` (Johan Andersson). -- Various bugfixes. - -### From 2.3 to 2.4 - -- Added `-snoc` (Matus Goljer). -- Added `-replace-at`, `-update-at`, `-remove-at`, and `-remove-at-indices` - (Matus Goljer). - -### From 2.2 to 2.3 - -- Added tree operations (Matus Goljer). -- Made Font Lock optional. - -### From 2.1 to 2.2 - -- Added `-compose` (Christina Whyte). - -### From 2.0 to 2.1 - -- Added indexing operations (Matus Goljer). - -### From 1.8 to 2.0 - -- Split out `dash-functional.el` (Matus Goljer). -- Added `-andfn`, `-orfn`, `-not`, `-cut`, `-const`, `-flip`, and `-on` (Matus - Goljer). -- Fixed `-min`, `-max`, `-min-by`, and `-max-by` (Matus Goljer). - -### From 1.7 to 1.8 - -- Added `-first-item` and `-last-item` (Wilfred Hughes). - -### From 1.6 to 1.7 - -- Added `-rotate` (Matus Goljer). - -### From 1.5 to 1.6 - -- Added `-min`, `-max`, `-min-by`, and `-max-by` (Johan Andersson). - -### From 1.4 to 1.5 - -- Added `-sum` and `-product` (Johan Andersson). - -### From 1.3 to 1.4 - -- Added `-sort`. -- Added `-reduce-r` (Matus Goljer). -- Added `-reduce-r-from` (Matus Goljer). - -### From 1.2 to 1.3 - -- Added `-partition-in-steps`. -- Added `-partition-all-in-steps`. - -### From 1.1 to 1.2 - -- Added `-last` (Matus Goljer). -- Added `-insert-at` (Emanuel Evans). -- Added `-when-let` and `-if-let` (Emanuel Evans). -- Added `-when-let*` and `-if-let*` (Emanuel Evans). -- Various bugfixes. - -## License - -This program 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 of the License, or -(at your option) any later version. - -This program 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 this program. If not, see . blob - 08126c355d1183a7f06da00ff442b1838af0df65 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/README.md +++ /dev/null @@ -1,3182 +0,0 @@ -[![CI](https://github.com/magnars/dash.el/actions/workflows/test.yml/badge.svg)](https://github.com/magnars/dash.el/actions/workflows/test.yml) -[![GNU ELPA](https://elpa.gnu.org/packages/dash.svg)](https://elpa.gnu.org/packages/dash.html) -[![GNU-devel ELPA](https://elpa.gnu.org/devel/dash.svg)](https://elpa.gnu.org/devel/dash.html) -[![MELPA Stable](https://stable.melpa.org/packages/dash-badge.svg)](https://stable.melpa.org/#/dash) -[![MELPA](https://melpa.org/packages/dash-badge.svg)](https://melpa.org/#/dash) - -# dash.el - -A modern list API for Emacs. No -[`'cl`](https://gnu.org/software/emacs/manual/html_node/cl/) required. - -See the end of the file for license conditions. - -## Contents - -* [Change log](#change-log) - * [Upcoming breaking change!](#upcoming-breaking-change) -* [Installation](#installation) -* [Functions](#functions) -* [Contribute](#contribute) -* [Contributors](#contributors) -* [License](#license) - -## Change log - -See the [`NEWS.md`](NEWS.md) file. - -### Upcoming breaking change! - -- For backward compatibility reasons, `-zip` when called with two - lists returns a list of cons cells, rather than a list of proper - lists. This is a clunky API, and may be changed in a future release - to always return a list of proper lists, as `-zip-lists` currently - does. - - **N.B.:** Do not rely on the current behavior of `-zip` for two - lists. Instead, use `-zip-pair` for a list of cons cells, and - `-zip-lists` for a list of proper lists. - -## Installation - -Dash is available on [GNU ELPA](https://elpa.gnu.org/), [GNU-devel -ELPA](https://elpa.gnu.org/devel/), and [MELPA](https://melpa.org/), -and can be installed with the standard command `package-install`: - - M-x package-install RET dash RET - -See [`(info "(emacs) Package -Installation")`](https://gnu.org/software/emacs/manual/html_node/emacs/Package-Installation.html). - -Alternatively, you can just dump `dash.el` in your `load-path` -somewhere. See [`(info "(emacs) Lisp -Libraries")`](https://gnu.org/software/emacs/manual/html_node/emacs/Lisp-Libraries.html). - -### Using in a package - -Add something like this to the library's headers: - - ;; Package-Requires: ((dash "2.19.1")) - -See [`(info "(elisp) Library -Headers")`](https://gnu.org/software/emacs/manual/html_node/elisp/Library-Headers.html). - -### Fontification of special variables - -Font lock of special Dash variables (`it`, `acc`, etc.) in Emacs Lisp -buffers can optionally be enabled with the autoloaded minor mode -`dash-fontify-mode`. In older Emacs versions which do not dynamically -detect macros, the minor mode also fontifies Dash macro calls. - -To automatically enable the minor mode in all Emacs Lisp buffers, just -call its autoloaded global counterpart `global-dash-fontify-mode`, -either interactively or from your `user-init-file`: - -```el -(global-dash-fontify-mode) -``` - -### Info symbol lookup - -While editing Elisp files, you can use `C-h S` (`info-lookup-symbol`) -to look up Elisp symbols in the relevant Info manuals (see [`(emacs) -Info -Lookup`](https://gnu.org/software/emacs/manual/html_node/emacs/Info-Lookup.html)). -To enable the same for Dash symbols, use the command -`dash-register-info-lookup`. It can be called directly when needed, -or automatically from your `user-init-file`. For example: - -```el -(with-eval-after-load 'info-look - (dash-register-info-lookup)) -``` - -## Functions - -All functions and constructs in the library use a dash (`-`) prefix. - -The library also provides anaphoric macro versions of functions where -that makes sense. The names of these macros are prefixed with two -dashes (`--`) instead of one. - -While `-map` applies a function to each element of a list, its -anaphoric counterpart `--map` evaluates a form with the local variable -`it` temporarily bound to the current list element instead. For -example: - -```el -(-map (lambda (n) (* n n)) '(1 2 3 4)) ; Normal version. -(--map (* it it) '(1 2 3 4)) ; Anaphoric version. -``` - -The normal version can of course also be written as follows: - -```el -(defun my-square (n) - "Return N multiplied by itself." - (* n n)) - -(-map #'my-square '(1 2 3 4)) -``` - -This demonstrates the utility of both versions. - -### Maps - -Functions in this category take a transforming function, which -is then applied sequentially to each or selected elements of the -input list. The results are collected in order and returned as a -new list. - -* [`-map`](#-map-fn-list) `(fn list)` -* [`-map-when`](#-map-when-pred-rep-list) `(pred rep list)` -* [`-map-first`](#-map-first-pred-rep-list) `(pred rep list)` -* [`-map-last`](#-map-last-pred-rep-list) `(pred rep list)` -* [`-map-indexed`](#-map-indexed-fn-list) `(fn list)` -* [`-annotate`](#-annotate-fn-list) `(fn list)` -* [`-splice`](#-splice-pred-fun-list) `(pred fun list)` -* [`-splice-list`](#-splice-list-pred-new-list-list) `(pred new-list list)` -* [`-mapcat`](#-mapcat-fn-list) `(fn list)` -* [`-copy`](#-copy-list) `(list)` - -### Sublist selection - -Functions returning a sublist of the original list. - -* [`-filter`](#-filter-pred-list) `(pred list)` -* [`-remove`](#-remove-pred-list) `(pred list)` -* [`-remove-first`](#-remove-first-pred-list) `(pred list)` -* [`-remove-last`](#-remove-last-pred-list) `(pred list)` -* [`-remove-item`](#-remove-item-item-list) `(item list)` -* [`-non-nil`](#-non-nil-list) `(list)` -* [`-slice`](#-slice-list-from-optional-to-step) `(list from &optional to step)` -* [`-take`](#-take-n-list) `(n list)` -* [`-take-last`](#-take-last-n-list) `(n list)` -* [`-drop`](#-drop-n-list) `(n list)` -* [`-drop-last`](#-drop-last-n-list) `(n list)` -* [`-take-while`](#-take-while-pred-list) `(pred list)` -* [`-drop-while`](#-drop-while-pred-list) `(pred list)` -* [`-select-by-indices`](#-select-by-indices-indices-list) `(indices list)` -* [`-select-columns`](#-select-columns-columns-table) `(columns table)` -* [`-select-column`](#-select-column-column-table) `(column table)` - -### List to list - -Functions returning a modified copy of the input list. - -* [`-keep`](#-keep-fn-list) `(fn list)` -* [`-concat`](#-concat-rest-lists) `(&rest lists)` -* [`-flatten`](#-flatten-l) `(l)` -* [`-flatten-n`](#-flatten-n-num-list) `(num list)` -* [`-replace`](#-replace-old-new-list) `(old new list)` -* [`-replace-first`](#-replace-first-old-new-list) `(old new list)` -* [`-replace-last`](#-replace-last-old-new-list) `(old new list)` -* [`-insert-at`](#-insert-at-n-x-list) `(n x list)` -* [`-replace-at`](#-replace-at-n-x-list) `(n x list)` -* [`-update-at`](#-update-at-n-func-list) `(n func list)` -* [`-remove-at`](#-remove-at-n-list) `(n list)` -* [`-remove-at-indices`](#-remove-at-indices-indices-list) `(indices list)` - -### Reductions - -Functions reducing lists to a single value (which may also be a list). - -* [`-reduce-from`](#-reduce-from-fn-init-list) `(fn init list)` -* [`-reduce-r-from`](#-reduce-r-from-fn-init-list) `(fn init list)` -* [`-reduce`](#-reduce-fn-list) `(fn list)` -* [`-reduce-r`](#-reduce-r-fn-list) `(fn list)` -* [`-reductions-from`](#-reductions-from-fn-init-list) `(fn init list)` -* [`-reductions-r-from`](#-reductions-r-from-fn-init-list) `(fn init list)` -* [`-reductions`](#-reductions-fn-list) `(fn list)` -* [`-reductions-r`](#-reductions-r-fn-list) `(fn list)` -* [`-count`](#-count-pred-list) `(pred list)` -* [`-sum`](#-sum-list) `(list)` -* [`-running-sum`](#-running-sum-list) `(list)` -* [`-product`](#-product-list) `(list)` -* [`-running-product`](#-running-product-list) `(list)` -* [`-inits`](#-inits-list) `(list)` -* [`-tails`](#-tails-list) `(list)` -* [`-common-prefix`](#-common-prefix-rest-lists) `(&rest lists)` -* [`-common-suffix`](#-common-suffix-rest-lists) `(&rest lists)` -* [`-min`](#-min-list) `(list)` -* [`-min-by`](#-min-by-comparator-list) `(comparator list)` -* [`-max`](#-max-list) `(list)` -* [`-max-by`](#-max-by-comparator-list) `(comparator list)` - -### Unfolding - -Operations dual to reductions, building lists from a seed -value rather than consuming a list to produce a single value. - -* [`-iterate`](#-iterate-fun-init-n) `(fun init n)` -* [`-unfold`](#-unfold-fun-seed) `(fun seed)` - -### Predicates - -Reductions of one or more lists to a boolean value. - -* [`-some`](#-some-pred-list) `(pred list)` -* [`-every`](#-every-pred-list) `(pred list)` -* [`-any?`](#-any-pred-list) `(pred list)` -* [`-all?`](#-all-pred-list) `(pred list)` -* [`-none?`](#-none-pred-list) `(pred list)` -* [`-only-some?`](#-only-some-pred-list) `(pred list)` -* [`-contains?`](#-contains-list-element) `(list element)` -* [`-same-items?`](#-same-items-list-list2) `(list list2)` -* [`-is-prefix?`](#-is-prefix-prefix-list) `(prefix list)` -* [`-is-suffix?`](#-is-suffix-suffix-list) `(suffix list)` -* [`-is-infix?`](#-is-infix-infix-list) `(infix list)` -* [`-cons-pair?`](#-cons-pair-obj) `(obj)` - -### Partitioning - -Functions partitioning the input list into a list of lists. - -* [`-split-at`](#-split-at-n-list) `(n list)` -* [`-split-with`](#-split-with-pred-list) `(pred list)` -* [`-split-on`](#-split-on-item-list) `(item list)` -* [`-split-when`](#-split-when-fn-list) `(fn list)` -* [`-separate`](#-separate-pred-list) `(pred list)` -* [`-partition`](#-partition-n-list) `(n list)` -* [`-partition-all`](#-partition-all-n-list) `(n list)` -* [`-partition-in-steps`](#-partition-in-steps-n-step-list) `(n step list)` -* [`-partition-all-in-steps`](#-partition-all-in-steps-n-step-list) `(n step list)` -* [`-partition-by`](#-partition-by-fn-list) `(fn list)` -* [`-partition-by-header`](#-partition-by-header-fn-list) `(fn list)` -* [`-partition-after-pred`](#-partition-after-pred-pred-list) `(pred list)` -* [`-partition-before-pred`](#-partition-before-pred-pred-list) `(pred list)` -* [`-partition-before-item`](#-partition-before-item-item-list) `(item list)` -* [`-partition-after-item`](#-partition-after-item-item-list) `(item list)` -* [`-group-by`](#-group-by-fn-list) `(fn list)` - -### Indexing - -Functions retrieving or sorting based on list indices and -related predicates. - -* [`-elem-index`](#-elem-index-elem-list) `(elem list)` -* [`-elem-indices`](#-elem-indices-elem-list) `(elem list)` -* [`-find-index`](#-find-index-pred-list) `(pred list)` -* [`-find-last-index`](#-find-last-index-pred-list) `(pred list)` -* [`-find-indices`](#-find-indices-pred-list) `(pred list)` -* [`-grade-up`](#-grade-up-comparator-list) `(comparator list)` -* [`-grade-down`](#-grade-down-comparator-list) `(comparator list)` - -### Set operations - -Operations pretending lists are sets. - -* [`-union`](#-union-list-list2) `(list list2)` -* [`-difference`](#-difference-list-list2) `(list list2)` -* [`-intersection`](#-intersection-list-list2) `(list list2)` -* [`-powerset`](#-powerset-list) `(list)` -* [`-permutations`](#-permutations-list) `(list)` -* [`-distinct`](#-distinct-list) `(list)` - -### Other list operations - -Other list functions not fit to be classified elsewhere. - -* [`-rotate`](#-rotate-n-list) `(n list)` -* [`-repeat`](#-repeat-n-x) `(n x)` -* [`-cons*`](#-cons-rest-args) `(&rest args)` -* [`-snoc`](#-snoc-list-elem-rest-elements) `(list elem &rest elements)` -* [`-interpose`](#-interpose-sep-list) `(sep list)` -* [`-interleave`](#-interleave-rest-lists) `(&rest lists)` -* [`-iota`](#-iota-count-optional-start-step) `(count &optional start step)` -* [`-zip-with`](#-zip-with-fn-list1-list2) `(fn list1 list2)` -* [`-zip`](#-zip-rest-lists) `(&rest lists)` -* [`-zip-lists`](#-zip-lists-rest-lists) `(&rest lists)` -* [`-zip-fill`](#-zip-fill-fill-value-rest-lists) `(fill-value &rest lists)` -* [`-unzip`](#-unzip-lists) `(lists)` -* [`-cycle`](#-cycle-list) `(list)` -* [`-pad`](#-pad-fill-value-rest-lists) `(fill-value &rest lists)` -* [`-table`](#-table-fn-rest-lists) `(fn &rest lists)` -* [`-table-flat`](#-table-flat-fn-rest-lists) `(fn &rest lists)` -* [`-first`](#-first-pred-list) `(pred list)` -* [`-last`](#-last-pred-list) `(pred list)` -* [`-first-item`](#-first-item-list) `(list)` -* [`-second-item`](#-second-item-list) `(list)` -* [`-third-item`](#-third-item-list) `(list)` -* [`-fourth-item`](#-fourth-item-list) `(list)` -* [`-fifth-item`](#-fifth-item-list) `(list)` -* [`-last-item`](#-last-item-list) `(list)` -* [`-butlast`](#-butlast-list) `(list)` -* [`-sort`](#-sort-comparator-list) `(comparator list)` -* [`-list`](#-list-arg) `(arg)` -* [`-fix`](#-fix-fn-list) `(fn list)` - -### Tree operations - -Functions pretending lists are trees. - -* [`-tree-seq`](#-tree-seq-branch-children-tree) `(branch children tree)` -* [`-tree-map`](#-tree-map-fn-tree) `(fn tree)` -* [`-tree-map-nodes`](#-tree-map-nodes-pred-fun-tree) `(pred fun tree)` -* [`-tree-reduce`](#-tree-reduce-fn-tree) `(fn tree)` -* [`-tree-reduce-from`](#-tree-reduce-from-fn-init-value-tree) `(fn init-value tree)` -* [`-tree-mapreduce`](#-tree-mapreduce-fn-folder-tree) `(fn folder tree)` -* [`-tree-mapreduce-from`](#-tree-mapreduce-from-fn-folder-init-value-tree) `(fn folder init-value tree)` -* [`-clone`](#-clone-list) `(list)` - -### Threading macros - -Macros that conditionally combine sequential forms for brevity -or readability. - -* [`->`](#--x-optional-form-rest-more) `(x &optional form &rest more)` -* [`->>`](#--x-optional-form-rest-more) `(x &optional form &rest more)` -* [`-->`](#---x-rest-forms) `(x &rest forms)` -* [`-as->`](#-as--value-variable-rest-forms) `(value variable &rest forms)` -* [`-some->`](#-some--x-optional-form-rest-more) `(x &optional form &rest more)` -* [`-some->>`](#-some--x-optional-form-rest-more) `(x &optional form &rest more)` -* [`-some-->`](#-some---expr-rest-forms) `(expr &rest forms)` -* [`-doto`](#-doto-init-rest-forms) `(init &rest forms)` - -### Binding - -Macros that combine `let` and `let*` with destructuring and flow control. - -* [`-when-let`](#-when-let-var-val-rest-body) `((var val) &rest body)` -* [`-when-let*`](#-when-let-vars-vals-rest-body) `(vars-vals &rest body)` -* [`-if-let`](#-if-let-var-val-then-rest-else) `((var val) then &rest else)` -* [`-if-let*`](#-if-let-vars-vals-then-rest-else) `(vars-vals then &rest else)` -* [`-let`](#-let-varlist-rest-body) `(varlist &rest body)` -* [`-let*`](#-let-varlist-rest-body) `(varlist &rest body)` -* [`-lambda`](#-lambda-match-form-rest-body) `(match-form &rest body)` -* [`-setq`](#-setq-match-form-val) `([match-form val] ...)` - -### Side effects - -Functions iterating over lists for side effect only. - -* [`-each`](#-each-list-fn) `(list fn)` -* [`-each-while`](#-each-while-list-pred-fn) `(list pred fn)` -* [`-each-indexed`](#-each-indexed-list-fn) `(list fn)` -* [`-each-r`](#-each-r-list-fn) `(list fn)` -* [`-each-r-while`](#-each-r-while-list-pred-fn) `(list pred fn)` -* [`-dotimes`](#-dotimes-num-fn) `(num fn)` - -### Destructive operations - -Macros that modify variables holding lists. - -* [`!cons`](#cons-car-cdr) `(car cdr)` -* [`!cdr`](#cdr-list) `(list)` - -### Function combinators - -Functions that manipulate and compose other functions. - -* [`-partial`](#-partial-fun-rest-args) `(fun &rest args)` -* [`-rpartial`](#-rpartial-fn-rest-args) `(fn &rest args)` -* [`-juxt`](#-juxt-rest-fns) `(&rest fns)` -* [`-compose`](#-compose-rest-fns) `(&rest fns)` -* [`-applify`](#-applify-fn) `(fn)` -* [`-on`](#-on-op-trans) `(op trans)` -* [`-flip`](#-flip-fn) `(fn)` -* [`-rotate-args`](#-rotate-args-n-fn) `(n fn)` -* [`-const`](#-const-c) `(c)` -* [`-cut`](#-cut-rest-params) `(&rest params)` -* [`-not`](#-not-pred) `(pred)` -* [`-orfn`](#-orfn-rest-preds) `(&rest preds)` -* [`-andfn`](#-andfn-rest-preds) `(&rest preds)` -* [`-iteratefn`](#-iteratefn-fn-n) `(fn n)` -* [`-fixfn`](#-fixfn-fn-optional-equal-test-halt-test) `(fn &optional equal-test halt-test)` -* [`-prodfn`](#-prodfn-rest-fns) `(&rest fns)` - -## Maps - -Functions in this category take a transforming function, which -is then applied sequentially to each or selected elements of the -input list. The results are collected in order and returned as a -new list. - -#### -map `(fn list)` - -Apply `fn` to each item in `list` and return the list of results. - -This function's anaphoric counterpart is `--map`. - -```el -(-map (lambda (num) (* num num)) '(1 2 3 4)) ;; => (1 4 9 16) -(-map #'1+ '(1 2 3 4)) ;; => (2 3 4 5) -(--map (* it it) '(1 2 3 4)) ;; => (1 4 9 16) -``` - -#### -map-when `(pred rep list)` - -Return a new list where the elements in `list` that do not match the `pred` function -are unchanged, and where the elements in `list` that do match the `pred` function are mapped -through the `rep` function. - -Alias: `-replace-where` - -See also: [`-update-at`](#-update-at-n-func-list) - -```el -(-map-when 'even? 'square '(1 2 3 4)) ;; => (1 4 3 16) -(--map-when (> it 2) (* it it) '(1 2 3 4)) ;; => (1 2 9 16) -(--map-when (= it 2) 17 '(1 2 3 4)) ;; => (1 17 3 4) -``` - -#### -map-first `(pred rep list)` - -Replace first item in `list` satisfying `pred` with result of `rep` called on this item. - -See also: [`-map-when`](#-map-when-pred-rep-list), [`-replace-first`](#-replace-first-old-new-list) - -```el -(-map-first 'even? 'square '(1 2 3 4)) ;; => (1 4 3 4) -(--map-first (> it 2) (* it it) '(1 2 3 4)) ;; => (1 2 9 4) -(--map-first (= it 2) 17 '(1 2 3 2)) ;; => (1 17 3 2) -``` - -#### -map-last `(pred rep list)` - -Replace last item in `list` satisfying `pred` with result of `rep` called on this item. - -See also: [`-map-when`](#-map-when-pred-rep-list), [`-replace-last`](#-replace-last-old-new-list) - -```el -(-map-last 'even? 'square '(1 2 3 4)) ;; => (1 2 3 16) -(--map-last (> it 2) (* it it) '(1 2 3 4)) ;; => (1 2 3 16) -(--map-last (= it 2) 17 '(1 2 3 2)) ;; => (1 2 3 17) -``` - -#### -map-indexed `(fn list)` - -Apply `fn` to each index and item in `list` and return the list of results. -This is like [`-map`](#-map-fn-list), but `fn` takes two arguments: the index of the -current element within `list`, and the element itself. - -This function's anaphoric counterpart is `--map-indexed`. - -For a side-effecting variant, see also [`-each-indexed`](#-each-indexed-list-fn). - -```el -(-map-indexed (lambda (index item) (- item index)) '(1 2 3 4)) ;; => (1 1 1 1) -(--map-indexed (- it it-index) '(1 2 3 4)) ;; => (1 1 1 1) -(-map-indexed #'* '(1 2 3 4)) ;; => (0 2 6 12) -``` - -#### -annotate `(fn list)` - -Return a list of cons cells where each cell is `fn` applied to each -element of `list` paired with the unmodified element of `list`. - -```el -(-annotate '1+ '(1 2 3)) ;; => ((2 . 1) (3 . 2) (4 . 3)) -(-annotate 'length '(("h" "e" "l" "l" "o") ("hello" "world"))) ;; => ((5 "h" "e" "l" "l" "o") (2 "hello" "world")) -(--annotate (< 1 it) '(0 1 2 3)) ;; => ((nil . 0) (nil . 1) (t . 2) (t . 3)) -``` - -#### -splice `(pred fun list)` - -Splice lists generated by `fun` in place of elements matching `pred` in `list`. - -`fun` takes the element matching `pred` as input. - -This function can be used as replacement for `,@` in case you -need to splice several lists at marked positions (for example -with keywords). - -See also: [`-splice-list`](#-splice-list-pred-new-list-list), [`-insert-at`](#-insert-at-n-x-list) - -```el -(-splice 'even? (lambda (x) (list x x)) '(1 2 3 4)) ;; => (1 2 2 3 4 4) -(--splice 't (list it it) '(1 2 3 4)) ;; => (1 1 2 2 3 3 4 4) -(--splice (equal it :magic) '((list of) (magical) (code)) '((foo) (bar) :magic (baz))) ;; => ((foo) (bar) (list of) (magical) (code) (baz)) -``` - -#### -splice-list `(pred new-list list)` - -Splice `new-list` in place of elements matching `pred` in `list`. - -See also: [`-splice`](#-splice-pred-fun-list), [`-insert-at`](#-insert-at-n-x-list) - -```el -(-splice-list 'keywordp '(a b c) '(1 :foo 2)) ;; => (1 a b c 2) -(-splice-list 'keywordp nil '(1 :foo 2)) ;; => (1 2) -(--splice-list (keywordp it) '(a b c) '(1 :foo 2)) ;; => (1 a b c 2) -``` - -#### -mapcat `(fn list)` - -Return the concatenation of the result of mapping `fn` over `list`. -Thus function `fn` should return a list. - -```el -(-mapcat 'list '(1 2 3)) ;; => (1 2 3) -(-mapcat (lambda (item) (list 0 item)) '(1 2 3)) ;; => (0 1 0 2 0 3) -(--mapcat (list 0 it) '(1 2 3)) ;; => (0 1 0 2 0 3) -``` - -#### -copy `(list)` - -Create a shallow copy of `list`. - -```el -(-copy '(1 2 3)) ;; => (1 2 3) -(let ((a '(1 2 3))) (eq a (-copy a))) ;; => nil -``` - -## Sublist selection - -Functions returning a sublist of the original list. - -#### -filter `(pred list)` - -Return a new list of the items in `list` for which `pred` returns non-nil. - -Alias: `-select`. - -This function's anaphoric counterpart is `--filter`. - -For similar operations, see also [`-keep`](#-keep-fn-list) and [`-remove`](#-remove-pred-list). - -```el -(-filter (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) ;; => (2 4) -(-filter #'natnump '(-2 -1 0 1 2)) ;; => (0 1 2) -(--filter (= 0 (% it 2)) '(1 2 3 4)) ;; => (2 4) -``` - -#### -remove `(pred list)` - -Return a new list of the items in `list` for which `pred` returns nil. - -Alias: `-reject`. - -This function's anaphoric counterpart is `--remove`. - -For similar operations, see also [`-keep`](#-keep-fn-list) and [`-filter`](#-filter-pred-list). - -```el -(-remove (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) ;; => (1 3) -(-remove #'natnump '(-2 -1 0 1 2)) ;; => (-2 -1) -(--remove (= 0 (% it 2)) '(1 2 3 4)) ;; => (1 3) -``` - -#### -remove-first `(pred list)` - -Remove the first item from `list` for which `pred` returns non-nil. -This is a non-destructive operation, but only the front of `list` -leading up to the removed item is a copy; the rest is `list`'s -original tail. If no item is removed, then the result is a -complete copy. - -Alias: `-reject-first`. - -This function's anaphoric counterpart is `--remove-first`. - -See also [`-map-first`](#-map-first-pred-rep-list), [`-remove-item`](#-remove-item-item-list), and [`-remove-last`](#-remove-last-pred-list). - -```el -(-remove-first #'natnump '(-2 -1 0 1 2)) ;; => (-2 -1 1 2) -(-remove-first #'stringp '(1 2 "first" "second")) ;; => (1 2 "second") -(--remove-first (> it 3) '(1 2 3 4 5 6)) ;; => (1 2 3 5 6) -``` - -#### -remove-last `(pred list)` - -Remove the last item from `list` for which `pred` returns non-nil. -The result is a copy of `list` regardless of whether an element is -removed. - -Alias: `-reject-last`. - -This function's anaphoric counterpart is `--remove-last`. - -See also [`-map-last`](#-map-last-pred-rep-list), [`-remove-item`](#-remove-item-item-list), and [`-remove-first`](#-remove-first-pred-list). - -```el -(-remove-last #'natnump '(1 3 5 4 7 8 10 -11)) ;; => (1 3 5 4 7 8 -11) -(-remove-last #'stringp '(1 2 "last" "second")) ;; => (1 2 "last") -(--remove-last (> it 3) '(1 2 3 4 5 6 7 8 9 10)) ;; => (1 2 3 4 5 6 7 8 9) -``` - -#### -remove-item `(item list)` - -Return a copy of `list` with all occurrences of `item` removed. -The comparison is done with `equal`. - -```el -(-remove-item 3 '(1 2 3 2 3 4 5 3)) ;; => (1 2 2 4 5) -(-remove-item 'foo '(foo bar baz foo)) ;; => (bar baz) -(-remove-item "bob" '("alice" "bob" "eve" "bob")) ;; => ("alice" "eve") -``` - -#### -non-nil `(list)` - -Return a copy of `list` with all nil items removed. - -```el -(-non-nil '(nil 1 nil 2 nil nil 3 4 nil 5 nil)) ;; => (1 2 3 4 5) -(-non-nil '((nil))) ;; => ((nil)) -(-non-nil ()) ;; => () -``` - -#### -slice `(list from &optional to step)` - -Return copy of `list`, starting from index `from` to index `to`. - -`from` or `to` may be negative. These values are then interpreted -modulo the length of the list. - -If `step` is a number, only each `step`th item in the resulting -section is returned. Defaults to 1. - -```el -(-slice '(1 2 3 4 5) 1) ;; => (2 3 4 5) -(-slice '(1 2 3 4 5) 0 3) ;; => (1 2 3) -(-slice '(1 2 3 4 5 6 7 8 9) 1 -1 2) ;; => (2 4 6 8) -``` - -#### -take `(n list)` - -Return a copy of the first `n` items in `list`. -Return a copy of `list` if it contains `n` items or fewer. -Return nil if `n` is zero or less. - -See also: [`-take-last`](#-take-last-n-list). - -```el -(-take 3 '(1 2 3 4 5)) ;; => (1 2 3) -(-take 17 '(1 2 3 4 5)) ;; => (1 2 3 4 5) -(-take 0 '(1 2 3 4 5)) ;; => () -``` - -#### -take-last `(n list)` - -Return a copy of the last `n` items of `list` in order. -Return a copy of `list` if it contains `n` items or fewer. -Return nil if `n` is zero or less. - -See also: [`-take`](#-take-n-list). - -```el -(-take-last 3 '(1 2 3 4 5)) ;; => (3 4 5) -(-take-last 17 '(1 2 3 4 5)) ;; => (1 2 3 4 5) -(-take-last 1 '(1 2 3 4 5)) ;; => (5) -``` - -#### -drop `(n list)` - -Return the tail (not a copy) of `list` without the first `n` items. -Return nil if `list` contains `n` items or fewer. -Return `list` if `n` is zero or less. - -For another variant, see also [`-drop-last`](#-drop-last-n-list). - -```el -(-drop 3 '(1 2 3 4 5)) ;; => (4 5) -(-drop 17 '(1 2 3 4 5)) ;; => () -(-drop 0 '(1 2 3 4 5)) ;; => (1 2 3 4 5) -``` - -#### -drop-last `(n list)` - -Return a copy of `list` without its last `n` items. -Return a copy of `list` if `n` is zero or less. -Return nil if `list` contains `n` items or fewer. - -See also: [`-drop`](#-drop-n-list). - -```el -(-drop-last 3 '(1 2 3 4 5)) ;; => (1 2) -(-drop-last 17 '(1 2 3 4 5)) ;; => () -(-drop-last 0 '(1 2 3 4 5)) ;; => (1 2 3 4 5) -``` - -#### -take-while `(pred list)` - -Take successive items from `list` for which `pred` returns non-nil. -`pred` is a function of one argument. Return a new list of the -successive elements from the start of `list` for which `pred` returns -non-nil. - -This function's anaphoric counterpart is `--take-while`. - -For another variant, see also [`-drop-while`](#-drop-while-pred-list). - -```el -(-take-while #'even? '(1 2 3 4)) ;; => () -(-take-while #'even? '(2 4 5 6)) ;; => (2 4) -(--take-while (< it 4) '(1 2 3 4 3 2 1)) ;; => (1 2 3) -``` - -#### -drop-while `(pred list)` - -Drop successive items from `list` for which `pred` returns non-nil. -`pred` is a function of one argument. Return the tail (not a copy) -of `list` starting from its first element for which `pred` returns -nil. - -This function's anaphoric counterpart is `--drop-while`. - -For another variant, see also [`-take-while`](#-take-while-pred-list). - -```el -(-drop-while #'even? '(1 2 3 4)) ;; => (1 2 3 4) -(-drop-while #'even? '(2 4 5 6)) ;; => (5 6) -(--drop-while (< it 4) '(1 2 3 4 3 2 1)) ;; => (4 3 2 1) -``` - -#### -select-by-indices `(indices list)` - -Return a list whose elements are elements from `list` selected -as `(nth i list)` for all i from `indices`. - -```el -(-select-by-indices '(4 10 2 3 6) '("v" "e" "l" "o" "c" "i" "r" "a" "p" "t" "o" "r")) ;; => ("c" "o" "l" "o" "r") -(-select-by-indices '(2 1 0) '("a" "b" "c")) ;; => ("c" "b" "a") -(-select-by-indices '(0 1 2 0 1 3 3 1) '("f" "a" "r" "l")) ;; => ("f" "a" "r" "f" "a" "l" "l" "a") -``` - -#### -select-columns `(columns table)` - -Select `columns` from `table`. - -`table` is a list of lists where each element represents one row. -It is assumed each row has the same length. - -Each row is transformed such that only the specified `columns` are -selected. - -See also: [`-select-column`](#-select-column-column-table), [`-select-by-indices`](#-select-by-indices-indices-list) - -```el -(-select-columns '(0 2) '((1 2 3) (a b c) (:a :b :c))) ;; => ((1 3) (a c) (:a :c)) -(-select-columns '(1) '((1 2 3) (a b c) (:a :b :c))) ;; => ((2) (b) (:b)) -(-select-columns nil '((1 2 3) (a b c) (:a :b :c))) ;; => (nil nil nil) -``` - -#### -select-column `(column table)` - -Select `column` from `table`. - -`table` is a list of lists where each element represents one row. -It is assumed each row has the same length. - -The single selected column is returned as a list. - -See also: [`-select-columns`](#-select-columns-columns-table), [`-select-by-indices`](#-select-by-indices-indices-list) - -```el -(-select-column 1 '((1 2 3) (a b c) (:a :b :c))) ;; => (2 b :b) -``` - -## List to list - -Functions returning a modified copy of the input list. - -#### -keep `(fn list)` - -Return a new list of the non-nil results of applying `fn` to each item in `list`. -Like [`-filter`](#-filter-pred-list), but returns the non-nil results of `fn` instead of -the corresponding elements of `list`. - -Its anaphoric counterpart is `--keep`. - -```el -(-keep #'cdr '((1 2 3) (4 5) (6))) ;; => ((2 3) (5)) -(-keep (lambda (n) (and (> n 3) (* 10 n))) '(1 2 3 4 5 6)) ;; => (40 50 60) -(--keep (and (> it 3) (* 10 it)) '(1 2 3 4 5 6)) ;; => (40 50 60) -``` - -#### -concat `(&rest lists)` - -Return a new list with the concatenation of the elements in the supplied `lists`. - -```el -(-concat '(1)) ;; => (1) -(-concat '(1) '(2)) ;; => (1 2) -(-concat '(1) '(2 3) '(4)) ;; => (1 2 3 4) -``` - -#### -flatten `(l)` - -Take a nested list `l` and return its contents as a single, flat list. - -Note that because `nil` represents a list of zero elements (an -empty list), any mention of nil in `l` will disappear after -flattening. If you need to preserve nils, consider [`-flatten-n`](#-flatten-n-num-list) -or map them to some unique symbol and then map them back. - -Conses of two atoms are considered "terminals", that is, they -aren't flattened further. - -See also: [`-flatten-n`](#-flatten-n-num-list) - -```el -(-flatten '((1))) ;; => (1) -(-flatten '((1 (2 3) (((4 (5))))))) ;; => (1 2 3 4 5) -(-flatten '(1 2 (3 . 4))) ;; => (1 2 (3 . 4)) -``` - -#### -flatten-n `(num list)` - -Flatten `num` levels of a nested `list`. - -See also: [`-flatten`](#-flatten-l) - -```el -(-flatten-n 1 '((1 2) ((3 4) ((5 6))))) ;; => (1 2 (3 4) ((5 6))) -(-flatten-n 2 '((1 2) ((3 4) ((5 6))))) ;; => (1 2 3 4 (5 6)) -(-flatten-n 3 '((1 2) ((3 4) ((5 6))))) ;; => (1 2 3 4 5 6) -``` - -#### -replace `(old new list)` - -Replace all `old` items in `list` with `new`. - -Elements are compared using `equal`. - -See also: [`-replace-at`](#-replace-at-n-x-list) - -```el -(-replace 1 "1" '(1 2 3 4 3 2 1)) ;; => ("1" 2 3 4 3 2 "1") -(-replace "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) ;; => ("a" "nice" "bar" "sentence" "about" "bar") -(-replace 1 2 nil) ;; => nil -``` - -#### -replace-first `(old new list)` - -Replace the first occurrence of `old` with `new` in `list`. - -Elements are compared using `equal`. - -See also: [`-map-first`](#-map-first-pred-rep-list) - -```el -(-replace-first 1 "1" '(1 2 3 4 3 2 1)) ;; => ("1" 2 3 4 3 2 1) -(-replace-first "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) ;; => ("a" "nice" "bar" "sentence" "about" "foo") -(-replace-first 1 2 nil) ;; => nil -``` - -#### -replace-last `(old new list)` - -Replace the last occurrence of `old` with `new` in `list`. - -Elements are compared using `equal`. - -See also: [`-map-last`](#-map-last-pred-rep-list) - -```el -(-replace-last 1 "1" '(1 2 3 4 3 2 1)) ;; => (1 2 3 4 3 2 "1") -(-replace-last "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) ;; => ("a" "nice" "foo" "sentence" "about" "bar") -(-replace-last 1 2 nil) ;; => nil -``` - -#### -insert-at `(n x list)` - -Return a list with `x` inserted into `list` at position `n`. - -See also: [`-splice`](#-splice-pred-fun-list), [`-splice-list`](#-splice-list-pred-new-list-list) - -```el -(-insert-at 1 'x '(a b c)) ;; => (a x b c) -(-insert-at 12 'x '(a b c)) ;; => (a b c x) -``` - -#### -replace-at `(n x list)` - -Return a list with element at `n`th position in `list` replaced with `x`. - -See also: [`-replace`](#-replace-old-new-list) - -```el -(-replace-at 0 9 '(0 1 2 3 4 5)) ;; => (9 1 2 3 4 5) -(-replace-at 1 9 '(0 1 2 3 4 5)) ;; => (0 9 2 3 4 5) -(-replace-at 4 9 '(0 1 2 3 4 5)) ;; => (0 1 2 3 9 5) -``` - -#### -update-at `(n func list)` - -Return a list with element at `n`th position in `list` replaced with `(func (nth n list))`. - -See also: [`-map-when`](#-map-when-pred-rep-list) - -```el -(-update-at 0 (lambda (x) (+ x 9)) '(0 1 2 3 4 5)) ;; => (9 1 2 3 4 5) -(-update-at 1 (lambda (x) (+ x 8)) '(0 1 2 3 4 5)) ;; => (0 9 2 3 4 5) -(--update-at 2 (length it) '("foo" "bar" "baz" "quux")) ;; => ("foo" "bar" 3 "quux") -``` - -#### -remove-at `(n list)` - -Return a list with element at `n`th position in `list` removed. - -See also: [`-remove-at-indices`](#-remove-at-indices-indices-list), [`-remove`](#-remove-pred-list) - -```el -(-remove-at 0 '("0" "1" "2" "3" "4" "5")) ;; => ("1" "2" "3" "4" "5") -(-remove-at 1 '("0" "1" "2" "3" "4" "5")) ;; => ("0" "2" "3" "4" "5") -(-remove-at 2 '("0" "1" "2" "3" "4" "5")) ;; => ("0" "1" "3" "4" "5") -``` - -#### -remove-at-indices `(indices list)` - -Return a list whose elements are elements from `list` without -elements selected as `(nth i list)` for all i -from `indices`. - -See also: [`-remove-at`](#-remove-at-n-list), [`-remove`](#-remove-pred-list) - -```el -(-remove-at-indices '(0) '("0" "1" "2" "3" "4" "5")) ;; => ("1" "2" "3" "4" "5") -(-remove-at-indices '(0 2 4) '("0" "1" "2" "3" "4" "5")) ;; => ("1" "3" "5") -(-remove-at-indices '(0 5) '("0" "1" "2" "3" "4" "5")) ;; => ("1" "2" "3" "4") -``` - -## Reductions - -Functions reducing lists to a single value (which may also be a list). - -#### -reduce-from `(fn init list)` - -Reduce the function `fn` across `list`, starting with `init`. -Return the result of applying `fn` to `init` and the first element of -`list`, then applying `fn` to that result and the second element, -etc. If `list` is empty, return `init` without calling `fn`. - -This function's anaphoric counterpart is `--reduce-from`. - -For other folds, see also [`-reduce`](#-reduce-fn-list) and [`-reduce-r`](#-reduce-r-fn-list). - -```el -(-reduce-from #'- 10 '(1 2 3)) ;; => 4 -(-reduce-from #'list 10 '(1 2 3)) ;; => (((10 1) 2) 3) -(--reduce-from (concat acc " " it) "START" '("a" "b" "c")) ;; => "START a b c" -``` - -#### -reduce-r-from `(fn init list)` - -Reduce the function `fn` across `list` in reverse, starting with `init`. -Return the result of applying `fn` to the last element of `list` and -`init`, then applying `fn` to the second-to-last element and the -previous result of `fn`, etc. That is, the first argument of `fn` is -the current element, and its second argument the accumulated -value. If `list` is empty, return `init` without calling `fn`. - -This function is like [`-reduce-from`](#-reduce-from-fn-init-list) but the operation associates -from the right rather than left. In other words, it starts from -the end of `list` and flips the arguments to `fn`. Conceptually, it -is like replacing the conses in `list` with applications of `fn`, and -its last link with `init`, and evaluating the resulting expression. - -This function's anaphoric counterpart is `--reduce-r-from`. - -For other folds, see also [`-reduce-r`](#-reduce-r-fn-list) and [`-reduce`](#-reduce-fn-list). - -```el -(-reduce-r-from #'- 10 '(1 2 3)) ;; => -8 -(-reduce-r-from #'list 10 '(1 2 3)) ;; => (1 (2 (3 10))) -(--reduce-r-from (concat it " " acc) "END" '("a" "b" "c")) ;; => "a b c END" -``` - -#### -reduce `(fn list)` - -Reduce the function `fn` across `list`. -Return the result of applying `fn` to the first two elements of -`list`, then applying `fn` to that result and the third element, etc. -If `list` contains a single element, return it without calling `fn`. -If `list` is empty, return the result of calling `fn` with no -arguments. - -This function's anaphoric counterpart is `--reduce`. - -For other folds, see also [`-reduce-from`](#-reduce-from-fn-init-list) and [`-reduce-r`](#-reduce-r-fn-list). - -```el -(-reduce #'- '(1 2 3 4)) ;; => -8 -(-reduce #'list '(1 2 3 4)) ;; => (((1 2) 3) 4) -(--reduce (format "%s-%d" acc it) '(1 2 3)) ;; => "1-2-3" -``` - -#### -reduce-r `(fn list)` - -Reduce the function `fn` across `list` in reverse. -Return the result of applying `fn` to the last two elements of -`list`, then applying `fn` to the third-to-last element and the -previous result of `fn`, etc. That is, the first argument of `fn` is -the current element, and its second argument the accumulated -value. If `list` contains a single element, return it without -calling `fn`. If `list` is empty, return the result of calling `fn` -with no arguments. - -This function is like [`-reduce`](#-reduce-fn-list) but the operation associates from -the right rather than left. In other words, it starts from the -end of `list` and flips the arguments to `fn`. Conceptually, it is -like replacing the conses in `list` with applications of `fn`, -ignoring its last link, and evaluating the resulting expression. - -This function's anaphoric counterpart is `--reduce-r`. - -For other folds, see also [`-reduce-r-from`](#-reduce-r-from-fn-init-list) and [`-reduce`](#-reduce-fn-list). - -```el -(-reduce-r #'- '(1 2 3 4)) ;; => -2 -(-reduce-r #'list '(1 2 3 4)) ;; => (1 (2 (3 4))) -(--reduce-r (format "%s-%d" acc it) '(1 2 3)) ;; => "3-2-1" -``` - -#### -reductions-from `(fn init list)` - -Return a list of `fn`'s intermediate reductions across `list`. -That is, a list of the intermediate values of the accumulator -when [`-reduce-from`](#-reduce-from-fn-init-list) (which see) is called with the same -arguments. - -This function's anaphoric counterpart is `--reductions-from`. - -For other folds, see also [`-reductions`](#-reductions-fn-list) and [`-reductions-r`](#-reductions-r-fn-list). - -```el -(-reductions-from #'max 0 '(2 1 4 3)) ;; => (0 2 2 4 4) -(-reductions-from #'* 1 '(1 2 3 4)) ;; => (1 1 2 6 24) -(--reductions-from (format "(FN %s %d)" acc it) "INIT" '(1 2 3)) ;; => ("INIT" "(FN INIT 1)" "(FN (FN INIT 1) 2)" "(FN (FN (FN INIT 1) 2) 3)") -``` - -#### -reductions-r-from `(fn init list)` - -Return a list of `fn`'s intermediate reductions across reversed `list`. -That is, a list of the intermediate values of the accumulator -when [`-reduce-r-from`](#-reduce-r-from-fn-init-list) (which see) is called with the same -arguments. - -This function's anaphoric counterpart is `--reductions-r-from`. - -For other folds, see also [`-reductions`](#-reductions-fn-list) and [`-reductions-r`](#-reductions-r-fn-list). - -```el -(-reductions-r-from #'max 0 '(2 1 4 3)) ;; => (4 4 4 3 0) -(-reductions-r-from #'* 1 '(1 2 3 4)) ;; => (24 24 12 4 1) -(--reductions-r-from (format "(FN %d %s)" it acc) "INIT" '(1 2 3)) ;; => ("(FN 1 (FN 2 (FN 3 INIT)))" "(FN 2 (FN 3 INIT))" "(FN 3 INIT)" "INIT") -``` - -#### -reductions `(fn list)` - -Return a list of `fn`'s intermediate reductions across `list`. -That is, a list of the intermediate values of the accumulator -when [`-reduce`](#-reduce-fn-list) (which see) is called with the same arguments. - -This function's anaphoric counterpart is `--reductions`. - -For other folds, see also [`-reductions`](#-reductions-fn-list) and [`-reductions-r`](#-reductions-r-fn-list). - -```el -(-reductions #'+ '(1 2 3 4)) ;; => (1 3 6 10) -(-reductions #'* '(1 2 3 4)) ;; => (1 2 6 24) -(--reductions (format "(FN %s %d)" acc it) '(1 2 3)) ;; => (1 "(FN 1 2)" "(FN (FN 1 2) 3)") -``` - -#### -reductions-r `(fn list)` - -Return a list of `fn`'s intermediate reductions across reversed `list`. -That is, a list of the intermediate values of the accumulator -when [`-reduce-r`](#-reduce-r-fn-list) (which see) is called with the same arguments. - -This function's anaphoric counterpart is `--reductions-r`. - -For other folds, see also [`-reductions-r-from`](#-reductions-r-from-fn-init-list) and -[`-reductions`](#-reductions-fn-list). - -```el -(-reductions-r #'+ '(1 2 3 4)) ;; => (10 9 7 4) -(-reductions-r #'* '(1 2 3 4)) ;; => (24 24 12 4) -(--reductions-r (format "(FN %d %s)" it acc) '(1 2 3)) ;; => ("(FN 1 (FN 2 3))" "(FN 2 3)" 3) -``` - -#### -count `(pred list)` - -Counts the number of items in `list` where (`pred` item) is non-nil. - -```el -(-count 'even? '(1 2 3 4 5)) ;; => 2 -(--count (< it 4) '(1 2 3 4)) ;; => 3 -``` - -#### -sum `(list)` - -Return the sum of `list`. - -```el -(-sum ()) ;; => 0 -(-sum '(1)) ;; => 1 -(-sum '(1 2 3 4)) ;; => 10 -``` - -#### -running-sum `(list)` - -Return a list with running sums of items in `list`. -`list` must be non-empty. - -```el -(-running-sum '(1 2 3 4)) ;; => (1 3 6 10) -(-running-sum '(1)) ;; => (1) -(-running-sum ()) ;; Wrong type argument: consp, nil -``` - -#### -product `(list)` - -Return the product of `list`. - -```el -(-product ()) ;; => 1 -(-product '(1)) ;; => 1 -(-product '(1 2 3 4)) ;; => 24 -``` - -#### -running-product `(list)` - -Return a list with running products of items in `list`. -`list` must be non-empty. - -```el -(-running-product '(1 2 3 4)) ;; => (1 2 6 24) -(-running-product '(1)) ;; => (1) -(-running-product ()) ;; Wrong type argument: consp, nil -``` - -#### -inits `(list)` - -Return all prefixes of `list`. - -```el -(-inits '(1 2 3 4)) ;; => (nil (1) (1 2) (1 2 3) (1 2 3 4)) -(-inits nil) ;; => (nil) -(-inits '(1)) ;; => (nil (1)) -``` - -#### -tails `(list)` - -Return all suffixes of `list` - -```el -(-tails '(1 2 3 4)) ;; => ((1 2 3 4) (2 3 4) (3 4) (4) nil) -(-tails nil) ;; => (nil) -(-tails '(1)) ;; => ((1) nil) -``` - -#### -common-prefix `(&rest lists)` - -Return the longest common prefix of `lists`. - -```el -(-common-prefix '(1)) ;; => (1) -(-common-prefix '(1 2) '(3 4) '(1 2)) ;; => () -(-common-prefix '(1 2) '(1 2 3) '(1 2 3 4)) ;; => (1 2) -``` - -#### -common-suffix `(&rest lists)` - -Return the longest common suffix of `lists`. - -```el -(-common-suffix '(1)) ;; => (1) -(-common-suffix '(1 2) '(3 4) '(1 2)) ;; => () -(-common-suffix '(1 2 3 4) '(2 3 4) '(3 4)) ;; => (3 4) -``` - -#### -min `(list)` - -Return the smallest value from `list` of numbers or markers. - -```el -(-min '(0)) ;; => 0 -(-min '(3 2 1)) ;; => 1 -(-min '(1 2 3)) ;; => 1 -``` - -#### -min-by `(comparator list)` - -Take a comparison function `comparator` and a `list` and return -the least element of the list by the comparison function. - -See also combinator [`-on`](#-on-op-trans) which can transform the values before -comparing them. - -```el -(-min-by '> '(4 3 6 1)) ;; => 1 -(--min-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) ;; => (1 2 3) -(--min-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) ;; => (2) -``` - -#### -max `(list)` - -Return the largest value from `list` of numbers or markers. - -```el -(-max '(0)) ;; => 0 -(-max '(3 2 1)) ;; => 3 -(-max '(1 2 3)) ;; => 3 -``` - -#### -max-by `(comparator list)` - -Take a comparison function `comparator` and a `list` and return -the greatest element of the list by the comparison function. - -See also combinator [`-on`](#-on-op-trans) which can transform the values before -comparing them. - -```el -(-max-by '> '(4 3 6 1)) ;; => 6 -(--max-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) ;; => (3 2) -(--max-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) ;; => (1 2 3) -``` - -## Unfolding - -Operations dual to reductions, building lists from a seed -value rather than consuming a list to produce a single value. - -#### -iterate `(fun init n)` - -Return a list of iterated applications of `fun` to `init`. - -This means a list of the form: - - (`init` (`fun` `init`) (`fun` (`fun` `init`)) ...) - -`n` is the length of the returned list. - -```el -(-iterate #'1+ 1 10) ;; => (1 2 3 4 5 6 7 8 9 10) -(-iterate (lambda (x) (+ x x)) 2 5) ;; => (2 4 8 16 32) -(--iterate (* it it) 2 5) ;; => (2 4 16 256 65536) -``` - -#### -unfold `(fun seed)` - -Build a list from `seed` using `fun`. - -This is "dual" operation to [`-reduce-r`](#-reduce-r-fn-list): while -reduce-r -consumes a list to produce a single value, [`-unfold`](#-unfold-fun-seed) takes a -seed value and builds a (potentially infinite!) list. - -`fun` should return `nil` to stop the generating process, or a -cons (`a` . `b`), where `a` will be prepended to the result and `b` is -the new seed. - -```el -(-unfold (lambda (x) (unless (= x 0) (cons x (1- x)))) 10) ;; => (10 9 8 7 6 5 4 3 2 1) -(--unfold (when it (cons it (cdr it))) '(1 2 3 4)) ;; => ((1 2 3 4) (2 3 4) (3 4) (4)) -(--unfold (when it (cons it (butlast it))) '(1 2 3 4)) ;; => ((1 2 3 4) (1 2 3) (1 2) (1)) -``` - -## Predicates - -Reductions of one or more lists to a boolean value. - -#### -some `(pred list)` - -Return (`pred` x) for the first `list` item where (`pred` x) is non-nil, else nil. - -Alias: `-any`. - -This function's anaphoric counterpart is `--some`. - -```el -(-some #'stringp '(1 "2" 3)) ;; => t -(--some (string-match-p "x" it) '("foo" "axe" "xor")) ;; => 1 -(--some (= it-index 3) '(0 1 2)) ;; => nil -``` - -#### -every `(pred list)` - -Return non-nil if `pred` returns non-nil for all items in `list`. -If so, return the last such result of `pred`. Otherwise, once an -item is reached for which `pred` returns nil, return nil without -calling `pred` on any further `list` elements. - -This function is like `-every-p`, but on success returns the last -non-nil result of `pred` instead of just t. - -This function's anaphoric counterpart is `--every`. - -```el -(-every #'numberp '(1 2 3)) ;; => t -(--every (string-match-p "x" it) '("axe" "xor")) ;; => 0 -(--every (= it it-index) '(0 1 3)) ;; => nil -``` - -#### -any? `(pred list)` - -Return t if (`pred` x) is non-nil for any x in `list`, else nil. - -Alias: `-any-p`, `-some?`, `-some-p` - -```el -(-any? #'numberp '(nil 0 t)) ;; => t -(-any? #'numberp '(nil t t)) ;; => nil -(-any? #'null '(1 3 5)) ;; => nil -``` - -#### -all? `(pred list)` - -Return t if (`pred` `x`) is non-nil for all `x` in `list`, else nil. -In the latter case, stop after the first `x` for which (`pred` `x`) is -nil, without calling `pred` on any subsequent elements of `list`. - -The similar function [`-every`](#-every-pred-list) is more widely useful, since it -returns the last non-nil result of `pred` instead of just t on -success. - -Alias: `-all-p`, `-every-p`, `-every?`. - -This function's anaphoric counterpart is `--all?`. - -```el -(-all? #'numberp '(1 2 3)) ;; => t -(-all? #'numberp '(2 t 6)) ;; => nil -(--all? (= 0 (% it 2)) '(2 4 6)) ;; => t -``` - -#### -none? `(pred list)` - -Return t if (`pred` x) is nil for all x in `list`, else nil. - -Alias: `-none-p` - -```el -(-none? 'even? '(1 2 3)) ;; => nil -(-none? 'even? '(1 3 5)) ;; => t -(--none? (= 0 (% it 2)) '(1 2 3)) ;; => nil -``` - -#### -only-some? `(pred list)` - -Return `t` if at least one item of `list` matches `pred` and at least one item of `list` does not match `pred`. -Return `nil` both if all items match the predicate or if none of the items match the predicate. - -Alias: `-only-some-p` - -```el -(-only-some? 'even? '(1 2 3)) ;; => t -(-only-some? 'even? '(1 3 5)) ;; => nil -(-only-some? 'even? '(2 4 6)) ;; => nil -``` - -#### -contains? `(list element)` - -Return non-nil if `list` contains `element`. - -The test for equality is done with `equal`, or with `-compare-fn` -if that's non-nil. - -Alias: `-contains-p` - -```el -(-contains? '(1 2 3) 1) ;; => t -(-contains? '(1 2 3) 2) ;; => t -(-contains? '(1 2 3) 4) ;; => nil -``` - -#### -same-items? `(list list2)` - -Return true if `list` and `list2` has the same items. - -The order of the elements in the lists does not matter. - -Alias: `-same-items-p` - -```el -(-same-items? '(1 2 3) '(1 2 3)) ;; => t -(-same-items? '(1 2 3) '(3 2 1)) ;; => t -(-same-items? '(1 2 3) '(1 2 3 4)) ;; => nil -``` - -#### -is-prefix? `(prefix list)` - -Return non-nil if `prefix` is a prefix of `list`. - -Alias: `-is-prefix-p`. - -```el -(-is-prefix? '(1 2 3) '(1 2 3 4 5)) ;; => t -(-is-prefix? '(1 2 3 4 5) '(1 2 3)) ;; => nil -(-is-prefix? '(1 3) '(1 2 3 4 5)) ;; => nil -``` - -#### -is-suffix? `(suffix list)` - -Return non-nil if `suffix` is a suffix of `list`. - -Alias: `-is-suffix-p`. - -```el -(-is-suffix? '(3 4 5) '(1 2 3 4 5)) ;; => t -(-is-suffix? '(1 2 3 4 5) '(3 4 5)) ;; => nil -(-is-suffix? '(3 5) '(1 2 3 4 5)) ;; => nil -``` - -#### -is-infix? `(infix list)` - -Return non-nil if `infix` is infix of `list`. - -This operation runs in O(n^2) time - -Alias: `-is-infix-p` - -```el -(-is-infix? '(1 2 3) '(1 2 3 4 5)) ;; => t -(-is-infix? '(2 3 4) '(1 2 3 4 5)) ;; => t -(-is-infix? '(3 4 5) '(1 2 3 4 5)) ;; => t -``` - -#### -cons-pair? `(obj)` - -Return non-nil if `obj` is a true cons pair. -That is, a cons (`a` . `b`) where `b` is not a list. - -Alias: `-cons-pair-p`. - -```el -(-cons-pair? '(1 . 2)) ;; => t -(-cons-pair? '(1 2)) ;; => nil -(-cons-pair? '(1)) ;; => nil -``` - -## Partitioning - -Functions partitioning the input list into a list of lists. - -#### -split-at `(n list)` - -Split `list` into two sublists after the `n`th element. -The result is a list of two elements (`take` `drop`) where `take` is a -new list of the first `n` elements of `list`, and `drop` is the -remaining elements of `list` (not a copy). `take` and `drop` are like -the results of [`-take`](#-take-n-list) and [`-drop`](#-drop-n-list), respectively, but the split -is done in a single list traversal. - -```el -(-split-at 3 '(1 2 3 4 5)) ;; => ((1 2 3) (4 5)) -(-split-at 17 '(1 2 3 4 5)) ;; => ((1 2 3 4 5) nil) -(-split-at 0 '(1 2 3 4 5)) ;; => (nil (1 2 3 4 5)) -``` - -#### -split-with `(pred list)` - -Return a list of ((-take-while `pred` `list`) (-drop-while `pred` `list`)), in no more than one pass through the list. - -```el -(-split-with 'even? '(1 2 3 4)) ;; => (nil (1 2 3 4)) -(-split-with 'even? '(2 4 5 6)) ;; => ((2 4) (5 6)) -(--split-with (< it 4) '(1 2 3 4 3 2 1)) ;; => ((1 2 3) (4 3 2 1)) -``` - -#### -split-on `(item list)` - -Split the `list` each time `item` is found. - -Unlike [`-partition-by`](#-partition-by-fn-list), the `item` is discarded from the results. -Empty lists are also removed from the result. - -Comparison is done by `equal`. - -See also [`-split-when`](#-split-when-fn-list) - -```el -(-split-on '| '(Nil | Leaf a | Node [Tree a])) ;; => ((Nil) (Leaf a) (Node [Tree a])) -(-split-on :endgroup '("a" "b" :endgroup "c" :endgroup "d" "e")) ;; => (("a" "b") ("c") ("d" "e")) -(-split-on :endgroup '("a" "b" :endgroup :endgroup "d" "e")) ;; => (("a" "b") ("d" "e")) -``` - -#### -split-when `(fn list)` - -Split the `list` on each element where `fn` returns non-nil. - -Unlike [`-partition-by`](#-partition-by-fn-list), the "matched" element is discarded from -the results. Empty lists are also removed from the result. - -This function can be thought of as a generalization of -`split-string`. - -```el -(-split-when 'even? '(1 2 3 4 5 6)) ;; => ((1) (3) (5)) -(-split-when 'even? '(1 2 3 4 6 8 9)) ;; => ((1) (3) (9)) -(--split-when (memq it '(&optional &rest)) '(a b &optional c d &rest args)) ;; => ((a b) (c d) (args)) -``` - -#### -separate `(pred list)` - -Return a list of ((-filter `pred` `list`) (-remove `pred` `list`)), in one pass through the list. - -```el -(-separate (lambda (num) (= 0 (% num 2))) '(1 2 3 4 5 6 7)) ;; => ((2 4 6) (1 3 5 7)) -(--separate (< it 5) '(3 7 5 9 3 2 1 4 6)) ;; => ((3 3 2 1 4) (7 5 9 6)) -(-separate 'cdr '((1 2) (1) (1 2 3) (4))) ;; => (((1 2) (1 2 3)) ((1) (4))) -``` - -#### -partition `(n list)` - -Return a new list with the items in `list` grouped into `n`-sized sublists. -If there are not enough items to make the last group `n`-sized, -those items are discarded. - -```el -(-partition 2 '(1 2 3 4 5 6)) ;; => ((1 2) (3 4) (5 6)) -(-partition 2 '(1 2 3 4 5 6 7)) ;; => ((1 2) (3 4) (5 6)) -(-partition 3 '(1 2 3 4 5 6 7)) ;; => ((1 2 3) (4 5 6)) -``` - -#### -partition-all `(n list)` - -Return a new list with the items in `list` grouped into `n`-sized sublists. -The last group may contain less than `n` items. - -```el -(-partition-all 2 '(1 2 3 4 5 6)) ;; => ((1 2) (3 4) (5 6)) -(-partition-all 2 '(1 2 3 4 5 6 7)) ;; => ((1 2) (3 4) (5 6) (7)) -(-partition-all 3 '(1 2 3 4 5 6 7)) ;; => ((1 2 3) (4 5 6) (7)) -``` - -#### -partition-in-steps `(n step list)` - -Return a new list with the items in `list` grouped into `n`-sized sublists at offsets `step` apart. -If there are not enough items to make the last group `n`-sized, -those items are discarded. - -```el -(-partition-in-steps 2 1 '(1 2 3 4)) ;; => ((1 2) (2 3) (3 4)) -(-partition-in-steps 3 2 '(1 2 3 4)) ;; => ((1 2 3)) -(-partition-in-steps 3 2 '(1 2 3 4 5)) ;; => ((1 2 3) (3 4 5)) -``` - -#### -partition-all-in-steps `(n step list)` - -Return a new list with the items in `list` grouped into `n`-sized sublists at offsets `step` apart. -The last groups may contain less than `n` items. - -```el -(-partition-all-in-steps 2 1 '(1 2 3 4)) ;; => ((1 2) (2 3) (3 4) (4)) -(-partition-all-in-steps 3 2 '(1 2 3 4)) ;; => ((1 2 3) (3 4)) -(-partition-all-in-steps 3 2 '(1 2 3 4 5)) ;; => ((1 2 3) (3 4 5) (5)) -``` - -#### -partition-by `(fn list)` - -Apply `fn` to each item in `list`, splitting it each time `fn` returns a new value. - -```el -(-partition-by 'even? ()) ;; => () -(-partition-by 'even? '(1 1 2 2 2 3 4 6 8)) ;; => ((1 1) (2 2 2) (3) (4 6 8)) -(--partition-by (< it 3) '(1 2 3 4 3 2 1)) ;; => ((1 2) (3 4 3) (2 1)) -``` - -#### -partition-by-header `(fn list)` - -Apply `fn` to the first item in `list`. That is the header -value. Apply `fn` to each item in `list`, splitting it each time `fn` -returns the header value, but only after seeing at least one -other value (the body). - -```el -(--partition-by-header (= it 1) '(1 2 3 1 2 1 2 3 4)) ;; => ((1 2 3) (1 2) (1 2 3 4)) -(--partition-by-header (> it 0) '(1 2 0 1 0 1 2 3 0)) ;; => ((1 2 0) (1 0) (1 2 3 0)) -(-partition-by-header 'even? '(2 1 1 1 4 1 3 5 6 6 1)) ;; => ((2 1 1 1) (4 1 3 5) (6 6 1)) -``` - -#### -partition-after-pred `(pred list)` - -Partition `list` after each element for which `pred` returns non-nil. - -This function's anaphoric counterpart is `--partition-after-pred`. - -```el -(-partition-after-pred #'booleanp ()) ;; => () -(-partition-after-pred #'booleanp '(t t)) ;; => ((t) (t)) -(-partition-after-pred #'booleanp '(0 0 t t 0 t)) ;; => ((0 0 t) (t) (0 t)) -``` - -#### -partition-before-pred `(pred list)` - -Partition directly before each time `pred` is true on an element of `list`. - -```el -(-partition-before-pred #'booleanp ()) ;; => () -(-partition-before-pred #'booleanp '(0 t)) ;; => ((0) (t)) -(-partition-before-pred #'booleanp '(0 0 t 0 t t)) ;; => ((0 0) (t 0) (t) (t)) -``` - -#### -partition-before-item `(item list)` - -Partition directly before each time `item` appears in `list`. - -```el -(-partition-before-item 3 ()) ;; => () -(-partition-before-item 3 '(1)) ;; => ((1)) -(-partition-before-item 3 '(3)) ;; => ((3)) -``` - -#### -partition-after-item `(item list)` - -Partition directly after each time `item` appears in `list`. - -```el -(-partition-after-item 3 ()) ;; => () -(-partition-after-item 3 '(1)) ;; => ((1)) -(-partition-after-item 3 '(3)) ;; => ((3)) -``` - -#### -group-by `(fn list)` - -Separate `list` into an alist whose keys are `fn` applied to the -elements of `list`. Keys are compared by `equal`. - -```el -(-group-by 'even? ()) ;; => () -(-group-by 'even? '(1 1 2 2 2 3 4 6 8)) ;; => ((nil 1 1 3) (t 2 2 2 4 6 8)) -(--group-by (car (split-string it "/")) '("a/b" "c/d" "a/e")) ;; => (("a" "a/b" "a/e") ("c" "c/d")) -``` - -## Indexing - -Functions retrieving or sorting based on list indices and -related predicates. - -#### -elem-index `(elem list)` - -Return the index of the first element in the given `list` which -is equal to the query element `elem`, or nil if there is no -such element. - -```el -(-elem-index 2 '(6 7 8 2 3 4)) ;; => 3 -(-elem-index "bar" '("foo" "bar" "baz")) ;; => 1 -(-elem-index '(1 2) '((3) (5 6) (1 2) nil)) ;; => 2 -``` - -#### -elem-indices `(elem list)` - -Return the indices of all elements in `list` equal to the query -element `elem`, in ascending order. - -```el -(-elem-indices 2 '(6 7 8 2 3 4 2 1)) ;; => (3 6) -(-elem-indices "bar" '("foo" "bar" "baz")) ;; => (1) -(-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) ;; => (1 3) -``` - -#### -find-index `(pred list)` - -Take a predicate `pred` and a `list` and return the index of the -first element in the list satisfying the predicate, or nil if -there is no such element. - -See also [`-first`](#-first-pred-list). - -```el -(-find-index 'even? '(2 4 1 6 3 3 5 8)) ;; => 0 -(--find-index (< 5 it) '(2 4 1 6 3 3 5 8)) ;; => 3 -(-find-index (-partial 'string-lessp "baz") '("bar" "foo" "baz")) ;; => 1 -``` - -#### -find-last-index `(pred list)` - -Take a predicate `pred` and a `list` and return the index of the -last element in the list satisfying the predicate, or nil if -there is no such element. - -See also [`-last`](#-last-pred-list). - -```el -(-find-last-index 'even? '(2 4 1 6 3 3 5 8)) ;; => 7 -(--find-last-index (< 5 it) '(2 7 1 6 3 8 5 2)) ;; => 5 -(-find-last-index (-partial 'string-lessp "baz") '("q" "foo" "baz")) ;; => 1 -``` - -#### -find-indices `(pred list)` - -Return the indices of all elements in `list` satisfying the -predicate `pred`, in ascending order. - -```el -(-find-indices 'even? '(2 4 1 6 3 3 5 8)) ;; => (0 1 3 7) -(--find-indices (< 5 it) '(2 4 1 6 3 3 5 8)) ;; => (3 7) -(-find-indices (-partial 'string-lessp "baz") '("bar" "foo" "baz")) ;; => (1) -``` - -#### -grade-up `(comparator list)` - -Grade elements of `list` using `comparator` relation. -This yields a permutation vector such that applying this -permutation to `list` sorts it in ascending order. - -```el -(-grade-up #'< '(3 1 4 2 1 3 3)) ;; => (1 4 3 0 5 6 2) -(let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-up #'< l) l)) ;; => (1 1 2 3 3 3 4) -``` - -#### -grade-down `(comparator list)` - -Grade elements of `list` using `comparator` relation. -This yields a permutation vector such that applying this -permutation to `list` sorts it in descending order. - -```el -(-grade-down #'< '(3 1 4 2 1 3 3)) ;; => (2 0 5 6 3 1 4) -(let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-down #'< l) l)) ;; => (4 3 3 3 2 1 1) -``` - -## Set operations - -Operations pretending lists are sets. - -#### -union `(list list2)` - -Return a new list containing the elements of `list` and elements of `list2` that are not in `list`. -The test for equality is done with `equal`, -or with `-compare-fn` if that's non-nil. - -```el -(-union '(1 2 3) '(3 4 5)) ;; => (1 2 3 4 5) -(-union '(1 2 3 4) ()) ;; => (1 2 3 4) -(-union '(1 1 2 2) '(3 2 1)) ;; => (1 1 2 2 3) -``` - -#### -difference `(list list2)` - -Return a new list with only the members of `list` that are not in `list2`. -The test for equality is done with `equal`, -or with `-compare-fn` if that's non-nil. - -```el -(-difference () ()) ;; => () -(-difference '(1 2 3) '(4 5 6)) ;; => (1 2 3) -(-difference '(1 2 3 4) '(3 4 5 6)) ;; => (1 2) -``` - -#### -intersection `(list list2)` - -Return a new list containing only the elements that are members of both `list` and `list2`. -The test for equality is done with `equal`, -or with `-compare-fn` if that's non-nil. - -```el -(-intersection () ()) ;; => () -(-intersection '(1 2 3) '(4 5 6)) ;; => () -(-intersection '(1 2 3 4) '(3 4 5 6)) ;; => (3 4) -``` - -#### -powerset `(list)` - -Return the power set of `list`. - -```el -(-powerset ()) ;; => (nil) -(-powerset '(x y z)) ;; => ((x y z) (x y) (x z) (x) (y z) (y) (z) nil) -``` - -#### -permutations `(list)` - -Return the permutations of `list`. - -```el -(-permutations ()) ;; => (nil) -(-permutations '(1 2)) ;; => ((1 2) (2 1)) -(-permutations '(a b c)) ;; => ((a b c) (a c b) (b a c) (b c a) (c a b) (c b a)) -``` - -#### -distinct `(list)` - -Return a new list with all duplicates removed. -The test for equality is done with `equal`, -or with `-compare-fn` if that's non-nil. - -Alias: `-uniq` - -```el -(-distinct ()) ;; => () -(-distinct '(1 2 2 4)) ;; => (1 2 4) -(-distinct '(t t t)) ;; => (t) -``` - -## Other list operations - -Other list functions not fit to be classified elsewhere. - -#### -rotate `(n list)` - -Rotate `list` `n` places to the right (left if `n` is negative). -The time complexity is O(n). - -```el -(-rotate 3 '(1 2 3 4 5 6 7)) ;; => (5 6 7 1 2 3 4) -(-rotate -3 '(1 2 3 4 5 6 7)) ;; => (4 5 6 7 1 2 3) -(-rotate 16 '(1 2 3 4 5 6 7)) ;; => (6 7 1 2 3 4 5) -``` - -#### -repeat `(n x)` - -Return a new list of length `n` with each element being `x`. -Return nil if `n` is less than 1. - -```el -(-repeat 3 :a) ;; => (:a :a :a) -(-repeat 1 :a) ;; => (:a) -(-repeat 0 :a) ;; => nil -``` - -#### -cons* `(&rest args)` - -Make a new list from the elements of `args`. -The last 2 elements of `args` are used as the final cons of the -result, so if the final element of `args` is not a list, the result -is a dotted list. With no `args`, return nil. - -```el -(-cons* 1 2) ;; => (1 . 2) -(-cons* 1 2 3) ;; => (1 2 . 3) -(-cons* 1) ;; => 1 -``` - -#### -snoc `(list elem &rest elements)` - -Append `elem` to the end of the list. - -This is like `cons`, but operates on the end of list. - -If `elements` is non nil, append these to the list as well. - -```el -(-snoc '(1 2 3) 4) ;; => (1 2 3 4) -(-snoc '(1 2 3) 4 5 6) ;; => (1 2 3 4 5 6) -(-snoc '(1 2 3) '(4 5 6)) ;; => (1 2 3 (4 5 6)) -``` - -#### -interpose `(sep list)` - -Return a new list of all elements in `list` separated by `sep`. - -```el -(-interpose "-" ()) ;; => () -(-interpose "-" '("a")) ;; => ("a") -(-interpose "-" '("a" "b" "c")) ;; => ("a" "-" "b" "-" "c") -``` - -#### -interleave `(&rest lists)` - -Return a new list of the first item in each list, then the second etc. - -```el -(-interleave '(1 2) '("a" "b")) ;; => (1 "a" 2 "b") -(-interleave '(1 2) '("a" "b") '("A" "B")) ;; => (1 "a" "A" 2 "b" "B") -(-interleave '(1 2 3) '("a" "b")) ;; => (1 "a" 2 "b") -``` - -#### -iota `(count &optional start step)` - -Return a list containing `count` numbers. -Starts from `start` and adds `step` each time. The default `start` is -zero, the default `step` is 1. -This function takes its name from the corresponding primitive in -the `apl` language. - -```el -(-iota 6) ;; => (0 1 2 3 4 5) -(-iota 4 2.5 -2) ;; => (2.5 0.5 -1.5 -3.5) -(-iota -1) ;; Wrong type argument: natnump, -1 -``` - -#### -zip-with `(fn list1 list2)` - -Zip the two lists `list1` and `list2` using a function `fn`. This -function is applied pairwise taking as first argument element of -`list1` and as second argument element of `list2` at corresponding -position. - -The anaphoric form `--zip-with` binds the elements from `list1` as symbol `it`, -and the elements from `list2` as symbol `other`. - -```el -(-zip-with '+ '(1 2 3) '(4 5 6)) ;; => (5 7 9) -(-zip-with 'cons '(1 2 3) '(4 5 6)) ;; => ((1 . 4) (2 . 5) (3 . 6)) -(--zip-with (concat it " and " other) '("Batman" "Jekyll") '("Robin" "Hyde")) ;; => ("Batman and Robin" "Jekyll and Hyde") -``` - -#### -zip `(&rest lists)` - -Zip `lists` together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. - -If two lists are provided as arguments, return the groupings as a list -of cons cells. Otherwise, return the groupings as a list of lists. - -Use [`-zip-lists`](#-zip-lists-rest-lists) if you need the return value to always be a list -of lists. - -Alias: `-zip-pair` - -See also: [`-zip-lists`](#-zip-lists-rest-lists) - -```el -(-zip '(1 2 3) '(4 5 6)) ;; => ((1 . 4) (2 . 5) (3 . 6)) -(-zip '(1 2 3) '(4 5 6 7)) ;; => ((1 . 4) (2 . 5) (3 . 6)) -(-zip '(1 2) '(3 4 5) '(6)) ;; => ((1 3 6)) -``` - -#### -zip-lists `(&rest lists)` - -Zip `lists` together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. - -The return value is always list of lists, which is a difference -from `-zip-pair` which returns a cons-cell in case two input -lists are provided. - -See also: [`-zip`](#-zip-rest-lists) - -```el -(-zip-lists '(1 2 3) '(4 5 6)) ;; => ((1 4) (2 5) (3 6)) -(-zip-lists '(1 2 3) '(4 5 6 7)) ;; => ((1 4) (2 5) (3 6)) -(-zip-lists '(1 2) '(3 4 5) '(6)) ;; => ((1 3 6)) -``` - -#### -zip-fill `(fill-value &rest lists)` - -Zip `lists`, with `fill-value` padded onto the shorter lists. The -lengths of the returned groupings are equal to the length of the -longest input list. - -```el -(-zip-fill 0 '(1 2 3 4 5) '(6 7 8 9)) ;; => ((1 . 6) (2 . 7) (3 . 8) (4 . 9) (5 . 0)) -``` - -#### -unzip `(lists)` - -Unzip `lists`. - -This works just like [`-zip`](#-zip-rest-lists) but takes a list of lists instead of -a variable number of arguments, such that - - (-unzip (-zip `l1` `l2` `l3` ...)) - -is identity (given that the lists are the same length). - -Note in particular that calling this on a list of two lists will -return a list of cons-cells such that the above identity works. - -See also: [`-zip`](#-zip-rest-lists) - -```el -(-unzip (-zip '(1 2 3) '(a b c) '("e" "f" "g"))) ;; => ((1 2 3) (a b c) ("e" "f" "g")) -(-unzip '((1 2) (3 4) (5 6) (7 8) (9 10))) ;; => ((1 3 5 7 9) (2 4 6 8 10)) -(-unzip '((1 2) (3 4))) ;; => ((1 . 3) (2 . 4)) -``` - -#### -cycle `(list)` - -Return an infinite circular copy of `list`. -The returned list cycles through the elements of `list` and repeats -from the beginning. - -```el -(-take 5 (-cycle '(1 2 3))) ;; => (1 2 3 1 2) -(-take 7 (-cycle '(1 "and" 3))) ;; => (1 "and" 3 1 "and" 3 1) -(-zip (-cycle '(1 2 3)) '(1 2)) ;; => ((1 . 1) (2 . 2)) -``` - -#### -pad `(fill-value &rest lists)` - -Appends `fill-value` to the end of each list in `lists` such that they -will all have the same length. - -```el -(-pad 0 ()) ;; => (nil) -(-pad 0 '(1)) ;; => ((1)) -(-pad 0 '(1 2 3) '(4 5)) ;; => ((1 2 3) (4 5 0)) -``` - -#### -table `(fn &rest lists)` - -Compute outer product of `lists` using function `fn`. - -The function `fn` should have the same arity as the number of -supplied lists. - -The outer product is computed by applying fn to all possible -combinations created by taking one element from each list in -order. The dimension of the result is (length lists). - -See also: [`-table-flat`](#-table-flat-fn-rest-lists) - -```el -(-table '* '(1 2 3) '(1 2 3)) ;; => ((1 2 3) (2 4 6) (3 6 9)) -(-table (lambda (a b) (-sum (-zip-with '* a b))) '((1 2) (3 4)) '((1 3) (2 4))) ;; => ((7 15) (10 22)) -(apply '-table 'list (-repeat 3 '(1 2))) ;; => ((((1 1 1) (2 1 1)) ((1 2 1) (2 2 1))) (((1 1 2) (2 1 2)) ((1 2 2) (2 2 2)))) -``` - -#### -table-flat `(fn &rest lists)` - -Compute flat outer product of `lists` using function `fn`. - -The function `fn` should have the same arity as the number of -supplied lists. - -The outer product is computed by applying fn to all possible -combinations created by taking one element from each list in -order. The results are flattened, ignoring the tensor structure -of the result. This is equivalent to calling: - - (-flatten-n (1- (length lists)) (apply '-table fn lists)) - -but the implementation here is much more efficient. - -See also: [`-flatten-n`](#-flatten-n-num-list), [`-table`](#-table-fn-rest-lists) - -```el -(-table-flat 'list '(1 2 3) '(a b c)) ;; => ((1 a) (2 a) (3 a) (1 b) (2 b) (3 b) (1 c) (2 c) (3 c)) -(-table-flat '* '(1 2 3) '(1 2 3)) ;; => (1 2 3 2 4 6 3 6 9) -(apply '-table-flat 'list (-repeat 3 '(1 2))) ;; => ((1 1 1) (2 1 1) (1 2 1) (2 2 1) (1 1 2) (2 1 2) (1 2 2) (2 2 2)) -``` - -#### -first `(pred list)` - -Return the first item in `list` for which `pred` returns non-nil. -Return nil if no such element is found. -To get the first item in the list no questions asked, use `car`. - -Alias: `-find`. - -This function's anaphoric counterpart is `--first`. - -```el -(-first #'natnump '(-1 0 1)) ;; => 0 -(-first #'null '(1 2 3)) ;; => nil -(--first (> it 2) '(1 2 3)) ;; => 3 -``` - -#### -last `(pred list)` - -Return the last x in `list` where (`pred` x) is non-nil, else nil. - -```el -(-last 'even? '(1 2 3 4 5 6 3 3 3)) ;; => 6 -(-last 'even? '(1 3 7 5 9)) ;; => nil -(--last (> (length it) 3) '("a" "looong" "word" "and" "short" "one")) ;; => "short" -``` - -#### -first-item `(list)` - -Return the first item of `list`, or nil on an empty list. - -See also: [`-second-item`](#-second-item-list), [`-last-item`](#-last-item-list). - -```el -(-first-item '(1 2 3)) ;; => 1 -(-first-item nil) ;; => nil -(let ((list (list 1 2 3))) (setf (-first-item list) 5) list) ;; => (5 2 3) -``` - -#### -second-item `(list)` - -Return the second item of `list`, or nil if `list` is too short. - -See also: [`-third-item`](#-third-item-list). - -```el -(-second-item '(1 2 3)) ;; => 2 -(-second-item nil) ;; => nil -``` - -#### -third-item `(list)` - -Return the third item of `list`, or nil if `list` is too short. - -See also: [`-fourth-item`](#-fourth-item-list). - -```el -(-third-item '(1 2 3)) ;; => 3 -(-third-item nil) ;; => nil -``` - -#### -fourth-item `(list)` - -Return the fourth item of `list`, or nil if `list` is too short. - -See also: [`-fifth-item`](#-fifth-item-list). - -```el -(-fourth-item '(1 2 3 4)) ;; => 4 -(-fourth-item nil) ;; => nil -``` - -#### -fifth-item `(list)` - -Return the fifth item of `list`, or nil if `list` is too short. - -See also: [`-last-item`](#-last-item-list). - -```el -(-fifth-item '(1 2 3 4 5)) ;; => 5 -(-fifth-item nil) ;; => nil -``` - -#### -last-item `(list)` - -Return the last item of `list`, or nil on an empty list. - -```el -(-last-item '(1 2 3)) ;; => 3 -(-last-item nil) ;; => nil -(let ((list (list 1 2 3))) (setf (-last-item list) 5) list) ;; => (1 2 5) -``` - -#### -butlast `(list)` - -Return a list of all items in list except for the last. - -```el -(-butlast '(1 2 3)) ;; => (1 2) -(-butlast '(1 2)) ;; => (1) -(-butlast '(1)) ;; => nil -``` - -#### -sort `(comparator list)` - -Sort `list`, stably, comparing elements using `comparator`. -Return the sorted list. `list` is `not` modified by side effects. -`comparator` is called with two elements of `list`, and should return non-nil -if the first element should sort before the second. - -```el -(-sort '< '(3 1 2)) ;; => (1 2 3) -(-sort '> '(3 1 2)) ;; => (3 2 1) -(--sort (< it other) '(3 1 2)) ;; => (1 2 3) -``` - -#### -list `(arg)` - -Ensure `arg` is a list. -If `arg` is already a list, return it as is (not a copy). -Otherwise, return a new list with `arg` as its only element. - -Another supported calling convention is (-list &rest `args`). -In this case, if `arg` is not a list, a new list with all of -`args` as elements is returned. This use is supported for -backward compatibility and is otherwise deprecated. - -```el -(-list 1) ;; => (1) -(-list ()) ;; => () -(-list '(1 2 3)) ;; => (1 2 3) -``` - -#### -fix `(fn list)` - -Compute the (least) fixpoint of `fn` with initial input `list`. - -`fn` is called at least once, results are compared with `equal`. - -```el -(-fix (lambda (l) (-non-nil (--mapcat (-split-at (/ (length it) 2) it) l))) '((1 2 3))) ;; => ((1) (2) (3)) -(let ((l '((starwars scifi) (jedi starwars warrior)))) (--fix (-uniq (--mapcat (cons it (cdr (assq it l))) it)) '(jedi book))) ;; => (jedi starwars warrior scifi book) -``` - -## Tree operations - -Functions pretending lists are trees. - -#### -tree-seq `(branch children tree)` - -Return a sequence of the nodes in `tree`, in depth-first search order. - -`branch` is a predicate of one argument that returns non-nil if the -passed argument is a branch, that is, a node that can have children. - -`children` is a function of one argument that returns the children -of the passed branch node. - -Non-branch nodes are simply copied. - -```el -(-tree-seq 'listp 'identity '(1 (2 3) 4 (5 (6 7)))) ;; => ((1 (2 3) 4 (5 (6 7))) 1 (2 3) 2 3 4 (5 (6 7)) 5 (6 7) 6 7) -(-tree-seq 'listp 'reverse '(1 (2 3) 4 (5 (6 7)))) ;; => ((1 (2 3) 4 (5 (6 7))) (5 (6 7)) (6 7) 7 6 5 4 (2 3) 3 2 1) -(--tree-seq (vectorp it) (append it nil) [1 [2 3] 4 [5 [6 7]]]) ;; => ([1 [2 3] 4 [5 [6 7]]] 1 [2 3] 2 3 4 [5 [6 7]] 5 [6 7] 6 7) -``` - -#### -tree-map `(fn tree)` - -Apply `fn` to each element of `tree` while preserving the tree structure. - -```el -(-tree-map '1+ '(1 (2 3) (4 (5 6) 7))) ;; => (2 (3 4) (5 (6 7) 8)) -(-tree-map '(lambda (x) (cons x (expt 2 x))) '(1 (2 3) 4)) ;; => ((1 . 2) ((2 . 4) (3 . 8)) (4 . 16)) -(--tree-map (length it) '("" ("

" "text" "

") "")) ;; => (6 (3 4 4) 7) -``` - -#### -tree-map-nodes `(pred fun tree)` - -Call `fun` on each node of `tree` that satisfies `pred`. - -If `pred` returns nil, continue descending down this node. If `pred` -returns non-nil, apply `fun` to this node and do not descend -further. - -```el -(-tree-map-nodes 'vectorp (lambda (x) (-sum (append x nil))) '(1 [2 3] 4 (5 [6 7] 8))) ;; => (1 5 4 (5 13 8)) -(-tree-map-nodes 'keywordp (lambda (x) (symbol-name x)) '(1 :foo 4 ((5 6 :bar) :baz 8))) ;; => (1 ":foo" 4 ((5 6 ":bar") ":baz" 8)) -(--tree-map-nodes (eq (car-safe it) 'add-mode) (-concat it (list :mode 'emacs-lisp-mode)) '(with-mode emacs-lisp-mode (foo bar) (add-mode a b) (baz (add-mode c d)))) ;; => (with-mode emacs-lisp-mode (foo bar) (add-mode a b :mode emacs-lisp-mode) (baz (add-mode c d :mode emacs-lisp-mode))) -``` - -#### -tree-reduce `(fn tree)` - -Use `fn` to reduce elements of list `tree`. -If elements of `tree` are lists themselves, apply the reduction recursively. - -`fn` is first applied to first element of the list and second -element, then on this result and third element from the list etc. - -See [`-reduce-r`](#-reduce-r-fn-list) for how exactly are lists of zero or one element handled. - -```el -(-tree-reduce '+ '(1 (2 3) (4 5))) ;; => 15 -(-tree-reduce 'concat '("strings" (" on" " various") ((" levels")))) ;; => "strings on various levels" -(--tree-reduce (cond ((stringp it) (concat it " " acc)) (t (let ((sn (symbol-name it))) (concat "<" sn ">" acc "")))) '(body (p "some words") (div "more" (b "bold") "words"))) ;; => "

some words

more bold words
" -``` - -#### -tree-reduce-from `(fn init-value tree)` - -Use `fn` to reduce elements of list `tree`. -If elements of `tree` are lists themselves, apply the reduction recursively. - -`fn` is first applied to `init-value` and first element of the list, -then on this result and second element from the list etc. - -The initial value is ignored on cons pairs as they always contain -two elements. - -```el -(-tree-reduce-from '+ 1 '(1 (1 1) ((1)))) ;; => 8 -(--tree-reduce-from (-concat acc (list it)) nil '(1 (2 3 (4 5)) (6 7))) ;; => ((7 6) ((5 4) 3 2) 1) -``` - -#### -tree-mapreduce `(fn folder tree)` - -Apply `fn` to each element of `tree`, and make a list of the results. -If elements of `tree` are lists themselves, apply `fn` recursively to -elements of these nested lists. - -Then reduce the resulting lists using `folder` and initial value -`init-value`. See [`-reduce-r-from`](#-reduce-r-from-fn-init-list). - -This is the same as calling [`-tree-reduce`](#-tree-reduce-fn-tree) after [`-tree-map`](#-tree-map-fn-tree) -but is twice as fast as it only traverse the structure once. - -```el -(-tree-mapreduce 'list 'append '(1 (2 (3 4) (5 6)) (7 (8 9)))) ;; => (1 2 3 4 5 6 7 8 9) -(--tree-mapreduce 1 (+ it acc) '(1 (2 (4 9) (2 1)) (7 (4 3)))) ;; => 9 -(--tree-mapreduce 0 (max acc (1+ it)) '(1 (2 (4 9) (2 1)) (7 (4 3)))) ;; => 3 -``` - -#### -tree-mapreduce-from `(fn folder init-value tree)` - -Apply `fn` to each element of `tree`, and make a list of the results. -If elements of `tree` are lists themselves, apply `fn` recursively to -elements of these nested lists. - -Then reduce the resulting lists using `folder` and initial value -`init-value`. See [`-reduce-r-from`](#-reduce-r-from-fn-init-list). - -This is the same as calling [`-tree-reduce-from`](#-tree-reduce-from-fn-init-value-tree) after [`-tree-map`](#-tree-map-fn-tree) -but is twice as fast as it only traverse the structure once. - -```el -(-tree-mapreduce-from 'identity '* 1 '(1 (2 (3 4) (5 6)) (7 (8 9)))) ;; => 362880 -(--tree-mapreduce-from (+ it it) (cons it acc) nil '(1 (2 (4 9) (2 1)) (7 (4 3)))) ;; => (2 (4 (8 18) (4 2)) (14 (8 6))) -(concat "{" (--tree-mapreduce-from (cond ((-cons-pair? it) (concat (symbol-name (car it)) " -> " (symbol-name (cdr it)))) (t (concat (symbol-name it) " : {"))) (concat it (unless (or (equal acc "}") (equal (substring it (1- (length it))) "{")) ", ") acc) "}" '((elisp-mode (foo (bar . booze)) (baz . qux)) (c-mode (foo . bla) (bum . bam))))) ;; => "{elisp-mode : {foo : {bar -> booze}, baz -> qux}, c-mode : {foo -> bla, bum -> bam}}" -``` - -#### -clone `(list)` - -Create a deep copy of `list`. -The new list has the same elements and structure but all cons are -replaced with new ones. This is useful when you need to clone a -structure such as plist or alist. - -```el -(let* ((a '(1 2 3)) (b (-clone a))) (nreverse a) b) ;; => (1 2 3) -``` - -## Threading macros - -Macros that conditionally combine sequential forms for brevity -or readability. - -#### -> `(x &optional form &rest more)` - -Thread the expr through the forms. Insert `x` as the second item -in the first form, making a list of it if it is not a list -already. If there are more forms, insert the first form as the -second item in second form, etc. - -```el -(-> '(2 3 5)) ;; => (2 3 5) -(-> '(2 3 5) (append '(8 13))) ;; => (2 3 5 8 13) -(-> '(2 3 5) (append '(8 13)) (-slice 1 -1)) ;; => (3 5 8) -``` - -#### ->> `(x &optional form &rest more)` - -Thread the expr through the forms. Insert `x` as the last item -in the first form, making a list of it if it is not a list -already. If there are more forms, insert the first form as the -last item in second form, etc. - -```el -(->> '(1 2 3) (-map 'square)) ;; => (1 4 9) -(->> '(1 2 3) (-map 'square) (-remove 'even?)) ;; => (1 9) -(->> '(1 2 3) (-map 'square) (-reduce '+)) ;; => 14 -``` - -#### --> `(x &rest forms)` - -Starting with the value of `x`, thread each expression through `forms`. - -Insert `x` at the position signified by the symbol `it` in the first -form. If there are more forms, insert the first form at the position -signified by `it` in in second form, etc. - -```el -(--> "def" (concat "abc" it "ghi")) ;; => "abcdefghi" -(--> "def" (concat "abc" it "ghi") (upcase it)) ;; => "ABCDEFGHI" -(--> "def" (concat "abc" it "ghi") upcase) ;; => "ABCDEFGHI" -``` - -#### -as-> `(value variable &rest forms)` - -Starting with `value`, thread `variable` through `forms`. - -In the first form, bind `variable` to `value`. In the second form, bind -`variable` to the result of the first form, and so forth. - -```el -(-as-> 3 my-var (1+ my-var) (list my-var) (mapcar (lambda (ele) (* 2 ele)) my-var)) ;; => (8) -(-as-> 3 my-var 1+) ;; => 4 -(-as-> 3 my-var) ;; => 3 -``` - -#### -some-> `(x &optional form &rest more)` - -When expr is non-nil, thread it through the first form (via [`->`](#--x-optional-form-rest-more)), -and when that result is non-nil, through the next form, etc. - -```el -(-some-> '(2 3 5)) ;; => (2 3 5) -(-some-> 5 square) ;; => 25 -(-some-> 5 even? square) ;; => nil -``` - -#### -some->> `(x &optional form &rest more)` - -When expr is non-nil, thread it through the first form (via [`->>`](#--x-optional-form-rest-more)), -and when that result is non-nil, through the next form, etc. - -```el -(-some->> '(1 2 3) (-map 'square)) ;; => (1 4 9) -(-some->> '(1 3 5) (-last 'even?) (+ 100)) ;; => nil -(-some->> '(2 4 6) (-last 'even?) (+ 100)) ;; => 106 -``` - -#### -some--> `(expr &rest forms)` - -Thread `expr` through `forms` via [`-->`](#---x-rest-forms), while the result is non-nil. -When `expr` evaluates to non-nil, thread the result through the -first of `forms`, and when that result is non-nil, thread it -through the next form, etc. - -```el -(-some--> "def" (concat "abc" it "ghi")) ;; => "abcdefghi" -(-some--> nil (concat "abc" it "ghi")) ;; => nil -(-some--> '(0 1) (-remove #'natnump it) (append it it) (-map #'1+ it)) ;; => () -``` - -#### -doto `(init &rest forms)` - -Evaluate `init` and pass it as argument to `forms` with [`->`](#--x-optional-form-rest-more). -The `result` of evaluating `init` is threaded through each of `forms` -individually using [`->`](#--x-optional-form-rest-more), which see. The return value is `result`, -which `forms` may have modified by side effect. - -```el -(-doto (list 1 2 3) pop pop) ;; => (3) -(-doto (cons 1 2) (setcar 3) (setcdr 4)) ;; => (3 . 4) -(gethash 'k (--doto (make-hash-table) (puthash 'k 'v it))) ;; => v -``` - -## Binding - -Macros that combine `let` and `let*` with destructuring and flow control. - -#### -when-let `((var val) &rest body)` - -If `val` evaluates to non-nil, bind it to `var` and execute body. - -Note: binding is done according to [`-let`](#-let-varlist-rest-body). - -```el -(-when-let (match-index (string-match "d" "abcd")) (+ match-index 2)) ;; => 5 -(-when-let ((&plist :foo foo) (list :foo "foo")) foo) ;; => "foo" -(-when-let ((&plist :foo foo) (list :bar "bar")) foo) ;; => nil -``` - -#### -when-let* `(vars-vals &rest body)` - -If all `vals` evaluate to true, bind them to their corresponding -`vars` and execute body. `vars-vals` should be a list of (`var` `val`) -pairs. - -Note: binding is done according to [`-let*`](#-let-varlist-rest-body). `vals` are evaluated -sequentially, and evaluation stops after the first nil `val` is -encountered. - -```el -(-when-let* ((x 5) (y 3) (z (+ y 4))) (+ x y z)) ;; => 15 -(-when-let* ((x 5) (y nil) (z 7)) (+ x y z)) ;; => nil -``` - -#### -if-let `((var val) then &rest else)` - -If `val` evaluates to non-nil, bind it to `var` and do `then`, -otherwise do `else`. - -Note: binding is done according to [`-let`](#-let-varlist-rest-body). - -```el -(-if-let (match-index (string-match "d" "abc")) (+ match-index 3) 7) ;; => 7 -(--if-let (even? 4) it nil) ;; => t -``` - -#### -if-let* `(vars-vals then &rest else)` - -If all `vals` evaluate to true, bind them to their corresponding -`vars` and do `then`, otherwise do `else`. `vars-vals` should be a list -of (`var` `val`) pairs. - -Note: binding is done according to [`-let*`](#-let-varlist-rest-body). `vals` are evaluated -sequentially, and evaluation stops after the first nil `val` is -encountered. - -```el -(-if-let* ((x 5) (y 3) (z 7)) (+ x y z) "foo") ;; => 15 -(-if-let* ((x 5) (y nil) (z 7)) (+ x y z) "foo") ;; => "foo" -(-if-let* (((_ _ x) '(nil nil 7))) x) ;; => 7 -``` - -#### -let `(varlist &rest body)` - -Bind variables according to `varlist` then eval `body`. - -`varlist` is a list of lists of the form (`pattern` `source`). Each -`pattern` is matched against the `source` "structurally". `source` -is only evaluated once for each `pattern`. Each `pattern` is matched -recursively, and can therefore contain sub-patterns which are -matched against corresponding sub-expressions of `source`. - -All the SOURCEs are evalled before any symbols are -bound (i.e. "in parallel"). - -If `varlist` only contains one (`pattern` `source`) element, you can -optionally specify it using a vector and discarding the -outer-most parens. Thus - - (-let ((`pattern` `source`)) ...) - -becomes - - (-let [`pattern` `source`] ...). - -[`-let`](#-let-varlist-rest-body) uses a convention of not binding places (symbols) starting -with _ whenever it's possible. You can use this to skip over -entries you don't care about. However, this is not *always* -possible (as a result of implementation) and these symbols might -get bound to undefined values. - -Following is the overview of supported patterns. Remember that -patterns can be matched recursively, so every a, b, aK in the -following can be a matching construct and not necessarily a -symbol/variable. - -Symbol: - - a - bind the `source` to `a`. This is just like regular `let`. - -Conses and lists: - - (a) - bind `car` of cons/list to `a` - - (a . b) - bind car of cons to `a` and `cdr` to `b` - - (a b) - bind car of list to `a` and `cadr` to `b` - - (a1 a2 a3 ...) - bind 0th car of list to `a1`, 1st to `a2`, 2nd to `a3`... - - (a1 a2 a3 ... aN . rest) - as above, but bind the `n`th cdr to `rest`. - -Vectors: - - [a] - bind 0th element of a non-list sequence to `a` (works with - vectors, strings, bit arrays...) - - [a1 a2 a3 ...] - bind 0th element of non-list sequence to `a0`, 1st to - `a1`, 2nd to `a2`, ... - If the `pattern` is shorter than `source`, the values at - places not in `pattern` are ignored. - If the `pattern` is longer than `source`, an `error` is - thrown. - - [a1 a2 a3 ... &rest rest] - as above, but bind the rest of - the sequence to `rest`. This is - conceptually the same as improper list - matching (a1 a2 ... aN . rest) - -Key/value stores: - - (&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the - `source` plist to aK. If the - value is not found, aK is nil. - Uses `plist-get` to fetch values. - - (&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the - `source` alist to aK. If the - value is not found, aK is nil. - Uses `assoc` to fetch values. - - (&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the - `source` hash table to aK. If the - value is not found, aK is nil. - Uses `gethash` to fetch values. - -Further, special keyword &keys supports "inline" matching of -plist-like key-value pairs, similarly to &keys keyword of -`cl-defun`. - - (a1 a2 ... aN &keys key1 b1 ... keyN bK) - -This binds `n` values from the list to a1 ... aN, then interprets -the cdr as a plist (see key/value matching above). - -`a` shorthand notation for kv-destructuring exists which allows the -patterns be optionally left out and derived from the key name in -the following fashion: - -- a key :foo is converted into `foo` pattern, -- a key 'bar is converted into `bar` pattern, -- a key "baz" is converted into `baz` pattern. - -That is, the entire value under the key is bound to the derived -variable without any further destructuring. - -This is possible only when the form following the key is not a -valid pattern (i.e. not a symbol, a cons cell or a vector). -Otherwise the matching proceeds as usual and in case of an -invalid spec fails with an error. - -Thus the patterns are normalized as follows: - - ;; derive all the missing patterns - (&plist :foo 'bar "baz") => (&plist :foo foo 'bar bar "baz" baz) - - ;; we can specify some but not others - (&plist :foo 'bar explicit-bar) => (&plist :foo foo 'bar explicit-bar) - - ;; nothing happens, we store :foo in x - (&plist :foo x) => (&plist :foo x) - - ;; nothing happens, we match recursively - (&plist :foo (a b c)) => (&plist :foo (a b c)) - -You can name the source using the syntax `symbol` &as `pattern`. -This syntax works with lists (proper or improper), vectors and -all types of maps. - - (list &as a b c) (list 1 2 3) - -binds `a` to 1, `b` to 2, `c` to 3 and `list` to (1 2 3). - -Similarly: - - (bounds &as beg . end) (cons 1 2) - -binds `beg` to 1, `end` to 2 and `bounds` to (1 . 2). - - (items &as first . rest) (list 1 2 3) - -binds `first` to 1, `rest` to (2 3) and `items` to (1 2 3) - - [vect &as _ b c] [1 2 3] - -binds `b` to 2, `c` to 3 and `vect` to [1 2 3] (_ avoids binding as usual). - - (plist &as &plist :b b) (list :a 1 :b 2 :c 3) - -binds `b` to 2 and `plist` to (:a 1 :b 2 :c 3). Same for &alist and &hash. - -This is especially useful when we want to capture the result of a -computation and destructure at the same time. Consider the -form (function-returning-complex-structure) returning a list of -two vectors with two items each. We want to capture this entire -result and pass it to another computation, but at the same time -we want to get the second item from each vector. We can achieve -it with pattern - - (result &as [_ a] [_ b]) (function-returning-complex-structure) - -Note: Clojure programmers may know this feature as the ":as -binding". The difference is that we put the &as at the front -because we need to support improper list binding. - -```el -(-let (([a (b c) d] [1 (2 3) 4])) (list a b c d)) ;; => (1 2 3 4) -(-let [(a b c . d) (list 1 2 3 4 5 6)] (list a b c d)) ;; => (1 2 3 (4 5 6)) -(-let [(&plist :foo foo :bar bar) (list :baz 3 :foo 1 :qux 4 :bar 2)] (list foo bar)) ;; => (1 2) -``` - -#### -let* `(varlist &rest body)` - -Bind variables according to `varlist` then eval `body`. - -`varlist` is a list of lists of the form (`pattern` `source`). Each -`pattern` is matched against the `source` structurally. `source` is -only evaluated once for each `pattern`. - -Each `source` can refer to the symbols already bound by this -`varlist`. This is useful if you want to destructure `source` -recursively but also want to name the intermediate structures. - -See [`-let`](#-let-varlist-rest-body) for the list of all possible patterns. - -```el -(-let* (((a . b) (cons 1 2)) ((c . d) (cons 3 4))) (list a b c d)) ;; => (1 2 3 4) -(-let* (((a . b) (cons 1 (cons 2 3))) ((c . d) b)) (list a b c d)) ;; => (1 (2 . 3) 2 3) -(-let* (((&alist "foo" foo "bar" bar) (list (cons "foo" 1) (cons "bar" (list 'a 'b 'c)))) ((a b c) bar)) (list foo a b c bar)) ;; => (1 a b c (a b c)) -``` - -#### -lambda `(match-form &rest body)` - -Return a lambda which destructures its input as `match-form` and executes `body`. - -Note that you have to enclose the `match-form` in a pair of parens, -such that: - - (-lambda (x) body) - (-lambda (x y ...) body) - -has the usual semantics of `lambda`. Furthermore, these get -translated into normal `lambda`, so there is no performance -penalty. - -See [`-let`](#-let-varlist-rest-body) for a description of the destructuring mechanism. - -```el -(-map (-lambda ((x y)) (+ x y)) '((1 2) (3 4) (5 6))) ;; => (3 7 11) -(-map (-lambda ([x y]) (+ x y)) '([1 2] [3 4] [5 6])) ;; => (3 7 11) -(funcall (-lambda ((_ . a) (_ . b)) (-concat a b)) '(1 2 3) '(4 5 6)) ;; => (2 3 5 6) -``` - -#### -setq `([match-form val] ...)` - -Bind each `match-form` to the value of its `val`. - -`match-form` destructuring is done according to the rules of [`-let`](#-let-varlist-rest-body). - -This macro allows you to bind multiple variables by destructuring -the value, so for example: - - (-setq (a b) x - (&plist :c c) plist) - -expands roughly speaking to the following code - - (setq a (car x) - b (cadr x) - c (plist-get plist :c)) - -Care is taken to only evaluate each `val` once so that in case of -multiple assignments it does not cause unexpected side effects. - -```el -(let (a) (-setq a 1) a) ;; => 1 -(let (a b) (-setq (a b) (list 1 2)) (list a b)) ;; => (1 2) -(let (c) (-setq (&plist :c c) (list :c "c")) c) ;; => "c" -``` - -## Side effects - -Functions iterating over lists for side effect only. - -#### -each `(list fn)` - -Call `fn` on each element of `list`. -Return nil; this function is intended for side effects. - -Its anaphoric counterpart is `--each`. - -For access to the current element's index in `list`, see -[`-each-indexed`](#-each-indexed-list-fn). - -```el -(let (l) (-each '(1 2 3) (lambda (x) (push x l))) l) ;; => (3 2 1) -(let (l) (--each '(1 2 3) (push it l)) l) ;; => (3 2 1) -(-each '(1 2 3) #'identity) ;; => nil -``` - -#### -each-while `(list pred fn)` - -Call `fn` on each `item` in `list`, while (`pred` `item`) is non-nil. -Once an `item` is reached for which `pred` returns nil, `fn` is no -longer called. Return nil; this function is intended for side -effects. - -Its anaphoric counterpart is `--each-while`. - -```el -(let (l) (-each-while '(2 4 5 6) #'even? (lambda (x) (push x l))) l) ;; => (4 2) -(let (l) (--each-while '(1 2 3 4) (< it 3) (push it l)) l) ;; => (2 1) -(let ((s 0)) (--each-while '(1 3 4 5) (< it 5) (setq s (+ s it))) s) ;; => 8 -``` - -#### -each-indexed `(list fn)` - -Call `fn` on each index and element of `list`. -For each `item` at `index` in `list`, call (funcall `fn` `index` `item`). -Return nil; this function is intended for side effects. - -See also: [`-map-indexed`](#-map-indexed-fn-list). - -```el -(let (l) (-each-indexed '(a b c) (lambda (i x) (push (list x i) l))) l) ;; => ((c 2) (b 1) (a 0)) -(let (l) (--each-indexed '(a b c) (push (list it it-index) l)) l) ;; => ((c 2) (b 1) (a 0)) -(let (l) (--each-indexed () (push it l)) l) ;; => () -``` - -#### -each-r `(list fn)` - -Call `fn` on each element of `list` in reversed order. -Return nil; this function is intended for side effects. - -Its anaphoric counterpart is `--each-r`. - -```el -(let (l) (-each-r '(1 2 3) (lambda (x) (push x l))) l) ;; => (1 2 3) -(let (l) (--each-r '(1 2 3) (push it l)) l) ;; => (1 2 3) -(-each-r '(1 2 3) #'identity) ;; => nil -``` - -#### -each-r-while `(list pred fn)` - -Call `fn` on each `item` in reversed `list`, while (`pred` `item`) is non-nil. -Once an `item` is reached for which `pred` returns nil, `fn` is no -longer called. Return nil; this function is intended for side -effects. - -Its anaphoric counterpart is `--each-r-while`. - -```el -(let (l) (-each-r-while '(2 4 5 6) #'even? (lambda (x) (push x l))) l) ;; => (6) -(let (l) (--each-r-while '(1 2 3 4) (>= it 3) (push it l)) l) ;; => (3 4) -(let ((s 0)) (--each-r-while '(1 2 3 5) (> it 1) (setq s (+ s it))) s) ;; => 10 -``` - -#### -dotimes `(num fn)` - -Call `fn` `num` times, presumably for side effects. -`fn` is called with a single argument on successive integers -running from 0, inclusive, to `num`, exclusive. `fn` is not called -if `num` is less than 1. - -This function's anaphoric counterpart is `--dotimes`. - -```el -(let (s) (-dotimes 3 (lambda (n) (push n s))) s) ;; => (2 1 0) -(let (s) (-dotimes 0 (lambda (n) (push n s))) s) ;; => () -(let (s) (--dotimes 5 (push it s)) s) ;; => (4 3 2 1 0) -``` - -## Destructive operations - -Macros that modify variables holding lists. - -#### !cons `(car cdr)` - -Destructive: Set `cdr` to the cons of `car` and `cdr`. - -```el -(let (l) (!cons 5 l) l) ;; => (5) -(let ((l '(3))) (!cons 5 l) l) ;; => (5 3) -``` - -#### !cdr `(list)` - -Destructive: Set `list` to the cdr of `list`. - -```el -(let ((l '(3))) (!cdr l) l) ;; => () -(let ((l '(3 5))) (!cdr l) l) ;; => (5) -``` - -## Function combinators - -Functions that manipulate and compose other functions. - -#### -partial `(fun &rest args)` - -Return a function that is a partial application of `fun` to `args`. -`args` is a list of the first `n` arguments to pass to `fun`. -The result is a new function which does the same as `fun`, except that -the first `n` arguments are fixed at the values with which this function -was called. - -```el -(funcall (-partial #'+ 5)) ;; => 5 -(funcall (-partial #'- 5) 3) ;; => 2 -(funcall (-partial #'+ 5 2) 3) ;; => 10 -``` - -#### -rpartial `(fn &rest args)` - -Return a function that is a partial application of `fn` to `args`. -`args` is a list of the last `n` arguments to pass to `fn`. The result -is a new function which does the same as `fn`, except that the last -`n` arguments are fixed at the values with which this function was -called. This is like [`-partial`](#-partial-fun-rest-args), except the arguments are fixed -starting from the right rather than the left. - -```el -(funcall (-rpartial #'- 5)) ;; => -5 -(funcall (-rpartial #'- 5) 8) ;; => 3 -(funcall (-rpartial #'- 5 2) 10) ;; => 3 -``` - -#### -juxt `(&rest fns)` - -Return a function that is the juxtaposition of `fns`. -The returned function takes a variable number of `args`, applies -each of `fns` in turn to `args`, and returns the list of results. - -```el -(funcall (-juxt) 1 2) ;; => () -(funcall (-juxt #'+ #'- #'* #'/) 7 5) ;; => (12 2 35 1) -(mapcar (-juxt #'number-to-string #'1+) '(1 2)) ;; => (("1" 2) ("2" 3)) -``` - -#### -compose `(&rest fns)` - -Compose `fns` into a single composite function. -Return a function that takes a variable number of `args`, applies -the last function in `fns` to `args`, and returns the result of -calling each remaining function on the result of the previous -function, right-to-left. If no `fns` are given, return a variadic -`identity` function. - -```el -(funcall (-compose #'- #'1+ #'+) 1 2 3) ;; => -7 -(funcall (-compose #'identity #'1+) 3) ;; => 4 -(mapcar (-compose #'not #'stringp) '(nil "")) ;; => (t nil) -``` - -#### -applify `(fn)` - -Return a function that applies `fn` to a single list of args. -This changes the arity of `fn` from taking `n` distinct arguments to -taking 1 argument which is a list of `n` arguments. - -```el -(funcall (-applify #'+) nil) ;; => 0 -(mapcar (-applify #'+) '((1 1 1) (1 2 3) (5 5 5))) ;; => (3 6 15) -(funcall (-applify #'<) '(3 6)) ;; => t -``` - -#### -on `(op trans)` - -Return a function that calls `trans` on each arg and `op` on the results. -The returned function takes a variable number of arguments, calls -the function `trans` on each one in turn, and then passes those -results as the list of arguments to `op`, in the same order. - -For example, the following pairs of expressions are morally -equivalent: - - (funcall (-on #'+ #'1+) 1 2 3) = (+ (1+ 1) (1+ 2) (1+ 3)) - (funcall (-on #'+ #'1+)) = (+) - -```el -(-sort (-on #'< #'length) '((1 2 3) (1) (1 2))) ;; => ((1) (1 2) (1 2 3)) -(funcall (-on #'min #'string-to-number) "22" "2" "1" "12") ;; => 1 -(-min-by (-on #'> #'length) '((1 2 3) (4) (1 2))) ;; => (4) -``` - -#### -flip `(fn)` - -Return a function that calls `fn` with its arguments reversed. -The returned function takes the same number of arguments as `fn`. - -For example, the following two expressions are morally -equivalent: - - (funcall (-flip #'-) 1 2) = (- 2 1) - -See also: [`-rotate-args`](#-rotate-args-n-fn). - -```el -(-sort (-flip #'<) '(4 3 6 1)) ;; => (6 4 3 1) -(funcall (-flip #'-) 3 2 1 10) ;; => 4 -(funcall (-flip #'1+) 1) ;; => 2 -``` - -#### -rotate-args `(n fn)` - -Return a function that calls `fn` with args rotated `n` places to the right. -The returned function takes the same number of arguments as `fn`, -rotates the list of arguments `n` places to the right (left if `n` is -negative) just like [`-rotate`](#-rotate-n-list), and applies `fn` to the result. - -See also: [`-flip`](#-flip-fn). - -```el -(funcall (-rotate-args -1 #'list) 1 2 3 4) ;; => (2 3 4 1) -(funcall (-rotate-args 1 #'-) 1 10 100) ;; => 89 -(funcall (-rotate-args 2 #'list) 3 4 5 1 2) ;; => (1 2 3 4 5) -``` - -#### -const `(c)` - -Return a function that returns `c` ignoring any additional arguments. - -In types: a -> b -> a - -```el -(funcall (-const 2) 1 3 "foo") ;; => 2 -(mapcar (-const 1) '("a" "b" "c" "d")) ;; => (1 1 1 1) -(-sum (mapcar (-const 1) '("a" "b" "c" "d"))) ;; => 4 -``` - -#### -cut `(&rest params)` - -Take n-ary function and n arguments and specialize some of them. -Arguments denoted by <> will be left unspecialized. - -See `srfi-26` for detailed description. - -```el -(funcall (-cut list 1 <> 3 <> 5) 2 4) ;; => (1 2 3 4 5) -(-map (-cut funcall <> 5) `(1+ 1- ,(lambda (x) (/ 1.0 x)))) ;; => (6 4 0.2) -(-map (-cut <> 1 2 3) '(list vector string)) ;; => ((1 2 3) [1 2 3] "\1\2\3") -``` - -#### -not `(pred)` - -Return a predicate that negates the result of `pred`. -The returned predicate passes its arguments to `pred`. If `pred` -returns nil, the result is non-nil; otherwise the result is nil. - -See also: [`-andfn`](#-andfn-rest-preds) and [`-orfn`](#-orfn-rest-preds). - -```el -(funcall (-not #'numberp) "5") ;; => t -(-sort (-not #'<) '(5 2 1 0 6)) ;; => (6 5 2 1 0) -(-filter (-not (-partial #'< 4)) '(1 2 3 4 5 6 7 8)) ;; => (1 2 3 4) -``` - -#### -orfn `(&rest preds)` - -Return a predicate that returns the first non-nil result of `preds`. -The returned predicate takes a variable number of arguments, -passes them to each predicate in `preds` in turn until one of them -returns non-nil, and returns that non-nil result without calling -the remaining `preds`. If all `preds` return nil, or if no `preds` are -given, the returned predicate returns nil. - -See also: [`-andfn`](#-andfn-rest-preds) and [`-not`](#-not-pred). - -```el -(-filter (-orfn #'natnump #'booleanp) '(1 nil "a" -4 b c t)) ;; => (1 nil t) -(funcall (-orfn #'symbolp (-cut string-match-p "x" <>)) "axe") ;; => 1 -(funcall (-orfn #'= #'+) 1 1) ;; => t -``` - -#### -andfn `(&rest preds)` - -Return a predicate that returns non-nil if all `preds` do so. -The returned predicate `p` takes a variable number of arguments and -passes them to each predicate in `preds` in turn. If any one of -`preds` returns nil, `p` also returns nil without calling the -remaining `preds`. If all `preds` return non-nil, `p` returns the last -such value. If no `preds` are given, `p` always returns non-nil. - -See also: [`-orfn`](#-orfn-rest-preds) and [`-not`](#-not-pred). - -```el -(-filter (-andfn #'numberp (-cut < <> 5)) '(a 1 b 6 c 2)) ;; => (1 2) -(mapcar (-andfn #'numberp #'1+) '(a 1 b 6)) ;; => (nil 2 nil 7) -(funcall (-andfn #'= #'+) 1 1) ;; => 2 -``` - -#### -iteratefn `(fn n)` - -Return a function `fn` composed `n` times with itself. - -`fn` is a unary function. If you need to use a function of higher -arity, use [`-applify`](#-applify-fn) first to turn it into a unary function. - -With n = 0, this acts as identity function. - -In types: (a -> a) -> Int -> a -> a. - -This function satisfies the following law: - - (funcall (-iteratefn fn n) init) = (-last-item (-iterate fn init (1+ n))). - -```el -(funcall (-iteratefn (lambda (x) (* x x)) 3) 2) ;; => 256 -(funcall (-iteratefn '1+ 3) 1) ;; => 4 -(funcall (-iteratefn 'cdr 3) '(1 2 3 4 5)) ;; => (4 5) -``` - -#### -fixfn `(fn &optional equal-test halt-test)` - -Return a function that computes the (least) fixpoint of `fn`. - -`fn` must be a unary function. The returned lambda takes a single -argument, `x`, the initial value for the fixpoint iteration. The -iteration halts when either of the following conditions is satisfied: - - 1. Iteration converges to the fixpoint, with equality being - tested using `equal-test`. If `equal-test` is not specified, - `equal` is used. For functions over the floating point - numbers, it may be necessary to provide an appropriate - approximate comparison test. - - 2. `halt-test` returns a non-nil value. `halt-test` defaults to a - simple counter that returns t after `-fixfn-max-iterations`, - to guard against infinite iteration. Otherwise, `halt-test` - must be a function that accepts a single argument, the - current value of `x`, and returns non-nil as long as iteration - should continue. In this way, a more sophisticated - convergence test may be supplied by the caller. - -The return value of the lambda is either the fixpoint or, if -iteration halted before converging, a cons with car `halted` and -cdr the final output from `halt-test`. - -In types: (a -> a) -> a -> a. - -```el -(funcall (-fixfn #'cos #'approx=) 0.7) ;; ~> 0.7390851332151607 -(funcall (-fixfn (lambda (x) (expt (+ x 10) 0.25))) 2.0) ;; => 1.8555845286409378 -(funcall (-fixfn #'sin #'approx=) 0.1) ;; => (halted . t) -``` - -#### -prodfn `(&rest fns)` - -Take a list of n functions and return a function that takes a -list of length n, applying i-th function to i-th element of the -input list. Returns a list of length n. - -In types (for n=2): ((a -> b), (c -> d)) -> (a, c) -> (b, d) - -This function satisfies the following laws: - - (-compose (-prodfn f g ...) (-prodfn f' g' ...)) = (-prodfn (-compose f f') (-compose g g') ...) - (-prodfn f g ...) = (-juxt (-compose f (-partial 'nth 0)) (-compose g (-partial 'nth 1)) ...) - (-compose (-prodfn f g ...) (-juxt f' g' ...)) = (-juxt (-compose f f') (-compose g g') ...) - (-compose (-partial 'nth n) (-prod f1 f2 ...)) = (-compose fn (-partial 'nth n)) - -```el -(funcall (-prodfn '1+ '1- 'number-to-string) '(1 2 3)) ;; => (2 1 "3") -(-map (-prodfn '1+ '1-) '((1 2) (3 4) (5 6) (7 8))) ;; => ((2 1) (4 3) (6 5) (8 7)) -(apply '+ (funcall (-prodfn 'length 'string-to-number) '((1 2 3) "15"))) ;; => 18 -``` - -## Contribute - -Yes, please do. Pure functions in the list manipulation realm only, -please. There's a suite of examples/tests in `dev/examples.el`, so -remember to add tests for your additions, or I might break them later. - -You'll find the repo at: - - https://github.com/magnars/dash.el - -Run the tests with: - - make check - -Regenerate the docs with: - - make docs - -I highly recommend that you install these as a pre-commit hook, so -that the tests are always running and the docs are always in sync: - - cp dev/pre-commit.sh .git/hooks/pre-commit - -Oh, and don't edit `README.md` or `dash.texi` directly; they are -auto-generated. Change `readme-template.md` or `dash-template.texi` -instead, respectively. - -To ensure that `dash.el` can be distributed with GNU ELPA or Emacs, we -require that all contributors assign copyright to the Free Software -Foundation. For more on this, see [`(info "(emacs) Copyright -Assignment")`](https://gnu.org/software/emacs/manual/html_node/emacs/Copyright-Assignment.html). - -## Contributors - -- [Matus Goljer](https://github.com/Fuco1) contributed lots of features and - functions. -- [Takafumi Arakaki](https://github.com/tkf) contributed `-group-by`. -- [tali713](https://github.com/tali713) is the author of `-applify`. -- [Víctor M. Valenzuela](https://github.com/vemv) contributed `-repeat`. -- [Nic Ferrier](https://github.com/nicferrier) contributed `-cons*`. -- [Wilfred Hughes](https://github.com/Wilfred) contributed `-slice`, - `-first-item`, and `-last-item`. -- [Emanuel Evans](https://github.com/shosti) contributed `-if-let`, `-when-let`, - and `-insert-at`. -- [Johan Andersson](https://github.com/rejeep) contributed `-sum`, `-product`, - and `-same-items?`. -- [Christina Whyte](https://github.com/kurisuwhyte) contributed `-compose`. -- [Steve Lamb](https://github.com/steventlamb) contributed `-cycle`, `-pad`, - `-annotate`, `-zip-fill`, and a variadic version of `-zip`. -- [Fredrik Bergroth](https://github.com/fbergroth) made the `-if-let` family use - `-let` destructuring and improved the script for generating documentation. -- [Mark Oteiza](https://github.com/holomorph) contributed `-iota` and - the script to create an Info manual. -- [Vasilij Schneidermann](https://github.com/wasamasa) contributed `-some`. -- [William West](https://github.com/occidens) made `-fixfn` more robust at - handling floats. -- [Cam Saul](https://github.com/camsaul) contributed `-some->`, `-some->>`, and - `-some-->`. -- [Basil L. Contovounesios](https://github.com/basil-conto) contributed - `-common-prefix`, `-common-suffix`, and various other improvements. -- [Paul Pogonyshev](https://github.com/doublep) contributed `-each-r` and - `-each-r-while`. - -Thanks! - -New contributors are very welcome. See the -[`Contribute`](#contribute) section above. - -## License - -Copyright (C) 2012-2021 Free Software Foundation, Inc. - -Author: Magnar Sveen - -This program 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 of the License, or -(at your option) any later version. - -This program 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 this program. If not, see . blob - da60884bc741441ff195805eab1bf10c32c604c1 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/dash-autoloads.el +++ /dev/null @@ -1,83 +0,0 @@ -;;; dash-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from dash.el - -(autoload 'dash-fontify-mode "dash" "\ -Toggle fontification of Dash special variables. - -Dash-Fontify mode is a buffer-local minor mode intended for Emacs -Lisp buffers. Enabling it causes the special variables bound in -anaphoric Dash macros to be fontified. These anaphoras include -`it', `it-index', `acc', and `other'. In older Emacs versions -which do not dynamically detect macros, Dash-Fontify mode -additionally fontifies Dash macro calls. - -See also `dash-fontify-mode-lighter' and -`global-dash-fontify-mode'. - -This is a minor mode. If called interactively, toggle the -`Dash-Fontify mode' mode. If the prefix argument is positive, -enable the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `dash-fontify-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(put 'global-dash-fontify-mode 'globalized-minor-mode t) -(defvar global-dash-fontify-mode nil "\ -Non-nil if Global Dash-Fontify mode is enabled. -See the `global-dash-fontify-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `global-dash-fontify-mode'.") -(custom-autoload 'global-dash-fontify-mode "dash" nil) -(autoload 'global-dash-fontify-mode "dash" "\ -Toggle Dash-Fontify mode in all buffers. -With prefix ARG, enable Global Dash-Fontify mode if ARG is positive; -otherwise, disable it. - -If called from Lisp, toggle the mode if ARG is `toggle'. -Enable the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -Dash-Fontify mode is enabled in all buffers where -`dash--turn-on-fontify-mode' would do it. - -See `dash-fontify-mode' for more information on Dash-Fontify mode. - -(fn &optional ARG)" t) -(autoload 'dash-register-info-lookup "dash" "\ -Register the Dash Info manual with `info-lookup-symbol'. -This allows Dash symbols to be looked up with \\[info-lookup-symbol]." t) -(register-definition-prefixes "dash" '("!cdr" "!cons" "--" "->" "-a" "-butlast" "-c" "-d" "-e" "-f" "-gr" "-i" "-juxt" "-keep" "-l" "-m" "-no" "-o" "-p" "-r" "-s" "-t" "-u" "-value-to-list" "-when-let" "-zip" "dash-")) - -;;; End of scraped data - -(provide 'dash-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; dash-autoloads.el ends here blob - 105f1d8868480954977d35312b6dfec2ff15efc6 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/dash-functional.el +++ /dev/null @@ -1,53 +0,0 @@ -;;; dash-functional.el --- Collection of useful combinators for Emacs Lisp -*- lexical-binding: t -*- - -;; Copyright (C) 2013-2021 Free Software Foundation, Inc. - -;; Author: Matus Goljer -;; Magnar Sveen -;; Version: 1.3.0 -;; Package-Requires: ((dash "2.18.0")) -;; Keywords: extensions, lisp -;; Homepage: https://github.com/magnars/dash.el - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; *N.B.:* This package has been absorbed, and is therefore made -;; obsolete, by the `dash' package, version 2.18.0. -;; -;; If you maintain a package that depends on `dash-functional', then -;; you should change that to instead depend on `dash' version 2.18.0, -;; and remove all references to `dash-functional'. -;; -;; If you use any packages that depend on `dash-functional', either -;; directly or indirectly, then you will have to wait until all of -;; them have transitioned away from it before you can remove it. -;; -;; For more information on this, see the following URL: -;; `https://github.com/magnars/dash.el/wiki/Obsoletion-of-dash-functional.el' - -;;; Code: - -(require 'dash) - -(eval-and-compile - (let ((msg "Package dash-functional is obsolete; use dash 2.18.0 instead")) - (if (and noninteractive (fboundp 'byte-compile-warn)) - (byte-compile-warn msg) - (message "%s" msg)))) - -(provide 'dash-functional) - -;;; dash-functional.el ends here blob - 6be57be9eb872ebb668c6c01b78962be1e3f47c3 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/dash-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from dash.el -*- no-byte-compile: t -*- -(define-package "dash" "2.19.1" "A modern list library for Emacs" '((emacs "24")) :keywords '("extensions" "lisp") :authors '(("Magnar Sveen" . "magnars@gmail.com")) :maintainer '("Magnar Sveen" . "magnars@gmail.com") :url "https://github.com/magnars/dash.el") blob - 6dc3dc47c6894a7e6b7fa7c8692842d09d1a5351 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/dash-template.texi +++ /dev/null @@ -1,317 +0,0 @@ -\input texinfo @c -*- texinfo -*- -@c %**start of header -@setfilename dash.info -@set DASHVER @c [[ dash-version ]] -@settitle Dash: A modern list library for GNU Emacs. -@documentencoding UTF-8 -@documentlanguage en -@c %**end of header - -@copying -This manual is for Dash version @value{DASHVER}. - -Copyright @copyright{} 2012--2021 Free Software Foundation, Inc. - -@quotation -Permission is granted to copy, distribute and/or modify this document -under the terms of the GNU Free Documentation License, Version 1.3 or -any later version published by the Free Software Foundation; with the -Invariant Sections being ``GNU General Public License,'' and no -Front-Cover Texts or Back-Cover Texts. A copy of the license is -included in the section entitled ``GNU Free Documentation License''. -@end quotation -@end copying - -@dircategory Emacs -@direntry -* Dash: (dash.info). A modern list library for GNU Emacs. -@end direntry - -@titlepage -@title Dash Manual -@subtitle For Dash Version @value{DASHVER}. -@author Magnar Sveen -@page -@vskip 0pt plus 1filll -@insertcopying -@end titlepage - -@contents - -@ifnottex -@node Top -@top Dash - -@insertcopying -@end ifnottex - -@menu -* Installation:: Installing and configuring Dash. -* Functions:: Dash API reference. -* Development:: Contributing to Dash development. - -Appendices - -* FDL:: The license for this documentation. -* GPL:: Conditions for copying and changing Dash. -* Index:: Index including functions and macros. - -@detailmenu - --- The Detailed Node Listing --- - -Installation - -* Using in a package:: Listing Dash as a package dependency. -* Fontification of special variables:: Font Lock of anaphoric macro variables. -* Info symbol lookup:: Looking up Dash symbols in this manual. - -Functions - -@c [[ function-list ]] - -Development - -* Contribute:: How to contribute. -* Contributors:: List of contributors. -@end detailmenu -@end menu - -@node Installation -@chapter Installation - -Dash is available on @url{https://elpa.gnu.org/, GNU ELPA}, -@url{https://elpa.gnu.org/devel/, GNU-devel ELPA}, and -@url{https://melpa.org/, MELPA}, and can be installed with the -standard command @code{package-install} (@pxref{Package -Installation,,, emacs, The GNU Emacs Manual}). - -@table @kbd -@item M-x package-install @key{RET} dash @key{RET} -Install the Dash library. -@end table - -Alternatively, you can just dump @file{dash.el} in your -@code{load-path} somewhere (@pxref{Lisp Libraries,,, emacs, The GNU -Emacs Manual}). - -@menu -* Using in a package:: Listing Dash as a package dependency. -* Fontification of special variables:: Font Lock of anaphoric macro variables. -* Info symbol lookup:: Looking up Dash symbols in this manual. -@end menu - -@node Using in a package -@section Using in a package - -If you use Dash in your own package, be sure to list it as a -dependency in the library's headers as follows (@pxref{Library -Headers,,, elisp, The Emacs Lisp Reference Manual}). - -@lisp -;; Package-Requires: ((dash "@value{DASHVER}")) -@end lisp - -@node Fontification of special variables -@section Fontification of special variables - -@findex dash-fontify-mode -The autoloaded minor mode @code{dash-fontify-mode} is provided for -optional fontification of anaphoric Dash variables (@code{it}, -@code{acc}, etc.@:) in Emacs Lisp buffers using search-based Font Lock -(@pxref{Font Lock,,, emacs, The GNU Emacs Manual}). In older Emacs -versions which do not dynamically detect macros, the minor mode also -fontifies calls to Dash macros. - -@findex global-dash-fontify-mode -To automatically enable the minor mode in all Emacs Lisp buffers, just -call its autoloaded global counterpart -@code{global-dash-fontify-mode}, either interactively or from your -@code{user-init-file}: - -@lisp -(global-dash-fontify-mode) -@end lisp - -@node Info symbol lookup -@section Info symbol lookup - -@findex dash-register-info-lookup -While editing Elisp files, you can use @kbd{C-h S} -(@code{info-lookup-symbol}) to look up Elisp symbols in the relevant -Info manuals (@pxref{Info Lookup,,, emacs, The GNU Emacs Manual}). To -enable the same for Dash symbols, use the command -@code{dash-register-info-lookup}. It can be called directly when -needed, or automatically from your @code{user-init-file}. For -example: - -@lisp -(with-eval-after-load 'info-look - (dash-register-info-lookup)) -@end lisp - -@node Functions -@chapter Functions - -This chapter contains reference documentation for the Dash -@acronym{API, Application Programming Interface}. The names of all -public functions defined in the library are prefixed with a dash -character (@samp{-}). - -The library also provides anaphoric macro versions of functions where -that makes sense. The names of these macros are prefixed with two -dashes (@samp{--}) instead of one. - -For instance, while the function @code{-map} applies a function to -each element of a list, its anaphoric counterpart @code{--map} -evaluates a form with the local variable @code{it} temporarily bound -to the current list element instead. - -@lisp -@group -;; Normal version. -(-map (lambda (n) (* n n)) '(1 2 3 4)) - @result{} (1 4 9 16) -@end group - -@group -;; Anaphoric version. -(--map (* it it) '(1 2 3 4)) - @result{} (1 4 9 16) -@end group -@end lisp - -The normal version can, of course, also be written as in the following -example, which demonstrates the utility of both versions. - -@lisp -@group -(defun my-square (n) - "Return N multiplied by itself." - (* n n)) - -(-map #'my-square '(1 2 3 4)) - @result{} (1 4 9 16) -@end group -@end lisp - -@menu -@c [[ function-list ]] -@end menu - -@c [[ function-docs ]] -@node Development -@chapter Development - -The Dash repository is hosted on GitHub at -@url{https://github.com/magnars/dash.el}. - -@menu -* Contribute:: How to contribute. -* Contributors:: List of contributors. -@end menu - -@node Contribute -@section Contribute - -Yes, please do. Pure functions in the list manipulation realm only, -please. There's a suite of examples/tests in @file{dev/examples.el}, -so remember to add tests for your additions, or they may get broken -later. - -Run the tests with @samp{make check}. Regenerate the docs with -@samp{make docs}. Contributors are encouraged to install these -commands as a Git pre-commit hook, so that the tests are always -running and the docs are always in sync: - -@example -$ cp dev/pre-commit.sh .git/hooks/pre-commit -@end example - -Oh, and don't edit @file{README.md} or @file{dash.texi} directly, as -they are auto-generated. Instead, change their respective templates -@file{readme-template.md} or @file{dash-template.texi}. - -To ensure that Dash can be distributed with GNU ELPA or Emacs, we -require that all contributors assign copyright to the Free Software -Foundation. For more on this, @pxref{Copyright Assignment,,, emacs, -The GNU Emacs Manual}. - -@node Contributors -@section Contributors - -@itemize -@item -@url{https://github.com/Fuco1, Matus Goljer} contributed lots of -features and functions. -@item -@url{https://github.com/tkf, Takafumi Arakaki} contributed -@code{-group-by}. -@item -@url{https://github.com/tali713, tali713} is the author of -@code{-applify}. -@item -@url{https://github.com/vemv, V@'{i}ctor M. Valenzuela} contributed -@code{-repeat}. -@item -@url{https://github.com/nicferrier, Nic Ferrier} contributed -@code{-cons*}. -@item -@url{https://github.com/Wilfred, Wilfred Hughes} contributed -@code{-slice}, @code{-first-item}, and @code{-last-item}. -@item -@url{https://github.com/shosti, Emanuel Evans} contributed -@code{-if-let}, @code{-when-let}, and @code{-insert-at}. -@item -@url{https://github.com/rejeep, Johan Andersson} contributed -@code{-sum}, @code{-product}, and @code{-same-items?}. -@item -@url{https://github.com/kurisuwhyte, Christina Whyte} contributed -@code{-compose}. -@item -@url{https://github.com/steventlamb, Steve Lamb} contributed -@code{-cycle}, @code{-pad}, @code{-annotate}, @code{-zip-fill}, and a -variadic version of @code{-zip}. -@item -@url{https://github.com/fbergroth, Fredrik Bergroth} made the -@code{-if-let} family use @code{-let} destructuring and improved the -script for generating documentation. -@item -@url{https://github.com/holomorph, Mark Oteiza} contributed -@code{-iota} and the script to create an Info manual. -@item -@url{https://github.com/wasamasa, Vasilij Schneidermann} contributed -@code{-some}. -@item -@url{https://github.com/occidens, William West} made @code{-fixfn} -more robust at handling floats. -@item -@url{https://github.com/camsaul, Cam Saul} contributed @code{-some->}, -@code{-some->>}, and @code{-some-->}. -@item -@url{https://github.com/basil-conto, Basil L. Contovounesios} -contributed @code{-common-prefix}, @code{-common-suffix}, and various -other improvements. -@item -@url{https://github.com/doublep, Paul Pogonyshev} contributed -@code{-each-r} and @code{-each-r-while}. -@end itemize - -Thanks! - -New contributors are very welcome. @xref{Contribute}. - -@c Appendices. - -@node FDL -@appendix GNU Free Documentation License -@include doc/fdl.texi - -@node GPL -@appendix GNU General Public License -@include doc/gpl.texi - -@node Index -@unnumbered Index -@printindex fn - -@bye blob - 6386c5f69d65a55a03a27273e8d139fbd86a8f94 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/dash.el +++ /dev/null @@ -1,3531 +0,0 @@ -;;; dash.el --- A modern list library for Emacs -*- lexical-binding: t -*- - -;; Copyright (C) 2012-2021 Free Software Foundation, Inc. - -;; Author: Magnar Sveen -;; Version: 2.19.1 -;; Package-Requires: ((emacs "24")) -;; Keywords: extensions, lisp -;; Homepage: https://github.com/magnars/dash.el - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; A modern list API for Emacs. -;; -;; See its overview at https://github.com/magnars/dash.el#functions. - -;;; Code: - -;; TODO: `gv' was introduced in Emacs 24.3, so remove this and all -;; calls to `defsetf' when support for earlier versions is dropped. -(eval-when-compile - (unless (fboundp 'gv-define-setter) - (require 'cl))) - -(defgroup dash () - "Customize group for Dash, a modern list library." - :group 'extensions - :group 'lisp - :prefix "dash-") - -(defmacro !cons (car cdr) - "Destructive: Set CDR to the cons of CAR and CDR." - (declare (debug (form symbolp))) - `(setq ,cdr (cons ,car ,cdr))) - -(defmacro !cdr (list) - "Destructive: Set LIST to the cdr of LIST." - (declare (debug (symbolp))) - `(setq ,list (cdr ,list))) - -(defmacro --each (list &rest body) - "Evaluate BODY for each element of LIST and return nil. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating BODY. -This is the anaphoric counterpart to `-each'." - (declare (debug (form body)) (indent 1)) - (let ((l (make-symbol "list")) - (i (make-symbol "i"))) - `(let ((,l ,list) - (,i 0) - it it-index) - (ignore it it-index) - (while ,l - (setq it (pop ,l) it-index ,i ,i (1+ ,i)) - ,@body)))) - -(defun -each (list fn) - "Call FN on each element of LIST. -Return nil; this function is intended for side effects. - -Its anaphoric counterpart is `--each'. - -For access to the current element's index in LIST, see -`-each-indexed'." - (declare (indent 1)) - (ignore (mapc fn list))) - -(defalias '--each-indexed '--each) - -(defun -each-indexed (list fn) - "Call FN on each index and element of LIST. -For each ITEM at INDEX in LIST, call (funcall FN INDEX ITEM). -Return nil; this function is intended for side effects. - -See also: `-map-indexed'." - (declare (indent 1)) - (--each list (funcall fn it-index it))) - -(defmacro --each-while (list pred &rest body) - "Evaluate BODY for each item in LIST, while PRED evaluates to non-nil. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating PRED or BODY. Once -an element is reached for which PRED evaluates to nil, no further -BODY is evaluated. The return value is always nil. -This is the anaphoric counterpart to `-each-while'." - (declare (debug (form form body)) (indent 2)) - (let ((l (make-symbol "list")) - (i (make-symbol "i")) - (elt (make-symbol "elt"))) - `(let ((,l ,list) - (,i 0) - ,elt it it-index) - (ignore it it-index) - (while (and ,l (setq ,elt (pop ,l) it ,elt it-index ,i) ,pred) - (setq it ,elt it-index ,i ,i (1+ ,i)) - ,@body)))) - -(defun -each-while (list pred fn) - "Call FN on each ITEM in LIST, while (PRED ITEM) is non-nil. -Once an ITEM is reached for which PRED returns nil, FN is no -longer called. Return nil; this function is intended for side -effects. - -Its anaphoric counterpart is `--each-while'." - (declare (indent 2)) - (--each-while list (funcall pred it) (funcall fn it))) - -(defmacro --each-r (list &rest body) - "Evaluate BODY for each element of LIST in reversed order. -Each element of LIST in turn, starting at its end, is bound to -`it' and its index within LIST to `it-index' before evaluating -BODY. The return value is always nil. -This is the anaphoric counterpart to `-each-r'." - (declare (debug (form body)) (indent 1)) - (let ((v (make-symbol "vector")) - (i (make-symbol "i"))) - ;; Implementation note: building a vector is considerably faster - ;; than building a reversed list (vector takes less memory, so - ;; there is less GC), plus `length' comes naturally. In-place - ;; `nreverse' would be faster still, but BODY would be able to see - ;; that, even if the modification was undone before we return. - `(let* ((,v (vconcat ,list)) - (,i (length ,v)) - it it-index) - (ignore it it-index) - (while (> ,i 0) - (setq ,i (1- ,i) it-index ,i it (aref ,v ,i)) - ,@body)))) - -(defun -each-r (list fn) - "Call FN on each element of LIST in reversed order. -Return nil; this function is intended for side effects. - -Its anaphoric counterpart is `--each-r'." - (--each-r list (funcall fn it))) - -(defmacro --each-r-while (list pred &rest body) - "Eval BODY for each item in reversed LIST, while PRED evals to non-nil. -Each element of LIST in turn, starting at its end, is bound to -`it' and its index within LIST to `it-index' before evaluating -PRED or BODY. Once an element is reached for which PRED -evaluates to nil, no further BODY is evaluated. The return value -is always nil. -This is the anaphoric counterpart to `-each-r-while'." - (declare (debug (form form body)) (indent 2)) - (let ((v (make-symbol "vector")) - (i (make-symbol "i")) - (elt (make-symbol "elt"))) - `(let* ((,v (vconcat ,list)) - (,i (length ,v)) - ,elt it it-index) - (ignore it it-index) - (while (when (> ,i 0) - (setq ,i (1- ,i) it-index ,i) - (setq ,elt (aref ,v ,i) it ,elt) - ,pred) - (setq it-index ,i it ,elt) - ,@body)))) - -(defun -each-r-while (list pred fn) - "Call FN on each ITEM in reversed LIST, while (PRED ITEM) is non-nil. -Once an ITEM is reached for which PRED returns nil, FN is no -longer called. Return nil; this function is intended for side -effects. - -Its anaphoric counterpart is `--each-r-while'." - (--each-r-while list (funcall pred it) (funcall fn it))) - -(defmacro --dotimes (num &rest body) - "Evaluate BODY NUM times, presumably for side effects. -BODY is evaluated with the local variable `it' temporarily bound -to successive integers running from 0, inclusive, to NUM, -exclusive. BODY is not evaluated if NUM is less than 1. -This is the anaphoric counterpart to `-dotimes'." - (declare (debug (form body)) (indent 1)) - (let ((n (make-symbol "num")) - (i (make-symbol "i"))) - `(let ((,n ,num) - (,i 0) - it) - (ignore it) - (while (< ,i ,n) - (setq it ,i ,i (1+ ,i)) - ,@body)))) - -(defun -dotimes (num fn) - "Call FN NUM times, presumably for side effects. -FN is called with a single argument on successive integers -running from 0, inclusive, to NUM, exclusive. FN is not called -if NUM is less than 1. - -This function's anaphoric counterpart is `--dotimes'." - (declare (indent 1)) - (--dotimes num (funcall fn it))) - -(defun -map (fn list) - "Apply FN to each item in LIST and return the list of results. - -This function's anaphoric counterpart is `--map'." - (mapcar fn list)) - -(defmacro --map (form list) - "Eval FORM for each item in LIST and return the list of results. -Each element of LIST in turn is bound to `it' before evaluating -FORM. -This is the anaphoric counterpart to `-map'." - (declare (debug (def-form form))) - `(mapcar (lambda (it) (ignore it) ,form) ,list)) - -(defmacro --reduce-from (form init list) - "Accumulate a value by evaluating FORM across LIST. -This macro is like `--each' (which see), but it additionally -provides an accumulator variable `acc' which it successively -binds to the result of evaluating FORM for the current LIST -element before processing the next element. For the first -element, `acc' is initialized with the result of evaluating INIT. -The return value is the resulting value of `acc'. If LIST is -empty, FORM is not evaluated, and the return value is the result -of INIT. -This is the anaphoric counterpart to `-reduce-from'." - (declare (debug (form form form))) - `(let ((acc ,init)) - (--each ,list (setq acc ,form)) - acc)) - -(defun -reduce-from (fn init list) - "Reduce the function FN across LIST, starting with INIT. -Return the result of applying FN to INIT and the first element of -LIST, then applying FN to that result and the second element, -etc. If LIST is empty, return INIT without calling FN. - -This function's anaphoric counterpart is `--reduce-from'. - -For other folds, see also `-reduce' and `-reduce-r'." - (--reduce-from (funcall fn acc it) init list)) - -(defmacro --reduce (form list) - "Accumulate a value by evaluating FORM across LIST. -This macro is like `--reduce-from' (which see), except the first -element of LIST is taken as INIT. Thus if LIST contains a single -item, it is returned without evaluating FORM. If LIST is empty, -FORM is evaluated with `it' and `acc' bound to nil. -This is the anaphoric counterpart to `-reduce'." - (declare (debug (form form))) - (let ((lv (make-symbol "list-value"))) - `(let ((,lv ,list)) - (if ,lv - (--reduce-from ,form (car ,lv) (cdr ,lv)) - ;; Explicit nil binding pacifies lexical "variable left uninitialized" - ;; warning. See issue #377 and upstream https://bugs.gnu.org/47080. - (let ((acc nil) (it nil)) - (ignore acc it) - ,form))))) - -(defun -reduce (fn list) - "Reduce the function FN across LIST. -Return the result of applying FN to the first two elements of -LIST, then applying FN to that result and the third element, etc. -If LIST contains a single element, return it without calling FN. -If LIST is empty, return the result of calling FN with no -arguments. - -This function's anaphoric counterpart is `--reduce'. - -For other folds, see also `-reduce-from' and `-reduce-r'." - (if list - (-reduce-from fn (car list) (cdr list)) - (funcall fn))) - -(defmacro --reduce-r-from (form init list) - "Accumulate a value by evaluating FORM across LIST in reverse. -This macro is like `--reduce-from', except it starts from the end -of LIST. -This is the anaphoric counterpart to `-reduce-r-from'." - (declare (debug (form form form))) - `(let ((acc ,init)) - (--each-r ,list (setq acc ,form)) - acc)) - -(defun -reduce-r-from (fn init list) - "Reduce the function FN across LIST in reverse, starting with INIT. -Return the result of applying FN to the last element of LIST and -INIT, then applying FN to the second-to-last element and the -previous result of FN, etc. That is, the first argument of FN is -the current element, and its second argument the accumulated -value. If LIST is empty, return INIT without calling FN. - -This function is like `-reduce-from' but the operation associates -from the right rather than left. In other words, it starts from -the end of LIST and flips the arguments to FN. Conceptually, it -is like replacing the conses in LIST with applications of FN, and -its last link with INIT, and evaluating the resulting expression. - -This function's anaphoric counterpart is `--reduce-r-from'. - -For other folds, see also `-reduce-r' and `-reduce'." - (--reduce-r-from (funcall fn it acc) init list)) - -(defmacro --reduce-r (form list) - "Accumulate a value by evaluating FORM across LIST in reverse order. -This macro is like `--reduce', except it starts from the end of -LIST. -This is the anaphoric counterpart to `-reduce-r'." - (declare (debug (form form))) - `(--reduce ,form (reverse ,list))) - -(defun -reduce-r (fn list) - "Reduce the function FN across LIST in reverse. -Return the result of applying FN to the last two elements of -LIST, then applying FN to the third-to-last element and the -previous result of FN, etc. That is, the first argument of FN is -the current element, and its second argument the accumulated -value. If LIST contains a single element, return it without -calling FN. If LIST is empty, return the result of calling FN -with no arguments. - -This function is like `-reduce' but the operation associates from -the right rather than left. In other words, it starts from the -end of LIST and flips the arguments to FN. Conceptually, it is -like replacing the conses in LIST with applications of FN, -ignoring its last link, and evaluating the resulting expression. - -This function's anaphoric counterpart is `--reduce-r'. - -For other folds, see also `-reduce-r-from' and `-reduce'." - (if list - (--reduce-r (funcall fn it acc) list) - (funcall fn))) - -(defmacro --reductions-from (form init list) - "Return a list of FORM's intermediate reductions across LIST. -That is, a list of the intermediate values of the accumulator -when `--reduce-from' (which see) is called with the same -arguments. -This is the anaphoric counterpart to `-reductions-from'." - (declare (debug (form form form))) - `(nreverse - (--reduce-from (cons (let ((acc (car acc))) (ignore acc) ,form) acc) - (list ,init) - ,list))) - -(defun -reductions-from (fn init list) - "Return a list of FN's intermediate reductions across LIST. -That is, a list of the intermediate values of the accumulator -when `-reduce-from' (which see) is called with the same -arguments. - -This function's anaphoric counterpart is `--reductions-from'. - -For other folds, see also `-reductions' and `-reductions-r'." - (--reductions-from (funcall fn acc it) init list)) - -(defmacro --reductions (form list) - "Return a list of FORM's intermediate reductions across LIST. -That is, a list of the intermediate values of the accumulator -when `--reduce' (which see) is called with the same arguments. -This is the anaphoric counterpart to `-reductions'." - (declare (debug (form form))) - (let ((lv (make-symbol "list-value"))) - `(let ((,lv ,list)) - (if ,lv - (--reductions-from ,form (car ,lv) (cdr ,lv)) - (let (acc it) - (ignore acc it) - (list ,form)))))) - -(defun -reductions (fn list) - "Return a list of FN's intermediate reductions across LIST. -That is, a list of the intermediate values of the accumulator -when `-reduce' (which see) is called with the same arguments. - -This function's anaphoric counterpart is `--reductions'. - -For other folds, see also `-reductions' and `-reductions-r'." - (if list - (--reductions-from (funcall fn acc it) (car list) (cdr list)) - (list (funcall fn)))) - -(defmacro --reductions-r-from (form init list) - "Return a list of FORM's intermediate reductions across reversed LIST. -That is, a list of the intermediate values of the accumulator -when `--reduce-r-from' (which see) is called with the same -arguments. -This is the anaphoric counterpart to `-reductions-r-from'." - (declare (debug (form form form))) - `(--reduce-r-from (cons (let ((acc (car acc))) (ignore acc) ,form) acc) - (list ,init) - ,list)) - -(defun -reductions-r-from (fn init list) - "Return a list of FN's intermediate reductions across reversed LIST. -That is, a list of the intermediate values of the accumulator -when `-reduce-r-from' (which see) is called with the same -arguments. - -This function's anaphoric counterpart is `--reductions-r-from'. - -For other folds, see also `-reductions' and `-reductions-r'." - (--reductions-r-from (funcall fn it acc) init list)) - -(defmacro --reductions-r (form list) - "Return a list of FORM's intermediate reductions across reversed LIST. -That is, a list of the intermediate values of the accumulator -when `--reduce-re' (which see) is called with the same arguments. -This is the anaphoric counterpart to `-reductions-r'." - (declare (debug (form list))) - (let ((lv (make-symbol "list-value"))) - `(let ((,lv (reverse ,list))) - (if ,lv - (--reduce-from (cons (let ((acc (car acc))) (ignore acc) ,form) acc) - (list (car ,lv)) - (cdr ,lv)) - ;; Explicit nil binding pacifies lexical "variable left uninitialized" - ;; warning. See issue #377 and upstream https://bugs.gnu.org/47080. - (let ((acc nil) (it nil)) - (ignore acc it) - (list ,form)))))) - -(defun -reductions-r (fn list) - "Return a list of FN's intermediate reductions across reversed LIST. -That is, a list of the intermediate values of the accumulator -when `-reduce-r' (which see) is called with the same arguments. - -This function's anaphoric counterpart is `--reductions-r'. - -For other folds, see also `-reductions-r-from' and -`-reductions'." - (if list - (--reductions-r (funcall fn it acc) list) - (list (funcall fn)))) - -(defmacro --filter (form list) - "Return a new list of the items in LIST for which FORM evals to non-nil. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. -This is the anaphoric counterpart to `-filter'. -For the opposite operation, see also `--remove'." - (declare (debug (form form))) - (let ((r (make-symbol "result"))) - `(let (,r) - (--each ,list (when ,form (push it ,r))) - (nreverse ,r)))) - -(defun -filter (pred list) - "Return a new list of the items in LIST for which PRED returns non-nil. - -Alias: `-select'. - -This function's anaphoric counterpart is `--filter'. - -For similar operations, see also `-keep' and `-remove'." - (--filter (funcall pred it) list)) - -(defalias '-select '-filter) -(defalias '--select '--filter) - -(defmacro --remove (form list) - "Return a new list of the items in LIST for which FORM evals to nil. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. -This is the anaphoric counterpart to `-remove'. -For the opposite operation, see also `--filter'." - (declare (debug (form form))) - `(--filter (not ,form) ,list)) - -(defun -remove (pred list) - "Return a new list of the items in LIST for which PRED returns nil. - -Alias: `-reject'. - -This function's anaphoric counterpart is `--remove'. - -For similar operations, see also `-keep' and `-filter'." - (--remove (funcall pred it) list)) - -(defalias '-reject '-remove) -(defalias '--reject '--remove) - -(defmacro --remove-first (form list) - "Remove the first item from LIST for which FORM evals to non-nil. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. This is a -non-destructive operation, but only the front of LIST leading up -to the removed item is a copy; the rest is LIST's original tail. -If no item is removed, then the result is a complete copy. -This is the anaphoric counterpart to `-remove-first'." - (declare (debug (form form))) - (let ((front (make-symbol "front")) - (tail (make-symbol "tail"))) - `(let ((,tail ,list) ,front) - (--each-while ,tail (not ,form) - (push (pop ,tail) ,front)) - (if ,tail - (nconc (nreverse ,front) (cdr ,tail)) - (nreverse ,front))))) - -(defun -remove-first (pred list) - "Remove the first item from LIST for which PRED returns non-nil. -This is a non-destructive operation, but only the front of LIST -leading up to the removed item is a copy; the rest is LIST's -original tail. If no item is removed, then the result is a -complete copy. - -Alias: `-reject-first'. - -This function's anaphoric counterpart is `--remove-first'. - -See also `-map-first', `-remove-item', and `-remove-last'." - (--remove-first (funcall pred it) list)) - -(defalias '-reject-first '-remove-first) -(defalias '--reject-first '--remove-first) - -(defmacro --remove-last (form list) - "Remove the last item from LIST for which FORM evals to non-nil. -Each element of LIST in turn is bound to `it' before evaluating -FORM. The result is a copy of LIST regardless of whether an -element is removed. -This is the anaphoric counterpart to `-remove-last'." - (declare (debug (form form))) - `(nreverse (--remove-first ,form (reverse ,list)))) - -(defun -remove-last (pred list) - "Remove the last item from LIST for which PRED returns non-nil. -The result is a copy of LIST regardless of whether an element is -removed. - -Alias: `-reject-last'. - -This function's anaphoric counterpart is `--remove-last'. - -See also `-map-last', `-remove-item', and `-remove-first'." - (--remove-last (funcall pred it) list)) - -(defalias '-reject-last '-remove-last) -(defalias '--reject-last '--remove-last) - -(defalias '-remove-item #'remove - "Return a copy of LIST with all occurrences of ITEM removed. -The comparison is done with `equal'. -\n(fn ITEM LIST)") - -(defmacro --keep (form list) - "Eval FORM for each item in LIST and return the non-nil results. -Like `--filter', but returns the non-nil results of FORM instead -of the corresponding elements of LIST. Each element of LIST in -turn is bound to `it' and its index within LIST to `it-index' -before evaluating FORM. -This is the anaphoric counterpart to `-keep'." - (declare (debug (form form))) - (let ((r (make-symbol "result")) - (m (make-symbol "mapped"))) - `(let (,r) - (--each ,list (let ((,m ,form)) (when ,m (push ,m ,r)))) - (nreverse ,r)))) - -(defun -keep (fn list) - "Return a new list of the non-nil results of applying FN to each item in LIST. -Like `-filter', but returns the non-nil results of FN instead of -the corresponding elements of LIST. - -Its anaphoric counterpart is `--keep'." - (--keep (funcall fn it) list)) - -(defun -non-nil (list) - "Return a copy of LIST with all nil items removed." - (declare (pure t) (side-effect-free t)) - (--filter it list)) - -(defmacro --map-indexed (form list) - "Eval FORM for each item in LIST and return the list of results. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. This is like -`--map', but additionally makes `it-index' available to FORM. - -This is the anaphoric counterpart to `-map-indexed'." - (declare (debug (form form))) - (let ((r (make-symbol "result"))) - `(let (,r) - (--each ,list - (push ,form ,r)) - (nreverse ,r)))) - -(defun -map-indexed (fn list) - "Apply FN to each index and item in LIST and return the list of results. -This is like `-map', but FN takes two arguments: the index of the -current element within LIST, and the element itself. - -This function's anaphoric counterpart is `--map-indexed'. - -For a side-effecting variant, see also `-each-indexed'." - (--map-indexed (funcall fn it-index it) list)) - -(defmacro --map-when (pred rep list) - "Anaphoric form of `-map-when'." - (declare (debug (form form form))) - (let ((r (make-symbol "result"))) - `(let (,r) - (--each ,list (!cons (if ,pred ,rep it) ,r)) - (nreverse ,r)))) - -(defun -map-when (pred rep list) - "Return a new list where the elements in LIST that do not match the PRED function -are unchanged, and where the elements in LIST that do match the PRED function are mapped -through the REP function. - -Alias: `-replace-where' - -See also: `-update-at'" - (--map-when (funcall pred it) (funcall rep it) list)) - -(defalias '-replace-where '-map-when) -(defalias '--replace-where '--map-when) - -(defun -map-first (pred rep list) - "Replace first item in LIST satisfying PRED with result of REP called on this item. - -See also: `-map-when', `-replace-first'" - (let (front) - (while (and list (not (funcall pred (car list)))) - (push (car list) front) - (!cdr list)) - (if list - (-concat (nreverse front) (cons (funcall rep (car list)) (cdr list))) - (nreverse front)))) - -(defmacro --map-first (pred rep list) - "Anaphoric form of `-map-first'." - (declare (debug (def-form def-form form))) - `(-map-first (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) - -(defun -map-last (pred rep list) - "Replace last item in LIST satisfying PRED with result of REP called on this item. - -See also: `-map-when', `-replace-last'" - (nreverse (-map-first pred rep (reverse list)))) - -(defmacro --map-last (pred rep list) - "Anaphoric form of `-map-last'." - (declare (debug (def-form def-form form))) - `(-map-last (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) - -(defun -replace (old new list) - "Replace all OLD items in LIST with NEW. - -Elements are compared using `equal'. - -See also: `-replace-at'" - (declare (pure t) (side-effect-free t)) - (--map-when (equal it old) new list)) - -(defun -replace-first (old new list) - "Replace the first occurrence of OLD with NEW in LIST. - -Elements are compared using `equal'. - -See also: `-map-first'" - (declare (pure t) (side-effect-free t)) - (--map-first (equal old it) new list)) - -(defun -replace-last (old new list) - "Replace the last occurrence of OLD with NEW in LIST. - -Elements are compared using `equal'. - -See also: `-map-last'" - (declare (pure t) (side-effect-free t)) - (--map-last (equal old it) new list)) - -(defmacro --mapcat (form list) - "Anaphoric form of `-mapcat'." - (declare (debug (form form))) - `(apply 'append (--map ,form ,list))) - -(defun -mapcat (fn list) - "Return the concatenation of the result of mapping FN over LIST. -Thus function FN should return a list." - (--mapcat (funcall fn it) list)) - -(defmacro --iterate (form init n) - "Anaphoric version of `-iterate'." - (declare (debug (form form form))) - (let ((res (make-symbol "result")) - (len (make-symbol "n"))) - `(let ((,len ,n)) - (when (> ,len 0) - (let* ((it ,init) - (,res (list it))) - (dotimes (_ (1- ,len)) - (push (setq it ,form) ,res)) - (nreverse ,res)))))) - -(defun -iterate (fun init n) - "Return a list of iterated applications of FUN to INIT. - -This means a list of the form: - - (INIT (FUN INIT) (FUN (FUN INIT)) ...) - -N is the length of the returned list." - (--iterate (funcall fun it) init n)) - -(defun -flatten (l) - "Take a nested list L and return its contents as a single, flat list. - -Note that because `nil' represents a list of zero elements (an -empty list), any mention of nil in L will disappear after -flattening. If you need to preserve nils, consider `-flatten-n' -or map them to some unique symbol and then map them back. - -Conses of two atoms are considered \"terminals\", that is, they -aren't flattened further. - -See also: `-flatten-n'" - (declare (pure t) (side-effect-free t)) - (if (and (listp l) (listp (cdr l))) - (-mapcat '-flatten l) - (list l))) - -(defun -flatten-n (num list) - "Flatten NUM levels of a nested LIST. - -See also: `-flatten'" - (declare (pure t) (side-effect-free t)) - (dotimes (_ num) - (setq list (apply #'append (mapcar #'-list list)))) - list) - -(defun -concat (&rest lists) - "Return a new list with the concatenation of the elements in the supplied LISTS." - (declare (pure t) (side-effect-free t)) - (apply 'append lists)) - -(defalias '-copy 'copy-sequence - "Create a shallow copy of LIST. - -\(fn LIST)") - -(defun -splice (pred fun list) - "Splice lists generated by FUN in place of elements matching PRED in LIST. - -FUN takes the element matching PRED as input. - -This function can be used as replacement for `,@' in case you -need to splice several lists at marked positions (for example -with keywords). - -See also: `-splice-list', `-insert-at'" - (let (r) - (--each list - (if (funcall pred it) - (let ((new (funcall fun it))) - (--each new (!cons it r))) - (!cons it r))) - (nreverse r))) - -(defmacro --splice (pred form list) - "Anaphoric form of `-splice'." - (declare (debug (def-form def-form form))) - `(-splice (lambda (it) ,pred) (lambda (it) ,form) ,list)) - -(defun -splice-list (pred new-list list) - "Splice NEW-LIST in place of elements matching PRED in LIST. - -See also: `-splice', `-insert-at'" - (-splice pred (lambda (_) new-list) list)) - -(defmacro --splice-list (pred new-list list) - "Anaphoric form of `-splice-list'." - (declare (debug (def-form form form))) - `(-splice-list (lambda (it) ,pred) ,new-list ,list)) - -(defun -cons* (&rest args) - "Make a new list from the elements of ARGS. -The last 2 elements of ARGS are used as the final cons of the -result, so if the final element of ARGS is not a list, the result -is a dotted list. With no ARGS, return nil." - (declare (pure t) (side-effect-free t)) - (let* ((len (length args)) - (tail (nthcdr (- len 2) args)) - (last (cdr tail))) - (if (null last) - (car args) - (setcdr tail (car last)) - args))) - -(defun -snoc (list elem &rest elements) - "Append ELEM to the end of the list. - -This is like `cons', but operates on the end of list. - -If ELEMENTS is non nil, append these to the list as well." - (-concat list (list elem) elements)) - -(defmacro --first (form list) - "Return the first item in LIST for which FORM evals to non-nil. -Return nil if no such element is found. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. -This is the anaphoric counterpart to `-first'." - (declare (debug (form form))) - (let ((n (make-symbol "needle"))) - `(let (,n) - (--each-while ,list (or (not ,form) - (ignore (setq ,n it)))) - ,n))) - -(defun -first (pred list) - "Return the first item in LIST for which PRED returns non-nil. -Return nil if no such element is found. -To get the first item in the list no questions asked, use `car'. - -Alias: `-find'. - -This function's anaphoric counterpart is `--first'." - (--first (funcall pred it) list)) - -(defalias '-find '-first) -(defalias '--find '--first) - -(defmacro --some (form list) - "Return non-nil if FORM evals to non-nil for at least one item in LIST. -If so, return the first such result of FORM. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. -This is the anaphoric counterpart to `-some'." - (declare (debug (form form))) - (let ((n (make-symbol "needle"))) - `(let (,n) - (--each-while ,list (not (setq ,n ,form))) - ,n))) - -(defun -some (pred list) - "Return (PRED x) for the first LIST item where (PRED x) is non-nil, else nil. - -Alias: `-any'. - -This function's anaphoric counterpart is `--some'." - (--some (funcall pred it) list)) - -(defalias '-any '-some) -(defalias '--any '--some) - -(defmacro --every (form list) - "Return non-nil if FORM evals to non-nil for all items in LIST. -If so, return the last such result of FORM. Otherwise, once an -item is reached for which FORM yields nil, return nil without -evaluating FORM for any further LIST elements. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. - -This macro is like `--every-p', but on success returns the last -non-nil result of FORM instead of just t. - -This is the anaphoric counterpart to `-every'." - (declare (debug (form form))) - (let ((a (make-symbol "all"))) - `(let ((,a t)) - (--each-while ,list (setq ,a ,form)) - ,a))) - -(defun -every (pred list) - "Return non-nil if PRED returns non-nil for all items in LIST. -If so, return the last such result of PRED. Otherwise, once an -item is reached for which PRED returns nil, return nil without -calling PRED on any further LIST elements. - -This function is like `-every-p', but on success returns the last -non-nil result of PRED instead of just t. - -This function's anaphoric counterpart is `--every'." - (--every (funcall pred it) list)) - -(defmacro --last (form list) - "Anaphoric form of `-last'." - (declare (debug (form form))) - (let ((n (make-symbol "needle"))) - `(let (,n) - (--each ,list - (when ,form (setq ,n it))) - ,n))) - -(defun -last (pred list) - "Return the last x in LIST where (PRED x) is non-nil, else nil." - (--last (funcall pred it) list)) - -(defalias '-first-item 'car - "Return the first item of LIST, or nil on an empty list. - -See also: `-second-item', `-last-item'. - -\(fn LIST)") - -;; Ensure that calls to `-first-item' are compiled to a single opcode, -;; just like `car'. -(put '-first-item 'byte-opcode 'byte-car) -(put '-first-item 'byte-compile 'byte-compile-one-arg) - -(defalias '-second-item 'cadr - "Return the second item of LIST, or nil if LIST is too short. - -See also: `-third-item'. - -\(fn LIST)") - -(defalias '-third-item - (if (fboundp 'caddr) - #'caddr - (lambda (list) (car (cddr list)))) - "Return the third item of LIST, or nil if LIST is too short. - -See also: `-fourth-item'. - -\(fn LIST)") - -(defun -fourth-item (list) - "Return the fourth item of LIST, or nil if LIST is too short. - -See also: `-fifth-item'." - (declare (pure t) (side-effect-free t)) - (car (cdr (cdr (cdr list))))) - -(defun -fifth-item (list) - "Return the fifth item of LIST, or nil if LIST is too short. - -See also: `-last-item'." - (declare (pure t) (side-effect-free t)) - (car (cdr (cdr (cdr (cdr list)))))) - -(defun -last-item (list) - "Return the last item of LIST, or nil on an empty list." - (declare (pure t) (side-effect-free t)) - (car (last list))) - -;; Use `with-no-warnings' to suppress unbound `-last-item' or -;; undefined `gv--defsetter' warnings arising from both -;; `gv-define-setter' and `defsetf' in certain Emacs versions. -(with-no-warnings - (if (fboundp 'gv-define-setter) - (gv-define-setter -last-item (val x) `(setcar (last ,x) ,val)) - (defsetf -last-item (x) (val) `(setcar (last ,x) ,val)))) - -(defun -butlast (list) - "Return a list of all items in list except for the last." - ;; no alias as we don't want magic optional argument - (declare (pure t) (side-effect-free t)) - (butlast list)) - -(defmacro --count (pred list) - "Anaphoric form of `-count'." - (declare (debug (form form))) - (let ((r (make-symbol "result"))) - `(let ((,r 0)) - (--each ,list (when ,pred (setq ,r (1+ ,r)))) - ,r))) - -(defun -count (pred list) - "Counts the number of items in LIST where (PRED item) is non-nil." - (--count (funcall pred it) list)) - -(defun ---truthy? (obj) - "Return OBJ as a boolean value (t or nil)." - (declare (pure t) (side-effect-free t)) - (and obj t)) - -(defmacro --any? (form list) - "Anaphoric form of `-any?'." - (declare (debug (form form))) - `(and (--some ,form ,list) t)) - -(defun -any? (pred list) - "Return t if (PRED x) is non-nil for any x in LIST, else nil. - -Alias: `-any-p', `-some?', `-some-p'" - (--any? (funcall pred it) list)) - -(defalias '-some? '-any?) -(defalias '--some? '--any?) -(defalias '-any-p '-any?) -(defalias '--any-p '--any?) -(defalias '-some-p '-any?) -(defalias '--some-p '--any?) - -(defmacro --all? (form list) - "Return t if FORM evals to non-nil for all items in LIST. -Otherwise, once an item is reached for which FORM yields nil, -return nil without evaluating FORM for any further LIST elements. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. - -The similar macro `--every' is more widely useful, since it -returns the last non-nil result of FORM instead of just t on -success. - -Alias: `--all-p', `--every-p', `--every?'. - -This is the anaphoric counterpart to `-all?'." - (declare (debug (form form))) - `(and (--every ,form ,list) t)) - -(defun -all? (pred list) - "Return t if (PRED X) is non-nil for all X in LIST, else nil. -In the latter case, stop after the first X for which (PRED X) is -nil, without calling PRED on any subsequent elements of LIST. - -The similar function `-every' is more widely useful, since it -returns the last non-nil result of PRED instead of just t on -success. - -Alias: `-all-p', `-every-p', `-every?'. - -This function's anaphoric counterpart is `--all?'." - (--all? (funcall pred it) list)) - -(defalias '-every? '-all?) -(defalias '--every? '--all?) -(defalias '-all-p '-all?) -(defalias '--all-p '--all?) -(defalias '-every-p '-all?) -(defalias '--every-p '--all?) - -(defmacro --none? (form list) - "Anaphoric form of `-none?'." - (declare (debug (form form))) - `(--all? (not ,form) ,list)) - -(defun -none? (pred list) - "Return t if (PRED x) is nil for all x in LIST, else nil. - -Alias: `-none-p'" - (--none? (funcall pred it) list)) - -(defalias '-none-p '-none?) -(defalias '--none-p '--none?) - -(defmacro --only-some? (form list) - "Anaphoric form of `-only-some?'." - (declare (debug (form form))) - (let ((y (make-symbol "yes")) - (n (make-symbol "no"))) - `(let (,y ,n) - (--each-while ,list (not (and ,y ,n)) - (if ,form (setq ,y t) (setq ,n t))) - (---truthy? (and ,y ,n))))) - -(defun -only-some? (pred list) - "Return `t` if at least one item of LIST matches PRED and at least one item of LIST does not match PRED. -Return `nil` both if all items match the predicate or if none of the items match the predicate. - -Alias: `-only-some-p'" - (--only-some? (funcall pred it) list)) - -(defalias '-only-some-p '-only-some?) -(defalias '--only-some-p '--only-some?) - -(defun -slice (list from &optional to step) - "Return copy of LIST, starting from index FROM to index TO. - -FROM or TO may be negative. These values are then interpreted -modulo the length of the list. - -If STEP is a number, only each STEPth item in the resulting -section is returned. Defaults to 1." - (declare (pure t) (side-effect-free t)) - (let ((length (length list)) - (new-list nil)) - ;; to defaults to the end of the list - (setq to (or to length)) - (setq step (or step 1)) - ;; handle negative indices - (when (< from 0) - (setq from (mod from length))) - (when (< to 0) - (setq to (mod to length))) - - ;; iterate through the list, keeping the elements we want - (--each-while list (< it-index to) - (when (and (>= it-index from) - (= (mod (- from it-index) step) 0)) - (push it new-list))) - (nreverse new-list))) - -(defmacro --take-while (form list) - "Take successive items from LIST for which FORM evals to non-nil. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. Return a new -list of the successive elements from the start of LIST for which -FORM evaluates to non-nil. -This is the anaphoric counterpart to `-take-while'." - (declare (debug (form form))) - (let ((r (make-symbol "result"))) - `(let (,r) - (--each-while ,list ,form (push it ,r)) - (nreverse ,r)))) - -(defun -take-while (pred list) - "Take successive items from LIST for which PRED returns non-nil. -PRED is a function of one argument. Return a new list of the -successive elements from the start of LIST for which PRED returns -non-nil. - -This function's anaphoric counterpart is `--take-while'. - -For another variant, see also `-drop-while'." - (--take-while (funcall pred it) list)) - -(defmacro --drop-while (form list) - "Drop successive items from LIST for which FORM evals to non-nil. -Each element of LIST in turn is bound to `it' and its index -within LIST to `it-index' before evaluating FORM. Return the -tail (not a copy) of LIST starting from its first element for -which FORM evaluates to nil. -This is the anaphoric counterpart to `-drop-while'." - (declare (debug (form form))) - (let ((l (make-symbol "list"))) - `(let ((,l ,list)) - (--each-while ,l ,form (pop ,l)) - ,l))) - -(defun -drop-while (pred list) - "Drop successive items from LIST for which PRED returns non-nil. -PRED is a function of one argument. Return the tail (not a copy) -of LIST starting from its first element for which PRED returns -nil. - -This function's anaphoric counterpart is `--drop-while'. - -For another variant, see also `-take-while'." - (--drop-while (funcall pred it) list)) - -(defun -take (n list) - "Return a copy of the first N items in LIST. -Return a copy of LIST if it contains N items or fewer. -Return nil if N is zero or less. - -See also: `-take-last'." - (declare (pure t) (side-effect-free t)) - (--take-while (< it-index n) list)) - -(defun -take-last (n list) - "Return a copy of the last N items of LIST in order. -Return a copy of LIST if it contains N items or fewer. -Return nil if N is zero or less. - -See also: `-take'." - (declare (pure t) (side-effect-free t)) - (copy-sequence (last list n))) - -(defalias '-drop #'nthcdr - "Return the tail (not a copy) of LIST without the first N items. -Return nil if LIST contains N items or fewer. -Return LIST if N is zero or less. - -For another variant, see also `-drop-last'. -\n(fn N LIST)") - -(defun -drop-last (n list) - "Return a copy of LIST without its last N items. -Return a copy of LIST if N is zero or less. -Return nil if LIST contains N items or fewer. - -See also: `-drop'." - (declare (pure t) (side-effect-free t)) - (nbutlast (copy-sequence list) n)) - -(defun -split-at (n list) - "Split LIST into two sublists after the Nth element. -The result is a list of two elements (TAKE DROP) where TAKE is a -new list of the first N elements of LIST, and DROP is the -remaining elements of LIST (not a copy). TAKE and DROP are like -the results of `-take' and `-drop', respectively, but the split -is done in a single list traversal." - (declare (pure t) (side-effect-free t)) - (let (result) - (--each-while list (< it-index n) - (push (pop list) result)) - (list (nreverse result) list))) - -(defun -rotate (n list) - "Rotate LIST N places to the right (left if N is negative). -The time complexity is O(n)." - (declare (pure t) (side-effect-free t)) - (cond ((null list) ()) - ((zerop n) (copy-sequence list)) - ((let* ((len (length list)) - (n-mod-len (mod n len)) - (new-tail-len (- len n-mod-len))) - (append (nthcdr new-tail-len list) (-take new-tail-len list)))))) - -(defun -insert-at (n x list) - "Return a list with X inserted into LIST at position N. - -See also: `-splice', `-splice-list'" - (declare (pure t) (side-effect-free t)) - (let ((split-list (-split-at n list))) - (nconc (car split-list) (cons x (cadr split-list))))) - -(defun -replace-at (n x list) - "Return a list with element at Nth position in LIST replaced with X. - -See also: `-replace'" - (declare (pure t) (side-effect-free t)) - (let ((split-list (-split-at n list))) - (nconc (car split-list) (cons x (cdr (cadr split-list)))))) - -(defun -update-at (n func list) - "Return a list with element at Nth position in LIST replaced with `(func (nth n list))`. - -See also: `-map-when'" - (let ((split-list (-split-at n list))) - (nconc (car split-list) (cons (funcall func (car (cadr split-list))) (cdr (cadr split-list)))))) - -(defmacro --update-at (n form list) - "Anaphoric version of `-update-at'." - (declare (debug (form def-form form))) - `(-update-at ,n (lambda (it) ,form) ,list)) - -(defun -remove-at (n list) - "Return a list with element at Nth position in LIST removed. - -See also: `-remove-at-indices', `-remove'" - (declare (pure t) (side-effect-free t)) - (-remove-at-indices (list n) list)) - -(defun -remove-at-indices (indices list) - "Return a list whose elements are elements from LIST without -elements selected as `(nth i list)` for all i -from INDICES. - -See also: `-remove-at', `-remove'" - (declare (pure t) (side-effect-free t)) - (let* ((indices (-sort '< indices)) - (diffs (cons (car indices) (-map '1- (-zip-with '- (cdr indices) indices)))) - r) - (--each diffs - (let ((split (-split-at it list))) - (!cons (car split) r) - (setq list (cdr (cadr split))))) - (!cons list r) - (apply '-concat (nreverse r)))) - -(defmacro --split-with (pred list) - "Anaphoric form of `-split-with'." - (declare (debug (form form))) - (let ((l (make-symbol "list")) - (r (make-symbol "result")) - (c (make-symbol "continue"))) - `(let ((,l ,list) - (,r nil) - (,c t)) - (while (and ,l ,c) - (let ((it (car ,l))) - (if (not ,pred) - (setq ,c nil) - (!cons it ,r) - (!cdr ,l)))) - (list (nreverse ,r) ,l)))) - -(defun -split-with (pred list) - "Return a list of ((-take-while PRED LIST) (-drop-while PRED LIST)), in no more than one pass through the list." - (--split-with (funcall pred it) list)) - -(defmacro -split-on (item list) - "Split the LIST each time ITEM is found. - -Unlike `-partition-by', the ITEM is discarded from the results. -Empty lists are also removed from the result. - -Comparison is done by `equal'. - -See also `-split-when'" - (declare (debug (def-form form))) - `(-split-when (lambda (it) (equal it ,item)) ,list)) - -(defmacro --split-when (form list) - "Anaphoric version of `-split-when'." - (declare (debug (def-form form))) - `(-split-when (lambda (it) ,form) ,list)) - -(defun -split-when (fn list) - "Split the LIST on each element where FN returns non-nil. - -Unlike `-partition-by', the \"matched\" element is discarded from -the results. Empty lists are also removed from the result. - -This function can be thought of as a generalization of -`split-string'." - (let (r s) - (while list - (if (not (funcall fn (car list))) - (push (car list) s) - (when s (push (nreverse s) r)) - (setq s nil)) - (!cdr list)) - (when s (push (nreverse s) r)) - (nreverse r))) - -(defmacro --separate (form list) - "Anaphoric form of `-separate'." - (declare (debug (form form))) - (let ((y (make-symbol "yes")) - (n (make-symbol "no"))) - `(let (,y ,n) - (--each ,list (if ,form (!cons it ,y) (!cons it ,n))) - (list (nreverse ,y) (nreverse ,n))))) - -(defun -separate (pred list) - "Return a list of ((-filter PRED LIST) (-remove PRED LIST)), in one pass through the list." - (--separate (funcall pred it) list)) - -(defun dash--partition-all-in-steps-reversed (n step list) - "Used by `-partition-all-in-steps' and `-partition-in-steps'." - (when (< step 1) - (signal 'wrong-type-argument - `("Step size < 1 results in juicy infinite loops" ,step))) - (let (result) - (while list - (push (-take n list) result) - (setq list (nthcdr step list))) - result)) - -(defun -partition-all-in-steps (n step list) - "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. -The last groups may contain less than N items." - (declare (pure t) (side-effect-free t)) - (nreverse (dash--partition-all-in-steps-reversed n step list))) - -(defun -partition-in-steps (n step list) - "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. -If there are not enough items to make the last group N-sized, -those items are discarded." - (declare (pure t) (side-effect-free t)) - (let ((result (dash--partition-all-in-steps-reversed n step list))) - (while (and result (< (length (car result)) n)) - (!cdr result)) - (nreverse result))) - -(defun -partition-all (n list) - "Return a new list with the items in LIST grouped into N-sized sublists. -The last group may contain less than N items." - (declare (pure t) (side-effect-free t)) - (-partition-all-in-steps n n list)) - -(defun -partition (n list) - "Return a new list with the items in LIST grouped into N-sized sublists. -If there are not enough items to make the last group N-sized, -those items are discarded." - (declare (pure t) (side-effect-free t)) - (-partition-in-steps n n list)) - -(defmacro --partition-by (form list) - "Anaphoric form of `-partition-by'." - (declare (debug (form form))) - (let ((r (make-symbol "result")) - (s (make-symbol "sublist")) - (v (make-symbol "value")) - (n (make-symbol "new-value")) - (l (make-symbol "list"))) - `(let ((,l ,list)) - (when ,l - (let* ((,r nil) - (it (car ,l)) - (,s (list it)) - (,v ,form) - (,l (cdr ,l))) - (while ,l - (let* ((it (car ,l)) - (,n ,form)) - (unless (equal ,v ,n) - (!cons (nreverse ,s) ,r) - (setq ,s nil) - (setq ,v ,n)) - (!cons it ,s) - (!cdr ,l))) - (!cons (nreverse ,s) ,r) - (nreverse ,r)))))) - -(defun -partition-by (fn list) - "Apply FN to each item in LIST, splitting it each time FN returns a new value." - (--partition-by (funcall fn it) list)) - -(defmacro --partition-by-header (form list) - "Anaphoric form of `-partition-by-header'." - (declare (debug (form form))) - (let ((r (make-symbol "result")) - (s (make-symbol "sublist")) - (h (make-symbol "header-value")) - (b (make-symbol "seen-body?")) - (n (make-symbol "new-value")) - (l (make-symbol "list"))) - `(let ((,l ,list)) - (when ,l - (let* ((,r nil) - (it (car ,l)) - (,s (list it)) - (,h ,form) - (,b nil) - (,l (cdr ,l))) - (while ,l - (let* ((it (car ,l)) - (,n ,form)) - (if (equal ,h ,n) - (when ,b - (!cons (nreverse ,s) ,r) - (setq ,s nil) - (setq ,b nil)) - (setq ,b t)) - (!cons it ,s) - (!cdr ,l))) - (!cons (nreverse ,s) ,r) - (nreverse ,r)))))) - -(defun -partition-by-header (fn list) - "Apply FN to the first item in LIST. That is the header -value. Apply FN to each item in LIST, splitting it each time FN -returns the header value, but only after seeing at least one -other value (the body)." - (--partition-by-header (funcall fn it) list)) - -(defmacro --partition-after-pred (form list) - "Partition LIST after each element for which FORM evaluates to non-nil. -Each element of LIST in turn is bound to `it' before evaluating -FORM. - -This is the anaphoric counterpart to `-partition-after-pred'." - (let ((l (make-symbol "list")) - (r (make-symbol "result")) - (s (make-symbol "sublist"))) - `(let ((,l ,list) ,r ,s) - (when ,l - (--each ,l - (push it ,s) - (when ,form - (push (nreverse ,s) ,r) - (setq ,s ()))) - (when ,s - (push (nreverse ,s) ,r)) - (nreverse ,r))))) - -(defun -partition-after-pred (pred list) - "Partition LIST after each element for which PRED returns non-nil. - -This function's anaphoric counterpart is `--partition-after-pred'." - (--partition-after-pred (funcall pred it) list)) - -(defun -partition-before-pred (pred list) - "Partition directly before each time PRED is true on an element of LIST." - (nreverse (-map #'reverse - (-partition-after-pred pred (reverse list))))) - -(defun -partition-after-item (item list) - "Partition directly after each time ITEM appears in LIST." - (-partition-after-pred (lambda (ele) (equal ele item)) - list)) - -(defun -partition-before-item (item list) - "Partition directly before each time ITEM appears in LIST." - (-partition-before-pred (lambda (ele) (equal ele item)) - list)) - -(defmacro --group-by (form list) - "Anaphoric form of `-group-by'." - (declare (debug t)) - (let ((n (make-symbol "n")) - (k (make-symbol "k")) - (grp (make-symbol "grp"))) - `(nreverse - (-map - (lambda (,n) - (cons (car ,n) - (nreverse (cdr ,n)))) - (--reduce-from - (let* ((,k (,@form)) - (,grp (assoc ,k acc))) - (if ,grp - (setcdr ,grp (cons it (cdr ,grp))) - (push - (list ,k it) - acc)) - acc) - nil ,list))))) - -(defun -group-by (fn list) - "Separate LIST into an alist whose keys are FN applied to the -elements of LIST. Keys are compared by `equal'." - (--group-by (funcall fn it) list)) - -(defun -interpose (sep list) - "Return a new list of all elements in LIST separated by SEP." - (declare (pure t) (side-effect-free t)) - (let (result) - (when list - (!cons (car list) result) - (!cdr list)) - (while list - (setq result (cons (car list) (cons sep result))) - (!cdr list)) - (nreverse result))) - -(defun -interleave (&rest lists) - "Return a new list of the first item in each list, then the second etc." - (declare (pure t) (side-effect-free t)) - (when lists - (let (result) - (while (-none? 'null lists) - (--each lists (!cons (car it) result)) - (setq lists (-map 'cdr lists))) - (nreverse result)))) - -(defmacro --zip-with (form list1 list2) - "Anaphoric form of `-zip-with'. - -The elements in list1 are bound as symbol `it', the elements in list2 as symbol `other'." - (declare (debug (form form form))) - (let ((r (make-symbol "result")) - (l1 (make-symbol "list1")) - (l2 (make-symbol "list2"))) - `(let ((,r nil) - (,l1 ,list1) - (,l2 ,list2)) - (while (and ,l1 ,l2) - (let ((it (car ,l1)) - (other (car ,l2))) - (!cons ,form ,r) - (!cdr ,l1) - (!cdr ,l2))) - (nreverse ,r)))) - -(defun -zip-with (fn list1 list2) - "Zip the two lists LIST1 and LIST2 using a function FN. This -function is applied pairwise taking as first argument element of -LIST1 and as second argument element of LIST2 at corresponding -position. - -The anaphoric form `--zip-with' binds the elements from LIST1 as symbol `it', -and the elements from LIST2 as symbol `other'." - (--zip-with (funcall fn it other) list1 list2)) - -(defun -zip-lists (&rest lists) - "Zip LISTS together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. - -The return value is always list of lists, which is a difference -from `-zip-pair' which returns a cons-cell in case two input -lists are provided. - -See also: `-zip'" - (declare (pure t) (side-effect-free t)) - (when lists - (let (results) - (while (-none? 'null lists) - (setq results (cons (mapcar 'car lists) results)) - (setq lists (mapcar 'cdr lists))) - (nreverse results)))) - -(defun -zip (&rest lists) - "Zip LISTS together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. - -If two lists are provided as arguments, return the groupings as a list -of cons cells. Otherwise, return the groupings as a list of lists. - -Use `-zip-lists' if you need the return value to always be a list -of lists. - -Alias: `-zip-pair' - -See also: `-zip-lists'" - (declare (pure t) (side-effect-free t)) - (when lists - (let (results) - (while (-none? 'null lists) - (setq results (cons (mapcar 'car lists) results)) - (setq lists (mapcar 'cdr lists))) - (setq results (nreverse results)) - (if (= (length lists) 2) - ;; to support backward compatibility, return - ;; a cons cell if two lists were provided - (--map (cons (car it) (cadr it)) results) - results)))) - -(defalias '-zip-pair '-zip) - -(defun -zip-fill (fill-value &rest lists) - "Zip LISTS, with FILL-VALUE padded onto the shorter lists. The -lengths of the returned groupings are equal to the length of the -longest input list." - (declare (pure t) (side-effect-free t)) - (apply '-zip (apply '-pad (cons fill-value lists)))) - -(defun -unzip (lists) - "Unzip LISTS. - -This works just like `-zip' but takes a list of lists instead of -a variable number of arguments, such that - - (-unzip (-zip L1 L2 L3 ...)) - -is identity (given that the lists are the same length). - -Note in particular that calling this on a list of two lists will -return a list of cons-cells such that the above identity works. - -See also: `-zip'" - (apply '-zip lists)) - -(defun -cycle (list) - "Return an infinite circular copy of LIST. -The returned list cycles through the elements of LIST and repeats -from the beginning." - (declare (pure t) (side-effect-free t)) - ;; Also works with sequences that aren't lists. - (let ((newlist (append list ()))) - (nconc newlist newlist))) - -(defun -pad (fill-value &rest lists) - "Appends FILL-VALUE to the end of each list in LISTS such that they -will all have the same length." - (let* ((annotations (-annotate 'length lists)) - (n (-max (-map 'car annotations)))) - (--map (append (cdr it) (-repeat (- n (car it)) fill-value)) annotations))) - -(defun -annotate (fn list) - "Return a list of cons cells where each cell is FN applied to each -element of LIST paired with the unmodified element of LIST." - (-zip (-map fn list) list)) - -(defmacro --annotate (form list) - "Anaphoric version of `-annotate'." - (declare (debug (def-form form))) - `(-annotate (lambda (it) ,form) ,list)) - -(defun dash--table-carry (lists restore-lists &optional re) - "Helper for `-table' and `-table-flat'. - -If a list overflows, carry to the right and reset the list." - (while (not (or (car lists) - (equal lists '(nil)))) - (setcar lists (car restore-lists)) - (pop (cadr lists)) - (!cdr lists) - (!cdr restore-lists) - (when re - (push (nreverse (car re)) (cadr re)) - (setcar re nil) - (!cdr re)))) - -(defun -table (fn &rest lists) - "Compute outer product of LISTS using function FN. - -The function FN should have the same arity as the number of -supplied lists. - -The outer product is computed by applying fn to all possible -combinations created by taking one element from each list in -order. The dimension of the result is (length lists). - -See also: `-table-flat'" - (let ((restore-lists (copy-sequence lists)) - (last-list (last lists)) - (re (make-list (length lists) nil))) - (while (car last-list) - (let ((item (apply fn (-map 'car lists)))) - (push item (car re)) - (setcar lists (cdar lists)) ;; silence byte compiler - (dash--table-carry lists restore-lists re))) - (nreverse (car (last re))))) - -(defun -table-flat (fn &rest lists) - "Compute flat outer product of LISTS using function FN. - -The function FN should have the same arity as the number of -supplied lists. - -The outer product is computed by applying fn to all possible -combinations created by taking one element from each list in -order. The results are flattened, ignoring the tensor structure -of the result. This is equivalent to calling: - - (-flatten-n (1- (length lists)) (apply \\='-table fn lists)) - -but the implementation here is much more efficient. - -See also: `-flatten-n', `-table'" - (let ((restore-lists (copy-sequence lists)) - (last-list (last lists)) - re) - (while (car last-list) - (let ((item (apply fn (-map 'car lists)))) - (push item re) - (setcar lists (cdar lists)) ;; silence byte compiler - (dash--table-carry lists restore-lists))) - (nreverse re))) - -(defun -elem-index (elem list) - "Return the index of the first element in the given LIST which -is equal to the query element ELEM, or nil if there is no -such element." - (declare (pure t) (side-effect-free t)) - (car (-elem-indices elem list))) - -(defun -elem-indices (elem list) - "Return the indices of all elements in LIST equal to the query -element ELEM, in ascending order." - (declare (pure t) (side-effect-free t)) - (-find-indices (-partial 'equal elem) list)) - -(defun -find-indices (pred list) - "Return the indices of all elements in LIST satisfying the -predicate PRED, in ascending order." - (apply 'append (--map-indexed (when (funcall pred it) (list it-index)) list))) - -(defmacro --find-indices (form list) - "Anaphoric version of `-find-indices'." - (declare (debug (def-form form))) - `(-find-indices (lambda (it) ,form) ,list)) - -(defun -find-index (pred list) - "Take a predicate PRED and a LIST and return the index of the -first element in the list satisfying the predicate, or nil if -there is no such element. - -See also `-first'." - (car (-find-indices pred list))) - -(defmacro --find-index (form list) - "Anaphoric version of `-find-index'." - (declare (debug (def-form form))) - `(-find-index (lambda (it) ,form) ,list)) - -(defun -find-last-index (pred list) - "Take a predicate PRED and a LIST and return the index of the -last element in the list satisfying the predicate, or nil if -there is no such element. - -See also `-last'." - (-last-item (-find-indices pred list))) - -(defmacro --find-last-index (form list) - "Anaphoric version of `-find-last-index'." - (declare (debug (def-form form))) - `(-find-last-index (lambda (it) ,form) ,list)) - -(defun -select-by-indices (indices list) - "Return a list whose elements are elements from LIST selected -as `(nth i list)` for all i from INDICES." - (declare (pure t) (side-effect-free t)) - (let (r) - (--each indices - (!cons (nth it list) r)) - (nreverse r))) - -(defun -select-columns (columns table) - "Select COLUMNS from TABLE. - -TABLE is a list of lists where each element represents one row. -It is assumed each row has the same length. - -Each row is transformed such that only the specified COLUMNS are -selected. - -See also: `-select-column', `-select-by-indices'" - (declare (pure t) (side-effect-free t)) - (--map (-select-by-indices columns it) table)) - -(defun -select-column (column table) - "Select COLUMN from TABLE. - -TABLE is a list of lists where each element represents one row. -It is assumed each row has the same length. - -The single selected column is returned as a list. - -See also: `-select-columns', `-select-by-indices'" - (declare (pure t) (side-effect-free t)) - (--mapcat (-select-by-indices (list column) it) table)) - -(defmacro -> (x &optional form &rest more) - "Thread the expr through the forms. Insert X as the second item -in the first form, making a list of it if it is not a list -already. If there are more forms, insert the first form as the -second item in second form, etc." - (declare (debug (form &rest [&or symbolp (sexp &rest form)]))) - (cond - ((null form) x) - ((null more) (if (listp form) - `(,(car form) ,x ,@(cdr form)) - (list form x))) - (:else `(-> (-> ,x ,form) ,@more)))) - -(defmacro ->> (x &optional form &rest more) - "Thread the expr through the forms. Insert X as the last item -in the first form, making a list of it if it is not a list -already. If there are more forms, insert the first form as the -last item in second form, etc." - (declare (debug ->)) - (cond - ((null form) x) - ((null more) (if (listp form) - `(,@form ,x) - (list form x))) - (:else `(->> (->> ,x ,form) ,@more)))) - -(defmacro --> (x &rest forms) - "Starting with the value of X, thread each expression through FORMS. - -Insert X at the position signified by the symbol `it' in the first -form. If there are more forms, insert the first form at the position -signified by `it' in in second form, etc." - (declare (debug (form body))) - `(-as-> ,x it ,@forms)) - -(defmacro -as-> (value variable &rest forms) - "Starting with VALUE, thread VARIABLE through FORMS. - -In the first form, bind VARIABLE to VALUE. In the second form, bind -VARIABLE to the result of the first form, and so forth." - (declare (debug (form symbolp body))) - (if (null forms) - `,value - `(let ((,variable ,value)) - (-as-> ,(if (symbolp (car forms)) - (list (car forms) variable) - (car forms)) - ,variable - ,@(cdr forms))))) - -(defmacro -some-> (x &optional form &rest more) - "When expr is non-nil, thread it through the first form (via `->'), -and when that result is non-nil, through the next form, etc." - (declare (debug ->) - (indent 1)) - (if (null form) x - (let ((result (make-symbol "result"))) - `(-some-> (-when-let (,result ,x) - (-> ,result ,form)) - ,@more)))) - -(defmacro -some->> (x &optional form &rest more) - "When expr is non-nil, thread it through the first form (via `->>'), -and when that result is non-nil, through the next form, etc." - (declare (debug ->) - (indent 1)) - (if (null form) x - (let ((result (make-symbol "result"))) - `(-some->> (-when-let (,result ,x) - (->> ,result ,form)) - ,@more)))) - -(defmacro -some--> (expr &rest forms) - "Thread EXPR through FORMS via `-->', while the result is non-nil. -When EXPR evaluates to non-nil, thread the result through the -first of FORMS, and when that result is non-nil, thread it -through the next form, etc." - (declare (debug (form &rest &or symbolp consp)) (indent 1)) - (if (null forms) expr - (let ((result (make-symbol "result"))) - `(-some--> (-when-let (,result ,expr) - (--> ,result ,(car forms))) - ,@(cdr forms))))) - -(defmacro -doto (init &rest forms) - "Evaluate INIT and pass it as argument to FORMS with `->'. -The RESULT of evaluating INIT is threaded through each of FORMS -individually using `->', which see. The return value is RESULT, -which FORMS may have modified by side effect." - (declare (debug (form &rest &or symbolp consp)) (indent 1)) - (let ((retval (make-symbol "result"))) - `(let ((,retval ,init)) - ,@(mapcar (lambda (form) `(-> ,retval ,form)) forms) - ,retval))) - -(defmacro --doto (init &rest forms) - "Anaphoric form of `-doto'. -This just evaluates INIT, binds the result to `it', evaluates -FORMS, and returns the final value of `it'. -Note: `it' need not be used in each form." - (declare (debug (form body)) (indent 1)) - `(let ((it ,init)) - ,@forms - it)) - -(defun -grade-up (comparator list) - "Grade elements of LIST using COMPARATOR relation. -This yields a permutation vector such that applying this -permutation to LIST sorts it in ascending order." - (->> (--map-indexed (cons it it-index) list) - (-sort (lambda (it other) (funcall comparator (car it) (car other)))) - (mapcar #'cdr))) - -(defun -grade-down (comparator list) - "Grade elements of LIST using COMPARATOR relation. -This yields a permutation vector such that applying this -permutation to LIST sorts it in descending order." - (->> (--map-indexed (cons it it-index) list) - (-sort (lambda (it other) (funcall comparator (car other) (car it)))) - (mapcar #'cdr))) - -(defvar dash--source-counter 0 - "Monotonic counter for generated symbols.") - -(defun dash--match-make-source-symbol () - "Generate a new dash-source symbol. - -All returned symbols are guaranteed to be unique." - (prog1 (make-symbol (format "--dash-source-%d--" dash--source-counter)) - (setq dash--source-counter (1+ dash--source-counter)))) - -(defun dash--match-ignore-place-p (symbol) - "Return non-nil if SYMBOL is a symbol and starts with _." - (and (symbolp symbol) - (eq (aref (symbol-name symbol) 0) ?_))) - -(defun dash--match-cons-skip-cdr (skip-cdr source) - "Helper function generating idiomatic shifting code." - (cond - ((= skip-cdr 0) - `(pop ,source)) - (t - `(prog1 ,(dash--match-cons-get-car skip-cdr source) - (setq ,source ,(dash--match-cons-get-cdr (1+ skip-cdr) source)))))) - -(defun dash--match-cons-get-car (skip-cdr source) - "Helper function generating idiomatic code to get nth car." - (cond - ((= skip-cdr 0) - `(car ,source)) - ((= skip-cdr 1) - `(cadr ,source)) - (t - `(nth ,skip-cdr ,source)))) - -(defun dash--match-cons-get-cdr (skip-cdr source) - "Helper function generating idiomatic code to get nth cdr." - (cond - ((= skip-cdr 0) - source) - ((= skip-cdr 1) - `(cdr ,source)) - (t - `(nthcdr ,skip-cdr ,source)))) - -(defun dash--match-cons (match-form source) - "Setup a cons matching environment and call the real matcher." - (let ((s (dash--match-make-source-symbol)) - (n 0) - (m match-form)) - (while (and (consp m) - (dash--match-ignore-place-p (car m))) - (setq n (1+ n)) (!cdr m)) - (cond - ;; when we only have one pattern in the list, we don't have to - ;; create a temporary binding (--dash-source--) for the source - ;; and just use the input directly - ((and (consp m) - (not (cdr m))) - (dash--match (car m) (dash--match-cons-get-car n source))) - ;; handle other special types - ((> n 0) - (dash--match m (dash--match-cons-get-cdr n source))) - ;; this is the only entry-point for dash--match-cons-1, that's - ;; why we can't simply use the above branch, it would produce - ;; infinite recursion - (t - (cons (list s source) (dash--match-cons-1 match-form s)))))) - -(defun dash--get-expand-function (type) - "Get expand function name for TYPE." - (intern-soft (format "dash-expand:%s" type))) - -(defun dash--match-cons-1 (match-form source &optional props) - "Match MATCH-FORM against SOURCE. - -MATCH-FORM is a proper or improper list. Each element of -MATCH-FORM is either a symbol, which gets bound to the respective -value in source or another match form which gets destructured -recursively. - -If the cdr of last cons cell in the list is `nil', matching stops -there. - -SOURCE is a proper or improper list." - (let ((skip-cdr (or (plist-get props :skip-cdr) 0))) - (cond - ((consp match-form) - (cond - ((cdr match-form) - (cond - ((and (symbolp (car match-form)) - (functionp (dash--get-expand-function (car match-form)))) - (dash--match-kv (dash--match-kv-normalize-match-form match-form) (dash--match-cons-get-cdr skip-cdr source))) - ((dash--match-ignore-place-p (car match-form)) - (dash--match-cons-1 (cdr match-form) source - (plist-put props :skip-cdr (1+ skip-cdr)))) - (t - (-concat (dash--match (car match-form) (dash--match-cons-skip-cdr skip-cdr source)) - (dash--match-cons-1 (cdr match-form) source))))) - (t ;; Last matching place, no need for shift - (dash--match (car match-form) (dash--match-cons-get-car skip-cdr source))))) - ((eq match-form nil) - nil) - (t ;; Handle improper lists. Last matching place, no need for shift - (dash--match match-form (dash--match-cons-get-cdr skip-cdr source)))))) - -(defun dash--match-vector (match-form source) - "Setup a vector matching environment and call the real matcher." - (let ((s (dash--match-make-source-symbol))) - (cond - ;; don't bind `s' if we only have one sub-pattern - ((= (length match-form) 1) - (dash--match (aref match-form 0) `(aref ,source 0))) - ;; if the source is a symbol, we don't need to re-bind it - ((symbolp source) - (dash--match-vector-1 match-form source)) - ;; don't bind `s' if we only have one sub-pattern which is not ignored - ((let* ((ignored-places (mapcar 'dash--match-ignore-place-p match-form)) - (ignored-places-n (length (-remove 'null ignored-places)))) - (when (= ignored-places-n (1- (length match-form))) - (let ((n (-find-index 'null ignored-places))) - (dash--match (aref match-form n) `(aref ,source ,n)))))) - (t - (cons (list s source) (dash--match-vector-1 match-form s)))))) - -(defun dash--match-vector-1 (match-form source) - "Match MATCH-FORM against SOURCE. - -MATCH-FORM is a vector. Each element of MATCH-FORM is either a -symbol, which gets bound to the respective value in source or -another match form which gets destructured recursively. - -If second-from-last place in MATCH-FORM is the symbol &rest, the -next element of the MATCH-FORM is matched against the tail of -SOURCE, starting at index of the &rest symbol. This is -conceptually the same as the (head . tail) match for improper -lists, where dot plays the role of &rest. - -SOURCE is a vector. - -If the MATCH-FORM vector is shorter than SOURCE vector, only -the (length MATCH-FORM) places are bound, the rest of the SOURCE -is discarded." - (let ((i 0) - (l (length match-form)) - (re)) - (while (< i l) - (let ((m (aref match-form i))) - (push (cond - ((and (symbolp m) - (eq m '&rest)) - (prog1 (dash--match - (aref match-form (1+ i)) - `(substring ,source ,i)) - (setq i l))) - ((and (symbolp m) - ;; do not match symbols starting with _ - (not (eq (aref (symbol-name m) 0) ?_))) - (list (list m `(aref ,source ,i)))) - ((not (symbolp m)) - (dash--match m `(aref ,source ,i)))) - re) - (setq i (1+ i)))) - (-flatten-n 1 (nreverse re)))) - -(defun dash--match-kv-normalize-match-form (pattern) - "Normalize kv PATTERN. - -This method normalizes PATTERN to the format expected by -`dash--match-kv'. See `-let' for the specification." - (let ((normalized (list (car pattern))) - (skip nil) - (fill-placeholder (make-symbol "--dash-fill-placeholder--"))) - (-each (apply '-zip (-pad fill-placeholder (cdr pattern) (cddr pattern))) - (lambda (pair) - (let ((current (car pair)) - (next (cdr pair))) - (if skip - (setq skip nil) - (if (or (eq fill-placeholder next) - (not (or (and (symbolp next) - (not (keywordp next)) - (not (eq next t)) - (not (eq next nil))) - (and (consp next) - (not (eq (car next) 'quote))) - (vectorp next)))) - (progn - (cond - ((keywordp current) - (push current normalized) - (push (intern (substring (symbol-name current) 1)) normalized)) - ((stringp current) - (push current normalized) - (push (intern current) normalized)) - ((and (consp current) - (eq (car current) 'quote)) - (push current normalized) - (push (cadr current) normalized)) - (t (error "-let: found key `%s' in kv destructuring but its pattern `%s' is invalid and can not be derived from the key" current next))) - (setq skip nil)) - (push current normalized) - (push next normalized) - (setq skip t)))))) - (nreverse normalized))) - -(defun dash--match-kv (match-form source) - "Setup a kv matching environment and call the real matcher. - -kv can be any key-value store, such as plist, alist or hash-table." - (let ((s (dash--match-make-source-symbol))) - (cond - ;; don't bind `s' if we only have one sub-pattern (&type key val) - ((= (length match-form) 3) - (dash--match-kv-1 (cdr match-form) source (car match-form))) - ;; if the source is a symbol, we don't need to re-bind it - ((symbolp source) - (dash--match-kv-1 (cdr match-form) source (car match-form))) - (t - (cons (list s source) (dash--match-kv-1 (cdr match-form) s (car match-form))))))) - -(defun dash-expand:&hash (key source) - "Generate extracting KEY from SOURCE for &hash destructuring." - `(gethash ,key ,source)) - -(defun dash-expand:&plist (key source) - "Generate extracting KEY from SOURCE for &plist destructuring." - `(plist-get ,source ,key)) - -(defun dash-expand:&alist (key source) - "Generate extracting KEY from SOURCE for &alist destructuring." - `(cdr (assoc ,key ,source))) - -(defun dash-expand:&hash? (key source) - "Generate extracting KEY from SOURCE for &hash? destructuring. -Similar to &hash but check whether the map is not nil." - (let ((src (make-symbol "src"))) - `(let ((,src ,source)) - (when ,src (gethash ,key ,src))))) - -(defalias 'dash-expand:&keys 'dash-expand:&plist) - -(defun dash--match-kv-1 (match-form source type) - "Match MATCH-FORM against SOURCE of type TYPE. - -MATCH-FORM is a proper list of the form (key1 place1 ... keyN -placeN). Each placeK is either a symbol, which gets bound to the -value of keyK retrieved from the key-value store, or another -match form which gets destructured recursively. - -SOURCE is a key-value store of type TYPE, which can be a plist, -an alist or a hash table. - -TYPE is a token specifying the type of the key-value store. -Valid values are &plist, &alist and &hash." - (-flatten-n 1 (-map - (lambda (kv) - (let* ((k (car kv)) - (v (cadr kv)) - (getter - (funcall (dash--get-expand-function type) k source))) - (cond - ((symbolp v) - (list (list v getter))) - (t (dash--match v getter))))) - (-partition 2 match-form)))) - -(defun dash--match-symbol (match-form source) - "Bind a symbol. - -This works just like `let', there is no destructuring." - (list (list match-form source))) - -(defun dash--match (match-form source) - "Match MATCH-FORM against SOURCE. - -This function tests the MATCH-FORM and dispatches to specific -matchers based on the type of the expression. - -Key-value stores are disambiguated by placing a token &plist, -&alist or &hash as a first item in the MATCH-FORM." - (cond - ((symbolp match-form) - (dash--match-symbol match-form source)) - ((consp match-form) - (cond - ;; Handle the "x &as" bindings first. - ((and (consp (cdr match-form)) - (symbolp (car match-form)) - (eq '&as (cadr match-form))) - (let ((s (car match-form))) - (cons (list s source) - (dash--match (cddr match-form) s)))) - ((functionp (dash--get-expand-function (car match-form))) - (dash--match-kv (dash--match-kv-normalize-match-form match-form) source)) - (t (dash--match-cons match-form source)))) - ((vectorp match-form) - ;; We support the &as binding in vectors too - (cond - ((and (> (length match-form) 2) - (symbolp (aref match-form 0)) - (eq '&as (aref match-form 1))) - (let ((s (aref match-form 0))) - (cons (list s source) - (dash--match (substring match-form 2) s)))) - (t (dash--match-vector match-form source)))))) - -(defun dash--normalize-let-varlist (varlist) - "Normalize VARLIST so that every binding is a list. - -`let' allows specifying a binding which is not a list but simply -the place which is then automatically bound to nil, such that all -three of the following are identical and evaluate to nil. - - (let (a) a) - (let ((a)) a) - (let ((a nil)) a) - -This function normalizes all of these to the last form." - (--map (if (consp it) it (list it nil)) varlist)) - -(defmacro -let* (varlist &rest body) - "Bind variables according to VARLIST then eval BODY. - -VARLIST is a list of lists of the form (PATTERN SOURCE). Each -PATTERN is matched against the SOURCE structurally. SOURCE is -only evaluated once for each PATTERN. - -Each SOURCE can refer to the symbols already bound by this -VARLIST. This is useful if you want to destructure SOURCE -recursively but also want to name the intermediate structures. - -See `-let' for the list of all possible patterns." - (declare (debug ((&rest [&or (sexp form) sexp]) body)) - (indent 1)) - (let* ((varlist (dash--normalize-let-varlist varlist)) - (bindings (--mapcat (dash--match (car it) (cadr it)) varlist))) - `(let* ,bindings - ,@body))) - -(defmacro -let (varlist &rest body) - "Bind variables according to VARLIST then eval BODY. - -VARLIST is a list of lists of the form (PATTERN SOURCE). Each -PATTERN is matched against the SOURCE \"structurally\". SOURCE -is only evaluated once for each PATTERN. Each PATTERN is matched -recursively, and can therefore contain sub-patterns which are -matched against corresponding sub-expressions of SOURCE. - -All the SOURCEs are evalled before any symbols are -bound (i.e. \"in parallel\"). - -If VARLIST only contains one (PATTERN SOURCE) element, you can -optionally specify it using a vector and discarding the -outer-most parens. Thus - - (-let ((PATTERN SOURCE)) ...) - -becomes - - (-let [PATTERN SOURCE] ...). - -`-let' uses a convention of not binding places (symbols) starting -with _ whenever it's possible. You can use this to skip over -entries you don't care about. However, this is not *always* -possible (as a result of implementation) and these symbols might -get bound to undefined values. - -Following is the overview of supported patterns. Remember that -patterns can be matched recursively, so every a, b, aK in the -following can be a matching construct and not necessarily a -symbol/variable. - -Symbol: - - a - bind the SOURCE to A. This is just like regular `let'. - -Conses and lists: - - (a) - bind `car' of cons/list to A - - (a . b) - bind car of cons to A and `cdr' to B - - (a b) - bind car of list to A and `cadr' to B - - (a1 a2 a3 ...) - bind 0th car of list to A1, 1st to A2, 2nd to A3... - - (a1 a2 a3 ... aN . rest) - as above, but bind the Nth cdr to REST. - -Vectors: - - [a] - bind 0th element of a non-list sequence to A (works with - vectors, strings, bit arrays...) - - [a1 a2 a3 ...] - bind 0th element of non-list sequence to A0, 1st to - A1, 2nd to A2, ... - If the PATTERN is shorter than SOURCE, the values at - places not in PATTERN are ignored. - If the PATTERN is longer than SOURCE, an `error' is - thrown. - - [a1 a2 a3 ... &rest rest] - as above, but bind the rest of - the sequence to REST. This is - conceptually the same as improper list - matching (a1 a2 ... aN . rest) - -Key/value stores: - - (&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the - SOURCE plist to aK. If the - value is not found, aK is nil. - Uses `plist-get' to fetch values. - - (&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the - SOURCE alist to aK. If the - value is not found, aK is nil. - Uses `assoc' to fetch values. - - (&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the - SOURCE hash table to aK. If the - value is not found, aK is nil. - Uses `gethash' to fetch values. - -Further, special keyword &keys supports \"inline\" matching of -plist-like key-value pairs, similarly to &keys keyword of -`cl-defun'. - - (a1 a2 ... aN &keys key1 b1 ... keyN bK) - -This binds N values from the list to a1 ... aN, then interprets -the cdr as a plist (see key/value matching above). - -A shorthand notation for kv-destructuring exists which allows the -patterns be optionally left out and derived from the key name in -the following fashion: - -- a key :foo is converted into `foo' pattern, -- a key 'bar is converted into `bar' pattern, -- a key \"baz\" is converted into `baz' pattern. - -That is, the entire value under the key is bound to the derived -variable without any further destructuring. - -This is possible only when the form following the key is not a -valid pattern (i.e. not a symbol, a cons cell or a vector). -Otherwise the matching proceeds as usual and in case of an -invalid spec fails with an error. - -Thus the patterns are normalized as follows: - - ;; derive all the missing patterns - (&plist :foo 'bar \"baz\") => (&plist :foo foo 'bar bar \"baz\" baz) - - ;; we can specify some but not others - (&plist :foo 'bar explicit-bar) => (&plist :foo foo 'bar explicit-bar) - - ;; nothing happens, we store :foo in x - (&plist :foo x) => (&plist :foo x) - - ;; nothing happens, we match recursively - (&plist :foo (a b c)) => (&plist :foo (a b c)) - -You can name the source using the syntax SYMBOL &as PATTERN. -This syntax works with lists (proper or improper), vectors and -all types of maps. - - (list &as a b c) (list 1 2 3) - -binds A to 1, B to 2, C to 3 and LIST to (1 2 3). - -Similarly: - - (bounds &as beg . end) (cons 1 2) - -binds BEG to 1, END to 2 and BOUNDS to (1 . 2). - - (items &as first . rest) (list 1 2 3) - -binds FIRST to 1, REST to (2 3) and ITEMS to (1 2 3) - - [vect &as _ b c] [1 2 3] - -binds B to 2, C to 3 and VECT to [1 2 3] (_ avoids binding as usual). - - (plist &as &plist :b b) (list :a 1 :b 2 :c 3) - -binds B to 2 and PLIST to (:a 1 :b 2 :c 3). Same for &alist and &hash. - -This is especially useful when we want to capture the result of a -computation and destructure at the same time. Consider the -form (function-returning-complex-structure) returning a list of -two vectors with two items each. We want to capture this entire -result and pass it to another computation, but at the same time -we want to get the second item from each vector. We can achieve -it with pattern - - (result &as [_ a] [_ b]) (function-returning-complex-structure) - -Note: Clojure programmers may know this feature as the \":as -binding\". The difference is that we put the &as at the front -because we need to support improper list binding." - (declare (debug ([&or (&rest [&or (sexp form) sexp]) - (vector [&rest [sexp form]])] - body)) - (indent 1)) - (if (vectorp varlist) - `(let* ,(dash--match (aref varlist 0) (aref varlist 1)) - ,@body) - (let* ((varlist (dash--normalize-let-varlist varlist)) - (inputs (--map-indexed (list (make-symbol (format "input%d" it-index)) (cadr it)) varlist)) - (new-varlist (--map (list (caar it) (cadr it)) (-zip varlist inputs)))) - `(let ,inputs - (-let* ,new-varlist ,@body))))) - -(defmacro -lambda (match-form &rest body) - "Return a lambda which destructures its input as MATCH-FORM and executes BODY. - -Note that you have to enclose the MATCH-FORM in a pair of parens, -such that: - - (-lambda (x) body) - (-lambda (x y ...) body) - -has the usual semantics of `lambda'. Furthermore, these get -translated into normal `lambda', so there is no performance -penalty. - -See `-let' for a description of the destructuring mechanism." - (declare (doc-string 2) (indent defun) - (debug (&define sexp - [&optional stringp] - [&optional ("interactive" interactive)] - def-body))) - (cond - ((nlistp match-form) - (signal 'wrong-type-argument (list #'listp match-form))) - ;; No destructuring, so just return regular `lambda' for speed. - ((-all? #'symbolp match-form) - `(lambda ,match-form ,@body)) - ((let ((inputs (--map-indexed - (list it (make-symbol (format "input%d" it-index))) - match-form))) - ;; TODO: because inputs to the `lambda' are evaluated only once, - ;; `-let*' need not create the extra bindings to ensure that. - ;; We should find a way to optimize that. Not critical however. - `(lambda ,(mapcar #'cadr inputs) - (-let* ,inputs ,@body)))))) - -(defmacro -setq (&rest forms) - "Bind each MATCH-FORM to the value of its VAL. - -MATCH-FORM destructuring is done according to the rules of `-let'. - -This macro allows you to bind multiple variables by destructuring -the value, so for example: - - (-setq (a b) x - (&plist :c c) plist) - -expands roughly speaking to the following code - - (setq a (car x) - b (cadr x) - c (plist-get plist :c)) - -Care is taken to only evaluate each VAL once so that in case of -multiple assignments it does not cause unexpected side effects. - -\(fn [MATCH-FORM VAL]...)" - (declare (debug (&rest sexp form)) - (indent 1)) - (when (= (mod (length forms) 2) 1) - (signal 'wrong-number-of-arguments (list '-setq (1+ (length forms))))) - (let* ((forms-and-sources - ;; First get all the necessary mappings with all the - ;; intermediate bindings. - (-map (lambda (x) (dash--match (car x) (cadr x))) - (-partition 2 forms))) - ;; To preserve the logic of dynamic scoping we must ensure - ;; that we `setq' the variables outside of the `let*' form - ;; which holds the destructured intermediate values. For - ;; this we generate for each variable a placeholder which is - ;; bound to (lexically) the result of the destructuring. - ;; Then outside of the helper `let*' form we bind all the - ;; original variables to their respective placeholders. - ;; TODO: There is a lot of room for possible optimization, - ;; for start playing with `special-variable-p' to eliminate - ;; unnecessary re-binding. - (variables-to-placeholders - (-mapcat - (lambda (bindings) - (-map - (lambda (binding) - (let ((var (car binding))) - (list var (make-symbol (concat "--dash-binding-" (symbol-name var) "--"))))) - (--filter (not (string-prefix-p "--" (symbol-name (car it)))) bindings))) - forms-and-sources))) - `(let ,(-map 'cadr variables-to-placeholders) - (let* ,(-flatten-n 1 forms-and-sources) - (setq ,@(-flatten (-map 'reverse variables-to-placeholders)))) - (setq ,@(-flatten variables-to-placeholders))))) - -(defmacro -if-let* (vars-vals then &rest else) - "If all VALS evaluate to true, bind them to their corresponding -VARS and do THEN, otherwise do ELSE. VARS-VALS should be a list -of (VAR VAL) pairs. - -Note: binding is done according to `-let*'. VALS are evaluated -sequentially, and evaluation stops after the first nil VAL is -encountered." - (declare (debug ((&rest (sexp form)) form body)) - (indent 2)) - (->> vars-vals - (--mapcat (dash--match (car it) (cadr it))) - (--reduce-r-from - (let ((var (car it)) - (val (cadr it))) - `(let ((,var ,val)) - (if ,var ,acc ,@else))) - then))) - -(defmacro -if-let (var-val then &rest else) - "If VAL evaluates to non-nil, bind it to VAR and do THEN, -otherwise do ELSE. - -Note: binding is done according to `-let'. - -\(fn (VAR VAL) THEN &rest ELSE)" - (declare (debug ((sexp form) form body)) - (indent 2)) - `(-if-let* (,var-val) ,then ,@else)) - -(defmacro --if-let (val then &rest else) - "If VAL evaluates to non-nil, bind it to symbol `it' and do THEN, -otherwise do ELSE." - (declare (debug (form form body)) - (indent 2)) - `(-if-let (it ,val) ,then ,@else)) - -(defmacro -when-let* (vars-vals &rest body) - "If all VALS evaluate to true, bind them to their corresponding -VARS and execute body. VARS-VALS should be a list of (VAR VAL) -pairs. - -Note: binding is done according to `-let*'. VALS are evaluated -sequentially, and evaluation stops after the first nil VAL is -encountered." - (declare (debug ((&rest (sexp form)) body)) - (indent 1)) - `(-if-let* ,vars-vals (progn ,@body))) - -(defmacro -when-let (var-val &rest body) - "If VAL evaluates to non-nil, bind it to VAR and execute body. - -Note: binding is done according to `-let'. - -\(fn (VAR VAL) &rest BODY)" - (declare (debug ((sexp form) body)) - (indent 1)) - `(-if-let ,var-val (progn ,@body))) - -(defmacro --when-let (val &rest body) - "If VAL evaluates to non-nil, bind it to symbol `it' and -execute body." - (declare (debug (form body)) - (indent 1)) - `(--if-let ,val (progn ,@body))) - -(defvar -compare-fn nil - "Tests for equality use this function or `equal' if this is nil. -It should only be set using dynamic scope with a let, like: - - (let ((-compare-fn #\\='=)) (-union numbers1 numbers2 numbers3)") - -(defun -distinct (list) - "Return a new list with all duplicates removed. -The test for equality is done with `equal', -or with `-compare-fn' if that's non-nil. - -Alias: `-uniq'" - ;; Implementation note: The speedup gained from hash table lookup - ;; starts to outweigh its overhead for lists of length greater than - ;; 32. See discussion in PR #305. - (let* ((len (length list)) - (lut (and (> len 32) - ;; Check that `-compare-fn' is a valid hash-table - ;; lookup function or `nil'. - (memq -compare-fn '(nil equal eq eql)) - (make-hash-table :test (or -compare-fn #'equal) - :size len)))) - (if lut - (--filter (unless (gethash it lut) - (puthash it t lut)) - list) - (--each list (unless (-contains? lut it) (!cons it lut))) - (nreverse lut)))) - -(defalias '-uniq '-distinct) - -(defun -union (list list2) - "Return a new list containing the elements of LIST and elements of LIST2 that are not in LIST. -The test for equality is done with `equal', -or with `-compare-fn' if that's non-nil." - ;; We fall back to iteration implementation if the comparison - ;; function isn't one of `eq', `eql' or `equal'. - (let* ((result (reverse list)) - ;; TODO: get rid of this dynamic variable, pass it as an - ;; argument instead. - (-compare-fn (if (bound-and-true-p -compare-fn) - -compare-fn - 'equal))) - (if (memq -compare-fn '(eq eql equal)) - (let ((ht (make-hash-table :test -compare-fn))) - (--each list (puthash it t ht)) - (--each list2 (unless (gethash it ht) (!cons it result)))) - (--each list2 (unless (-contains? result it) (!cons it result)))) - (nreverse result))) - -(defun -intersection (list list2) - "Return a new list containing only the elements that are members of both LIST and LIST2. -The test for equality is done with `equal', -or with `-compare-fn' if that's non-nil." - (--filter (-contains? list2 it) list)) - -(defun -difference (list list2) - "Return a new list with only the members of LIST that are not in LIST2. -The test for equality is done with `equal', -or with `-compare-fn' if that's non-nil." - (--filter (not (-contains? list2 it)) list)) - -(defun -powerset (list) - "Return the power set of LIST." - (if (null list) '(()) - (let ((last (-powerset (cdr list)))) - (append (mapcar (lambda (x) (cons (car list) x)) last) - last)))) - -(defun -permutations (list) - "Return the permutations of LIST." - (if (null list) '(()) - (apply #'append - (mapcar (lambda (x) - (mapcar (lambda (perm) (cons x perm)) - (-permutations (remove x list)))) - list)))) - -(defun -inits (list) - "Return all prefixes of LIST." - (let ((res (list list))) - (setq list (reverse list)) - (while list - (push (reverse (!cdr list)) res)) - res)) - -(defun -tails (list) - "Return all suffixes of LIST" - (-reductions-r-from 'cons nil list)) - -(defun -common-prefix (&rest lists) - "Return the longest common prefix of LISTS." - (declare (pure t) (side-effect-free t)) - (--reduce (--take-while (and acc (equal (pop acc) it)) it) - lists)) - -(defun -common-suffix (&rest lists) - "Return the longest common suffix of LISTS." - (nreverse (apply #'-common-prefix (mapcar #'reverse lists)))) - -(defun -contains? (list element) - "Return non-nil if LIST contains ELEMENT. - -The test for equality is done with `equal', or with `-compare-fn' -if that's non-nil. - -Alias: `-contains-p'" - (not - (null - (cond - ((null -compare-fn) (member element list)) - ((eq -compare-fn 'eq) (memq element list)) - ((eq -compare-fn 'eql) (memql element list)) - (t - (let ((lst list)) - (while (and lst - (not (funcall -compare-fn element (car lst)))) - (setq lst (cdr lst))) - lst)))))) - -(defalias '-contains-p '-contains?) - -(defun -same-items? (list list2) - "Return true if LIST and LIST2 has the same items. - -The order of the elements in the lists does not matter. - -Alias: `-same-items-p'" - (let ((length-a (length list)) - (length-b (length list2))) - (and - (= length-a length-b) - (= length-a (length (-intersection list list2)))))) - -(defalias '-same-items-p '-same-items?) - -(defun -is-prefix? (prefix list) - "Return non-nil if PREFIX is a prefix of LIST. - -Alias: `-is-prefix-p'." - (declare (pure t) (side-effect-free t)) - (--each-while list (and (equal (car prefix) it) - (!cdr prefix))) - (null prefix)) - -(defun -is-suffix? (suffix list) - "Return non-nil if SUFFIX is a suffix of LIST. - -Alias: `-is-suffix-p'." - (declare (pure t) (side-effect-free t)) - (equal suffix (last list (length suffix)))) - -(defun -is-infix? (infix list) - "Return non-nil if INFIX is infix of LIST. - -This operation runs in O(n^2) time - -Alias: `-is-infix-p'" - (declare (pure t) (side-effect-free t)) - (let (done) - (while (and (not done) list) - (setq done (-is-prefix? infix list)) - (!cdr list)) - done)) - -(defalias '-is-prefix-p '-is-prefix?) -(defalias '-is-suffix-p '-is-suffix?) -(defalias '-is-infix-p '-is-infix?) - -(defun -sort (comparator list) - "Sort LIST, stably, comparing elements using COMPARATOR. -Return the sorted list. LIST is NOT modified by side effects. -COMPARATOR is called with two elements of LIST, and should return non-nil -if the first element should sort before the second." - (sort (copy-sequence list) comparator)) - -(defmacro --sort (form list) - "Anaphoric form of `-sort'." - (declare (debug (def-form form))) - `(-sort (lambda (it other) ,form) ,list)) - -(defun -list (&optional arg &rest args) - "Ensure ARG is a list. -If ARG is already a list, return it as is (not a copy). -Otherwise, return a new list with ARG as its only element. - -Another supported calling convention is (-list &rest ARGS). -In this case, if ARG is not a list, a new list with all of -ARGS as elements is returned. This use is supported for -backward compatibility and is otherwise deprecated." - (declare (advertised-calling-convention (arg) "2.18.0") - (pure t) (side-effect-free t)) - (if (listp arg) arg (cons arg args))) - -(defun -repeat (n x) - "Return a new list of length N with each element being X. -Return nil if N is less than 1." - (declare (pure t) (side-effect-free t)) - (and (natnump n) (make-list n x))) - -(defun -sum (list) - "Return the sum of LIST." - (declare (pure t) (side-effect-free t)) - (apply '+ list)) - -(defun -running-sum (list) - "Return a list with running sums of items in LIST. -LIST must be non-empty." - (declare (pure t) (side-effect-free t)) - (or list (signal 'wrong-type-argument (list #'consp list))) - (-reductions #'+ list)) - -(defun -product (list) - "Return the product of LIST." - (declare (pure t) (side-effect-free t)) - (apply '* list)) - -(defun -running-product (list) - "Return a list with running products of items in LIST. -LIST must be non-empty." - (declare (pure t) (side-effect-free t)) - (or list (signal 'wrong-type-argument (list #'consp list))) - (-reductions #'* list)) - -(defun -max (list) - "Return the largest value from LIST of numbers or markers." - (declare (pure t) (side-effect-free t)) - (apply 'max list)) - -(defun -min (list) - "Return the smallest value from LIST of numbers or markers." - (declare (pure t) (side-effect-free t)) - (apply 'min list)) - -(defun -max-by (comparator list) - "Take a comparison function COMPARATOR and a LIST and return -the greatest element of the list by the comparison function. - -See also combinator `-on' which can transform the values before -comparing them." - (--reduce (if (funcall comparator it acc) it acc) list)) - -(defun -min-by (comparator list) - "Take a comparison function COMPARATOR and a LIST and return -the least element of the list by the comparison function. - -See also combinator `-on' which can transform the values before -comparing them." - (--reduce (if (funcall comparator it acc) acc it) list)) - -(defmacro --max-by (form list) - "Anaphoric version of `-max-by'. - -The items for the comparator form are exposed as \"it\" and \"other\"." - (declare (debug (def-form form))) - `(-max-by (lambda (it other) ,form) ,list)) - -(defmacro --min-by (form list) - "Anaphoric version of `-min-by'. - -The items for the comparator form are exposed as \"it\" and \"other\"." - (declare (debug (def-form form))) - `(-min-by (lambda (it other) ,form) ,list)) - -(defun -iota (count &optional start step) - "Return a list containing COUNT numbers. -Starts from START and adds STEP each time. The default START is -zero, the default STEP is 1. -This function takes its name from the corresponding primitive in -the APL language." - (declare (pure t) (side-effect-free t)) - (unless (natnump count) - (signal 'wrong-type-argument (list #'natnump count))) - (or start (setq start 0)) - (or step (setq step 1)) - (if (zerop step) - (make-list count start) - (--iterate (+ it step) start count))) - -(defun -fix (fn list) - "Compute the (least) fixpoint of FN with initial input LIST. - -FN is called at least once, results are compared with `equal'." - (let ((re (funcall fn list))) - (while (not (equal list re)) - (setq list re) - (setq re (funcall fn re))) - re)) - -(defmacro --fix (form list) - "Anaphoric form of `-fix'." - (declare (debug (def-form form))) - `(-fix (lambda (it) ,form) ,list)) - -(defun -unfold (fun seed) - "Build a list from SEED using FUN. - -This is \"dual\" operation to `-reduce-r': while -reduce-r -consumes a list to produce a single value, `-unfold' takes a -seed value and builds a (potentially infinite!) list. - -FUN should return `nil' to stop the generating process, or a -cons (A . B), where A will be prepended to the result and B is -the new seed." - (let ((last (funcall fun seed)) r) - (while last - (push (car last) r) - (setq last (funcall fun (cdr last)))) - (nreverse r))) - -(defmacro --unfold (form seed) - "Anaphoric version of `-unfold'." - (declare (debug (def-form form))) - `(-unfold (lambda (it) ,form) ,seed)) - -(defun -cons-pair? (obj) - "Return non-nil if OBJ is a true cons pair. -That is, a cons (A . B) where B is not a list. - -Alias: `-cons-pair-p'." - (declare (pure t) (side-effect-free t)) - (nlistp (cdr-safe obj))) - -(defalias '-cons-pair-p '-cons-pair?) - -(defun -cons-to-list (con) - "Convert a cons pair to a list with `car' and `cdr' of the pair respectively." - (declare (pure t) (side-effect-free t)) - (list (car con) (cdr con))) - -(defun -value-to-list (val) - "Convert a value to a list. - -If the value is a cons pair, make a list with two elements, `car' -and `cdr' of the pair respectively. - -If the value is anything else, wrap it in a list." - (declare (pure t) (side-effect-free t)) - (cond - ((-cons-pair? val) (-cons-to-list val)) - (t (list val)))) - -(defun -tree-mapreduce-from (fn folder init-value tree) - "Apply FN to each element of TREE, and make a list of the results. -If elements of TREE are lists themselves, apply FN recursively to -elements of these nested lists. - -Then reduce the resulting lists using FOLDER and initial value -INIT-VALUE. See `-reduce-r-from'. - -This is the same as calling `-tree-reduce-from' after `-tree-map' -but is twice as fast as it only traverse the structure once." - (cond - ((not tree) nil) - ((-cons-pair? tree) (funcall fn tree)) - ((listp tree) - (-reduce-r-from folder init-value (mapcar (lambda (x) (-tree-mapreduce-from fn folder init-value x)) tree))) - (t (funcall fn tree)))) - -(defmacro --tree-mapreduce-from (form folder init-value tree) - "Anaphoric form of `-tree-mapreduce-from'." - (declare (debug (def-form def-form form form))) - `(-tree-mapreduce-from (lambda (it) ,form) (lambda (it acc) ,folder) ,init-value ,tree)) - -(defun -tree-mapreduce (fn folder tree) - "Apply FN to each element of TREE, and make a list of the results. -If elements of TREE are lists themselves, apply FN recursively to -elements of these nested lists. - -Then reduce the resulting lists using FOLDER and initial value -INIT-VALUE. See `-reduce-r-from'. - -This is the same as calling `-tree-reduce' after `-tree-map' -but is twice as fast as it only traverse the structure once." - (cond - ((not tree) nil) - ((-cons-pair? tree) (funcall fn tree)) - ((listp tree) - (-reduce-r folder (mapcar (lambda (x) (-tree-mapreduce fn folder x)) tree))) - (t (funcall fn tree)))) - -(defmacro --tree-mapreduce (form folder tree) - "Anaphoric form of `-tree-mapreduce'." - (declare (debug (def-form def-form form))) - `(-tree-mapreduce (lambda (it) ,form) (lambda (it acc) ,folder) ,tree)) - -(defun -tree-map (fn tree) - "Apply FN to each element of TREE while preserving the tree structure." - (cond - ((not tree) nil) - ((-cons-pair? tree) (funcall fn tree)) - ((listp tree) - (mapcar (lambda (x) (-tree-map fn x)) tree)) - (t (funcall fn tree)))) - -(defmacro --tree-map (form tree) - "Anaphoric form of `-tree-map'." - (declare (debug (def-form form))) - `(-tree-map (lambda (it) ,form) ,tree)) - -(defun -tree-reduce-from (fn init-value tree) - "Use FN to reduce elements of list TREE. -If elements of TREE are lists themselves, apply the reduction recursively. - -FN is first applied to INIT-VALUE and first element of the list, -then on this result and second element from the list etc. - -The initial value is ignored on cons pairs as they always contain -two elements." - (cond - ((not tree) nil) - ((-cons-pair? tree) tree) - ((listp tree) - (-reduce-r-from fn init-value (mapcar (lambda (x) (-tree-reduce-from fn init-value x)) tree))) - (t tree))) - -(defmacro --tree-reduce-from (form init-value tree) - "Anaphoric form of `-tree-reduce-from'." - (declare (debug (def-form form form))) - `(-tree-reduce-from (lambda (it acc) ,form) ,init-value ,tree)) - -(defun -tree-reduce (fn tree) - "Use FN to reduce elements of list TREE. -If elements of TREE are lists themselves, apply the reduction recursively. - -FN is first applied to first element of the list and second -element, then on this result and third element from the list etc. - -See `-reduce-r' for how exactly are lists of zero or one element handled." - (cond - ((not tree) nil) - ((-cons-pair? tree) tree) - ((listp tree) - (-reduce-r fn (mapcar (lambda (x) (-tree-reduce fn x)) tree))) - (t tree))) - -(defmacro --tree-reduce (form tree) - "Anaphoric form of `-tree-reduce'." - (declare (debug (def-form form))) - `(-tree-reduce (lambda (it acc) ,form) ,tree)) - -(defun -tree-map-nodes (pred fun tree) - "Call FUN on each node of TREE that satisfies PRED. - -If PRED returns nil, continue descending down this node. If PRED -returns non-nil, apply FUN to this node and do not descend -further." - (if (funcall pred tree) - (funcall fun tree) - (if (and (listp tree) - (not (-cons-pair? tree))) - (-map (lambda (x) (-tree-map-nodes pred fun x)) tree) - tree))) - -(defmacro --tree-map-nodes (pred form tree) - "Anaphoric form of `-tree-map-nodes'." - (declare (debug (def-form def-form form))) - `(-tree-map-nodes (lambda (it) ,pred) (lambda (it) ,form) ,tree)) - -(defun -tree-seq (branch children tree) - "Return a sequence of the nodes in TREE, in depth-first search order. - -BRANCH is a predicate of one argument that returns non-nil if the -passed argument is a branch, that is, a node that can have children. - -CHILDREN is a function of one argument that returns the children -of the passed branch node. - -Non-branch nodes are simply copied." - (cons tree - (when (funcall branch tree) - (-mapcat (lambda (x) (-tree-seq branch children x)) - (funcall children tree))))) - -(defmacro --tree-seq (branch children tree) - "Anaphoric form of `-tree-seq'." - (declare (debug (def-form def-form form))) - `(-tree-seq (lambda (it) ,branch) (lambda (it) ,children) ,tree)) - -(defun -clone (list) - "Create a deep copy of LIST. -The new list has the same elements and structure but all cons are -replaced with new ones. This is useful when you need to clone a -structure such as plist or alist." - (declare (pure t) (side-effect-free t)) - (-tree-map 'identity list)) - -;;; Combinators - -(defalias '-partial #'apply-partially) - -(defun -rpartial (fn &rest args) - "Return a function that is a partial application of FN to ARGS. -ARGS is a list of the last N arguments to pass to FN. The result -is a new function which does the same as FN, except that the last -N arguments are fixed at the values with which this function was -called. This is like `-partial', except the arguments are fixed -starting from the right rather than the left." - (declare (pure t) (side-effect-free t)) - (lambda (&rest args-before) (apply fn (append args-before args)))) - -(defun -juxt (&rest fns) - "Return a function that is the juxtaposition of FNS. -The returned function takes a variable number of ARGS, applies -each of FNS in turn to ARGS, and returns the list of results." - (declare (pure t) (side-effect-free t)) - (lambda (&rest args) (mapcar (lambda (x) (apply x args)) fns))) - -(defun -compose (&rest fns) - "Compose FNS into a single composite function. -Return a function that takes a variable number of ARGS, applies -the last function in FNS to ARGS, and returns the result of -calling each remaining function on the result of the previous -function, right-to-left. If no FNS are given, return a variadic -`identity' function." - (declare (pure t) (side-effect-free t)) - (let* ((fns (nreverse fns)) - (head (car fns)) - (tail (cdr fns))) - (cond (tail - (lambda (&rest args) - (--reduce-from (funcall it acc) (apply head args) tail))) - (fns head) - ((lambda (&optional arg &rest _) arg))))) - -(defun -applify (fn) - "Return a function that applies FN to a single list of args. -This changes the arity of FN from taking N distinct arguments to -taking 1 argument which is a list of N arguments." - (declare (pure t) (side-effect-free t)) - (lambda (args) (apply fn args))) - -(defun -on (op trans) - "Return a function that calls TRANS on each arg and OP on the results. -The returned function takes a variable number of arguments, calls -the function TRANS on each one in turn, and then passes those -results as the list of arguments to OP, in the same order. - -For example, the following pairs of expressions are morally -equivalent: - - (funcall (-on #\\='+ #\\='1+) 1 2 3) = (+ (1+ 1) (1+ 2) (1+ 3)) - (funcall (-on #\\='+ #\\='1+)) = (+)" - (declare (pure t) (side-effect-free t)) - (lambda (&rest args) - ;; This unrolling seems to be a relatively cheap way to keep the - ;; overhead of `mapcar' + `apply' in check. - (cond ((cddr args) - (apply op (mapcar trans args))) - ((cdr args) - (funcall op (funcall trans (car args)) (funcall trans (cadr args)))) - (args - (funcall op (funcall trans (car args)))) - ((funcall op))))) - -(defun -flip (fn) - "Return a function that calls FN with its arguments reversed. -The returned function takes the same number of arguments as FN. - -For example, the following two expressions are morally -equivalent: - - (funcall (-flip #\\='-) 1 2) = (- 2 1) - -See also: `-rotate-args'." - (declare (pure t) (side-effect-free t)) - (lambda (&rest args) ;; Open-code for speed. - (cond ((cddr args) (apply fn (nreverse args))) - ((cdr args) (funcall fn (cadr args) (car args))) - (args (funcall fn (car args))) - ((funcall fn))))) - -(defun -rotate-args (n fn) - "Return a function that calls FN with args rotated N places to the right. -The returned function takes the same number of arguments as FN, -rotates the list of arguments N places to the right (left if N is -negative) just like `-rotate', and applies FN to the result. - -See also: `-flip'." - (declare (pure t) (side-effect-free t)) - (if (zerop n) - fn - (let ((even (= (% n 2) 0))) - (lambda (&rest args) - (cond ((cddr args) ;; Open-code for speed. - (apply fn (-rotate n args))) - ((cdr args) - (let ((fst (car args)) - (snd (cadr args))) - (funcall fn (if even fst snd) (if even snd fst)))) - (args - (funcall fn (car args))) - ((funcall fn))))))) - -(defun -const (c) - "Return a function that returns C ignoring any additional arguments. - -In types: a -> b -> a" - (declare (pure t) (side-effect-free t)) - (lambda (&rest _) c)) - -(defmacro -cut (&rest params) - "Take n-ary function and n arguments and specialize some of them. -Arguments denoted by <> will be left unspecialized. - -See SRFI-26 for detailed description." - (declare (debug (&optional sexp &rest &or "<>" form))) - (let* ((i 0) - (args (--keep (when (eq it '<>) - (setq i (1+ i)) - (make-symbol (format "D%d" i))) - params))) - `(lambda ,args - ,(let ((body (--map (if (eq it '<>) (pop args) it) params))) - (if (eq (car params) '<>) - (cons #'funcall body) - body))))) - -(defun -not (pred) - "Return a predicate that negates the result of PRED. -The returned predicate passes its arguments to PRED. If PRED -returns nil, the result is non-nil; otherwise the result is nil. - -See also: `-andfn' and `-orfn'." - (declare (pure t) (side-effect-free t)) - (lambda (&rest args) (not (apply pred args)))) - -(defun -orfn (&rest preds) - "Return a predicate that returns the first non-nil result of PREDS. -The returned predicate takes a variable number of arguments, -passes them to each predicate in PREDS in turn until one of them -returns non-nil, and returns that non-nil result without calling -the remaining PREDS. If all PREDS return nil, or if no PREDS are -given, the returned predicate returns nil. - -See also: `-andfn' and `-not'." - (declare (pure t) (side-effect-free t)) - ;; Open-code for speed. - (cond ((cdr preds) (lambda (&rest args) (--some (apply it args) preds))) - (preds (car preds)) - (#'ignore))) - -(defun -andfn (&rest preds) - "Return a predicate that returns non-nil if all PREDS do so. -The returned predicate P takes a variable number of arguments and -passes them to each predicate in PREDS in turn. If any one of -PREDS returns nil, P also returns nil without calling the -remaining PREDS. If all PREDS return non-nil, P returns the last -such value. If no PREDS are given, P always returns non-nil. - -See also: `-orfn' and `-not'." - (declare (pure t) (side-effect-free t)) - ;; Open-code for speed. - (cond ((cdr preds) (lambda (&rest args) (--every (apply it args) preds))) - (preds (car preds)) - ;; As a `pure' function, this runtime check may generate - ;; backward-incompatible bytecode for `(-andfn)' at compile-time, - ;; but I doubt that's a problem in practice (famous last words). - ((fboundp 'always) #'always) - ((lambda (&rest _) t)))) - -(defun -iteratefn (fn n) - "Return a function FN composed N times with itself. - -FN is a unary function. If you need to use a function of higher -arity, use `-applify' first to turn it into a unary function. - -With n = 0, this acts as identity function. - -In types: (a -> a) -> Int -> a -> a. - -This function satisfies the following law: - - (funcall (-iteratefn fn n) init) = (-last-item (-iterate fn init (1+ n)))." - (lambda (x) (--dotimes n (setq x (funcall fn x))) x)) - -(defun -counter (&optional beg end inc) - "Return a closure that counts from BEG to END, with increment INC. - -The closure will return the next value in the counting sequence -each time it is called, and nil after END is reached. BEG -defaults to 0, INC defaults to 1, and if END is nil, the counter -will increment indefinitely. - -The closure accepts any number of arguments, which are discarded." - (let ((inc (or inc 1)) - (n (or beg 0))) - (lambda (&rest _) - (when (or (not end) (< n end)) - (prog1 n - (setq n (+ n inc))))))) - -(defvar -fixfn-max-iterations 1000 - "The default maximum number of iterations performed by `-fixfn' - unless otherwise specified.") - -(defun -fixfn (fn &optional equal-test halt-test) - "Return a function that computes the (least) fixpoint of FN. - -FN must be a unary function. The returned lambda takes a single -argument, X, the initial value for the fixpoint iteration. The -iteration halts when either of the following conditions is satisfied: - - 1. Iteration converges to the fixpoint, with equality being - tested using EQUAL-TEST. If EQUAL-TEST is not specified, - `equal' is used. For functions over the floating point - numbers, it may be necessary to provide an appropriate - approximate comparison test. - - 2. HALT-TEST returns a non-nil value. HALT-TEST defaults to a - simple counter that returns t after `-fixfn-max-iterations', - to guard against infinite iteration. Otherwise, HALT-TEST - must be a function that accepts a single argument, the - current value of X, and returns non-nil as long as iteration - should continue. In this way, a more sophisticated - convergence test may be supplied by the caller. - -The return value of the lambda is either the fixpoint or, if -iteration halted before converging, a cons with car `halted' and -cdr the final output from HALT-TEST. - -In types: (a -> a) -> a -> a." - (let ((eqfn (or equal-test 'equal)) - (haltfn (or halt-test - (-not - (-counter 0 -fixfn-max-iterations))))) - (lambda (x) - (let ((re (funcall fn x)) - (halt? (funcall haltfn x))) - (while (and (not halt?) (not (funcall eqfn x re))) - (setq x re - re (funcall fn re) - halt? (funcall haltfn re))) - (if halt? (cons 'halted halt?) - re))))) - -(defun -prodfn (&rest fns) - "Take a list of n functions and return a function that takes a -list of length n, applying i-th function to i-th element of the -input list. Returns a list of length n. - -In types (for n=2): ((a -> b), (c -> d)) -> (a, c) -> (b, d) - -This function satisfies the following laws: - - (-compose (-prodfn f g ...) (-prodfn f\\=' g\\=' ...)) = (-prodfn (-compose f f\\=') (-compose g g\\=') ...) - (-prodfn f g ...) = (-juxt (-compose f (-partial \\='nth 0)) (-compose g (-partial \\='nth 1)) ...) - (-compose (-prodfn f g ...) (-juxt f\\=' g\\=' ...)) = (-juxt (-compose f f\\=') (-compose g g\\=') ...) - (-compose (-partial \\='nth n) (-prod f1 f2 ...)) = (-compose fn (-partial \\='nth n))" - (lambda (x) (-zip-with 'funcall fns x))) - -;;; Font lock - -(defvar dash--keywords - `(;; TODO: Do not fontify the following automatic variables - ;; globally; detect and limit to their local anaphoric scope. - (,(rx symbol-start (| "acc" "it" "it-index" "other") symbol-end) - 0 font-lock-variable-name-face) - ;; Macros in dev/examples.el. Based on `lisp-mode-symbol-regexp'. - (,(rx ?\( (group (| "defexamples" "def-example-group")) symbol-end - (+ (in "\t ")) - (group (* (| (syntax word) (syntax symbol) (: ?\\ nonl))))) - (1 font-lock-keyword-face) - (2 font-lock-function-name-face)) - ;; Symbols in dev/examples.el. - ,(rx symbol-start (| "=>" "~>" "!!>") symbol-end) - ;; Elisp macro fontification was static prior to Emacs 25. - ,@(when (< emacs-major-version 25) - (let ((macs '("!cdr" - "!cons" - "-->" - "--all?" - "--annotate" - "--any?" - "--count" - "--dotimes" - "--doto" - "--drop-while" - "--each" - "--each-r" - "--each-r-while" - "--each-while" - "--filter" - "--find-index" - "--find-indices" - "--find-last-index" - "--first" - "--fix" - "--group-by" - "--if-let" - "--iterate" - "--keep" - "--last" - "--map" - "--map-first" - "--map-indexed" - "--map-last" - "--map-when" - "--mapcat" - "--max-by" - "--min-by" - "--none?" - "--only-some?" - "--partition-by" - "--partition-by-header" - "--reduce" - "--reduce-from" - "--reduce-r" - "--reduce-r-from" - "--reductions" - "--reductions-from" - "--reductions-r" - "--reductions-r-from" - "--remove" - "--remove-first" - "--remove-last" - "--separate" - "--some" - "--sort" - "--splice" - "--splice-list" - "--split-when" - "--split-with" - "--take-while" - "--tree-map" - "--tree-map-nodes" - "--tree-mapreduce" - "--tree-mapreduce-from" - "--tree-reduce" - "--tree-reduce-from" - "--tree-seq" - "--unfold" - "--update-at" - "--when-let" - "--zip-with" - "->" - "->>" - "-as->" - "-doto" - "-if-let" - "-if-let*" - "-lambda" - "-let" - "-let*" - "-setq" - "-some-->" - "-some->" - "-some->>" - "-split-on" - "-when-let" - "-when-let*"))) - `((,(concat "(" (regexp-opt macs 'symbols)) . 1))))) - "Font lock keywords for `dash-fontify-mode'.") - -(defcustom dash-fontify-mode-lighter nil - "Mode line lighter for `dash-fontify-mode'. -Either a string to display in the mode line when -`dash-fontify-mode' is on, or nil to display -nothing (the default)." - :package-version '(dash . "2.18.0") - :group 'dash - :type '(choice (string :tag "Lighter" :value " Dash") - (const :tag "Nothing" nil))) - -;;;###autoload -(define-minor-mode dash-fontify-mode - "Toggle fontification of Dash special variables. - -Dash-Fontify mode is a buffer-local minor mode intended for Emacs -Lisp buffers. Enabling it causes the special variables bound in -anaphoric Dash macros to be fontified. These anaphoras include -`it', `it-index', `acc', and `other'. In older Emacs versions -which do not dynamically detect macros, Dash-Fontify mode -additionally fontifies Dash macro calls. - -See also `dash-fontify-mode-lighter' and -`global-dash-fontify-mode'." - :group 'dash :lighter dash-fontify-mode-lighter - (if dash-fontify-mode - (font-lock-add-keywords nil dash--keywords t) - (font-lock-remove-keywords nil dash--keywords)) - (cond ((fboundp 'font-lock-flush) ;; Added in Emacs 25. - (font-lock-flush)) - ;; `font-lock-fontify-buffer' unconditionally enables - ;; `font-lock-mode' and is marked `interactive-only' in later - ;; Emacs versions which have `font-lock-flush', so we guard - ;; and pacify as needed, respectively. - (font-lock-mode - (with-no-warnings - (font-lock-fontify-buffer))))) - -(defun dash--turn-on-fontify-mode () - "Enable `dash-fontify-mode' if in an Emacs Lisp buffer." - (when (derived-mode-p #'emacs-lisp-mode) - (dash-fontify-mode))) - -;;;###autoload -(define-globalized-minor-mode global-dash-fontify-mode - dash-fontify-mode dash--turn-on-fontify-mode - :group 'dash) - -(defcustom dash-enable-fontlock nil - "If non-nil, fontify Dash macro calls and special variables." - :group 'dash - :set (lambda (sym val) - (set-default sym val) - (global-dash-fontify-mode (if val 1 0))) - :type 'boolean) - -(make-obsolete-variable - 'dash-enable-fontlock #'global-dash-fontify-mode "2.18.0") - -(define-obsolete-function-alias - 'dash-enable-font-lock #'global-dash-fontify-mode "2.18.0") - -;;; Info - -(defvar dash--info-doc-spec '("(dash) Index" nil "^ -+ .*: " "\\( \\|$\\)") - "The Dash :doc-spec entry for `info-lookup-alist'. -It is based on that for `emacs-lisp-mode'.") - -(defun dash--info-elisp-docs () - "Return the `emacs-lisp-mode' symbol docs from `info-lookup-alist'. -Specifically, return the cons containing their -`info-lookup->doc-spec' so that we can modify it." - (defvar info-lookup-alist) - (nthcdr 3 (assq #'emacs-lisp-mode (cdr (assq 'symbol info-lookup-alist))))) - -;;;###autoload -(defun dash-register-info-lookup () - "Register the Dash Info manual with `info-lookup-symbol'. -This allows Dash symbols to be looked up with \\[info-lookup-symbol]." - (interactive) - (require 'info-look) - (let ((docs (dash--info-elisp-docs))) - (setcar docs (append (car docs) (list dash--info-doc-spec))) - (info-lookup-reset))) - -(defun dash-unload-function () - "Remove Dash from `info-lookup-alist'. -Used by `unload-feature', which see." - (let ((docs (and (featurep 'info-look) - (dash--info-elisp-docs)))) - (when (member dash--info-doc-spec (car docs)) - (setcar docs (remove dash--info-doc-spec (car docs))) - (info-lookup-reset))) - nil) - -(provide 'dash) -;;; dash.el ends here blob - 8f4d8843b6396b95da0322958edcb8e9d19ad476 (mode 644) blob + /dev/null Binary files elpa/dash-2.19.1/dash.info and /dev/null differ blob - f23509408b7229c4e318a1a364a3bbd810c506fb (mode 644) blob + /dev/null --- elpa/dash-2.19.1/dash.texi +++ /dev/null @@ -1,4785 +0,0 @@ -\input texinfo @c -*- texinfo -*- -@c %**start of header -@setfilename dash.info -@set DASHVER 2.19.1 -@settitle Dash: A modern list library for GNU Emacs. -@documentencoding UTF-8 -@documentlanguage en -@c %**end of header - -@copying -This manual is for Dash version @value{DASHVER}. - -Copyright @copyright{} 2012--2021 Free Software Foundation, Inc. - -@quotation -Permission is granted to copy, distribute and/or modify this document -under the terms of the GNU Free Documentation License, Version 1.3 or -any later version published by the Free Software Foundation; with the -Invariant Sections being ``GNU General Public License,'' and no -Front-Cover Texts or Back-Cover Texts. A copy of the license is -included in the section entitled ``GNU Free Documentation License''. -@end quotation -@end copying - -@dircategory Emacs -@direntry -* Dash: (dash.info). A modern list library for GNU Emacs. -@end direntry - -@titlepage -@title Dash Manual -@subtitle For Dash Version @value{DASHVER}. -@author Magnar Sveen -@page -@vskip 0pt plus 1filll -@insertcopying -@end titlepage - -@contents - -@ifnottex -@node Top -@top Dash - -@insertcopying -@end ifnottex - -@menu -* Installation:: Installing and configuring Dash. -* Functions:: Dash API reference. -* Development:: Contributing to Dash development. - -Appendices - -* FDL:: The license for this documentation. -* GPL:: Conditions for copying and changing Dash. -* Index:: Index including functions and macros. - -@detailmenu - --- The Detailed Node Listing --- - -Installation - -* Using in a package:: Listing Dash as a package dependency. -* Fontification of special variables:: Font Lock of anaphoric macro variables. -* Info symbol lookup:: Looking up Dash symbols in this manual. - -Functions - -* Maps:: -* Sublist selection:: -* List to list:: -* Reductions:: -* Unfolding:: -* Predicates:: -* Partitioning:: -* Indexing:: -* Set operations:: -* Other list operations:: -* Tree operations:: -* Threading macros:: -* Binding:: -* Side effects:: -* Destructive operations:: -* Function combinators:: - -Development - -* Contribute:: How to contribute. -* Contributors:: List of contributors. -@end detailmenu -@end menu - -@node Installation -@chapter Installation - -Dash is available on @url{https://elpa.gnu.org/, GNU ELPA}, -@url{https://elpa.gnu.org/devel/, GNU-devel ELPA}, and -@url{https://melpa.org/, MELPA}, and can be installed with the -standard command @code{package-install} (@pxref{Package -Installation,,, emacs, The GNU Emacs Manual}). - -@table @kbd -@item M-x package-install @key{RET} dash @key{RET} -Install the Dash library. -@end table - -Alternatively, you can just dump @file{dash.el} in your -@code{load-path} somewhere (@pxref{Lisp Libraries,,, emacs, The GNU -Emacs Manual}). - -@menu -* Using in a package:: Listing Dash as a package dependency. -* Fontification of special variables:: Font Lock of anaphoric macro variables. -* Info symbol lookup:: Looking up Dash symbols in this manual. -@end menu - -@node Using in a package -@section Using in a package - -If you use Dash in your own package, be sure to list it as a -dependency in the library's headers as follows (@pxref{Library -Headers,,, elisp, The Emacs Lisp Reference Manual}). - -@lisp -;; Package-Requires: ((dash "@value{DASHVER}")) -@end lisp - -@node Fontification of special variables -@section Fontification of special variables - -@findex dash-fontify-mode -The autoloaded minor mode @code{dash-fontify-mode} is provided for -optional fontification of anaphoric Dash variables (@code{it}, -@code{acc}, etc.@:) in Emacs Lisp buffers using search-based Font Lock -(@pxref{Font Lock,,, emacs, The GNU Emacs Manual}). In older Emacs -versions which do not dynamically detect macros, the minor mode also -fontifies calls to Dash macros. - -@findex global-dash-fontify-mode -To automatically enable the minor mode in all Emacs Lisp buffers, just -call its autoloaded global counterpart -@code{global-dash-fontify-mode}, either interactively or from your -@code{user-init-file}: - -@lisp -(global-dash-fontify-mode) -@end lisp - -@node Info symbol lookup -@section Info symbol lookup - -@findex dash-register-info-lookup -While editing Elisp files, you can use @kbd{C-h S} -(@code{info-lookup-symbol}) to look up Elisp symbols in the relevant -Info manuals (@pxref{Info Lookup,,, emacs, The GNU Emacs Manual}). To -enable the same for Dash symbols, use the command -@code{dash-register-info-lookup}. It can be called directly when -needed, or automatically from your @code{user-init-file}. For -example: - -@lisp -(with-eval-after-load 'info-look - (dash-register-info-lookup)) -@end lisp - -@node Functions -@chapter Functions - -This chapter contains reference documentation for the Dash -@acronym{API, Application Programming Interface}. The names of all -public functions defined in the library are prefixed with a dash -character (@samp{-}). - -The library also provides anaphoric macro versions of functions where -that makes sense. The names of these macros are prefixed with two -dashes (@samp{--}) instead of one. - -For instance, while the function @code{-map} applies a function to -each element of a list, its anaphoric counterpart @code{--map} -evaluates a form with the local variable @code{it} temporarily bound -to the current list element instead. - -@lisp -@group -;; Normal version. -(-map (lambda (n) (* n n)) '(1 2 3 4)) - @result{} (1 4 9 16) -@end group - -@group -;; Anaphoric version. -(--map (* it it) '(1 2 3 4)) - @result{} (1 4 9 16) -@end group -@end lisp - -The normal version can, of course, also be written as in the following -example, which demonstrates the utility of both versions. - -@lisp -@group -(defun my-square (n) - "Return N multiplied by itself." - (* n n)) - -(-map #'my-square '(1 2 3 4)) - @result{} (1 4 9 16) -@end group -@end lisp - -@menu -* Maps:: -* Sublist selection:: -* List to list:: -* Reductions:: -* Unfolding:: -* Predicates:: -* Partitioning:: -* Indexing:: -* Set operations:: -* Other list operations:: -* Tree operations:: -* Threading macros:: -* Binding:: -* Side effects:: -* Destructive operations:: -* Function combinators:: -@end menu - -@node Maps -@section Maps - -Functions in this category take a transforming function, which -is then applied sequentially to each or selected elements of the -input list. The results are collected in order and returned as a -new list. - -@anchor{-map} -@defun -map (fn list) -Apply @var{fn} to each item in @var{list} and return the list of results. - -This function's anaphoric counterpart is @code{--map}. - -@example -@group -(-map (lambda (num) (* num num)) '(1 2 3 4)) - @result{} (1 4 9 16) -@end group -@group -(-map #'1+ '(1 2 3 4)) - @result{} (2 3 4 5) -@end group -@group -(--map (* it it) '(1 2 3 4)) - @result{} (1 4 9 16) -@end group -@end example -@end defun - -@anchor{-map-when} -@defun -map-when (pred rep list) -Return a new list where the elements in @var{list} that do not match the @var{pred} function -are unchanged, and where the elements in @var{list} that do match the @var{pred} function are mapped -through the @var{rep} function. - -Alias: @code{-replace-where} - -See also: @code{-update-at} (@pxref{-update-at}) - -@example -@group -(-map-when 'even? 'square '(1 2 3 4)) - @result{} (1 4 3 16) -@end group -@group -(--map-when (> it 2) (* it it) '(1 2 3 4)) - @result{} (1 2 9 16) -@end group -@group -(--map-when (= it 2) 17 '(1 2 3 4)) - @result{} (1 17 3 4) -@end group -@end example -@end defun - -@anchor{-map-first} -@defun -map-first (pred rep list) -Replace first item in @var{list} satisfying @var{pred} with result of @var{rep} called on this item. - -See also: @code{-map-when} (@pxref{-map-when}), @code{-replace-first} (@pxref{-replace-first}) - -@example -@group -(-map-first 'even? 'square '(1 2 3 4)) - @result{} (1 4 3 4) -@end group -@group -(--map-first (> it 2) (* it it) '(1 2 3 4)) - @result{} (1 2 9 4) -@end group -@group -(--map-first (= it 2) 17 '(1 2 3 2)) - @result{} (1 17 3 2) -@end group -@end example -@end defun - -@anchor{-map-last} -@defun -map-last (pred rep list) -Replace last item in @var{list} satisfying @var{pred} with result of @var{rep} called on this item. - -See also: @code{-map-when} (@pxref{-map-when}), @code{-replace-last} (@pxref{-replace-last}) - -@example -@group -(-map-last 'even? 'square '(1 2 3 4)) - @result{} (1 2 3 16) -@end group -@group -(--map-last (> it 2) (* it it) '(1 2 3 4)) - @result{} (1 2 3 16) -@end group -@group -(--map-last (= it 2) 17 '(1 2 3 2)) - @result{} (1 2 3 17) -@end group -@end example -@end defun - -@anchor{-map-indexed} -@defun -map-indexed (fn list) -Apply @var{fn} to each index and item in @var{list} and return the list of results. -This is like @code{-map} (@pxref{-map}), but @var{fn} takes two arguments: the index of the -current element within @var{list}, and the element itself. - -This function's anaphoric counterpart is @code{--map-indexed}. - -For a side-effecting variant, see also @code{-each-indexed} (@pxref{-each-indexed}). - -@example -@group -(-map-indexed (lambda (index item) (- item index)) '(1 2 3 4)) - @result{} (1 1 1 1) -@end group -@group -(--map-indexed (- it it-index) '(1 2 3 4)) - @result{} (1 1 1 1) -@end group -@group -(-map-indexed #'* '(1 2 3 4)) - @result{} (0 2 6 12) -@end group -@end example -@end defun - -@anchor{-annotate} -@defun -annotate (fn list) -Return a list of cons cells where each cell is @var{fn} applied to each -element of @var{list} paired with the unmodified element of @var{list}. - -@example -@group -(-annotate '1+ '(1 2 3)) - @result{} ((2 . 1) (3 . 2) (4 . 3)) -@end group -@group -(-annotate 'length '(("h" "e" "l" "l" "o") ("hello" "world"))) - @result{} ((5 "h" "e" "l" "l" "o") (2 "hello" "world")) -@end group -@group -(--annotate (< 1 it) '(0 1 2 3)) - @result{} ((nil . 0) (nil . 1) (t . 2) (t . 3)) -@end group -@end example -@end defun - -@anchor{-splice} -@defun -splice (pred fun list) -Splice lists generated by @var{fun} in place of elements matching @var{pred} in @var{list}. - -@var{fun} takes the element matching @var{pred} as input. - -This function can be used as replacement for @code{,@@} in case you -need to splice several lists at marked positions (for example -with keywords). - -See also: @code{-splice-list} (@pxref{-splice-list}), @code{-insert-at} (@pxref{-insert-at}) - -@example -@group -(-splice 'even? (lambda (x) (list x x)) '(1 2 3 4)) - @result{} (1 2 2 3 4 4) -@end group -@group -(--splice 't (list it it) '(1 2 3 4)) - @result{} (1 1 2 2 3 3 4 4) -@end group -@group -(--splice (equal it :magic) '((list of) (magical) (code)) '((foo) (bar) :magic (baz))) - @result{} ((foo) (bar) (list of) (magical) (code) (baz)) -@end group -@end example -@end defun - -@anchor{-splice-list} -@defun -splice-list (pred new-list list) -Splice @var{new-list} in place of elements matching @var{pred} in @var{list}. - -See also: @code{-splice} (@pxref{-splice}), @code{-insert-at} (@pxref{-insert-at}) - -@example -@group -(-splice-list 'keywordp '(a b c) '(1 :foo 2)) - @result{} (1 a b c 2) -@end group -@group -(-splice-list 'keywordp nil '(1 :foo 2)) - @result{} (1 2) -@end group -@group -(--splice-list (keywordp it) '(a b c) '(1 :foo 2)) - @result{} (1 a b c 2) -@end group -@end example -@end defun - -@anchor{-mapcat} -@defun -mapcat (fn list) -Return the concatenation of the result of mapping @var{fn} over @var{list}. -Thus function @var{fn} should return a list. - -@example -@group -(-mapcat 'list '(1 2 3)) - @result{} (1 2 3) -@end group -@group -(-mapcat (lambda (item) (list 0 item)) '(1 2 3)) - @result{} (0 1 0 2 0 3) -@end group -@group -(--mapcat (list 0 it) '(1 2 3)) - @result{} (0 1 0 2 0 3) -@end group -@end example -@end defun - -@anchor{-copy} -@defun -copy (list) -Create a shallow copy of @var{list}. - -@example -@group -(-copy '(1 2 3)) - @result{} (1 2 3) -@end group -@group -(let ((a '(1 2 3))) (eq a (-copy a))) - @result{} nil -@end group -@end example -@end defun - -@node Sublist selection -@section Sublist selection - -Functions returning a sublist of the original list. - -@anchor{-filter} -@defun -filter (pred list) -Return a new list of the items in @var{list} for which @var{pred} returns non-nil. - -Alias: @code{-select}. - -This function's anaphoric counterpart is @code{--filter}. - -For similar operations, see also @code{-keep} (@pxref{-keep}) and @code{-remove} (@pxref{-remove}). - -@example -@group -(-filter (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) - @result{} (2 4) -@end group -@group -(-filter #'natnump '(-2 -1 0 1 2)) - @result{} (0 1 2) -@end group -@group -(--filter (= 0 (% it 2)) '(1 2 3 4)) - @result{} (2 4) -@end group -@end example -@end defun - -@anchor{-remove} -@defun -remove (pred list) -Return a new list of the items in @var{list} for which @var{pred} returns nil. - -Alias: @code{-reject}. - -This function's anaphoric counterpart is @code{--remove}. - -For similar operations, see also @code{-keep} (@pxref{-keep}) and @code{-filter} (@pxref{-filter}). - -@example -@group -(-remove (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) - @result{} (1 3) -@end group -@group -(-remove #'natnump '(-2 -1 0 1 2)) - @result{} (-2 -1) -@end group -@group -(--remove (= 0 (% it 2)) '(1 2 3 4)) - @result{} (1 3) -@end group -@end example -@end defun - -@anchor{-remove-first} -@defun -remove-first (pred list) -Remove the first item from @var{list} for which @var{pred} returns non-nil. -This is a non-destructive operation, but only the front of @var{list} -leading up to the removed item is a copy; the rest is @var{list}'s -original tail. If no item is removed, then the result is a -complete copy. - -Alias: @code{-reject-first}. - -This function's anaphoric counterpart is @code{--remove-first}. - -See also @code{-map-first} (@pxref{-map-first}), @code{-remove-item} (@pxref{-remove-item}), and @code{-remove-last} (@pxref{-remove-last}). - -@example -@group -(-remove-first #'natnump '(-2 -1 0 1 2)) - @result{} (-2 -1 1 2) -@end group -@group -(-remove-first #'stringp '(1 2 "first" "second")) - @result{} (1 2 "second") -@end group -@group -(--remove-first (> it 3) '(1 2 3 4 5 6)) - @result{} (1 2 3 5 6) -@end group -@end example -@end defun - -@anchor{-remove-last} -@defun -remove-last (pred list) -Remove the last item from @var{list} for which @var{pred} returns non-nil. -The result is a copy of @var{list} regardless of whether an element is -removed. - -Alias: @code{-reject-last}. - -This function's anaphoric counterpart is @code{--remove-last}. - -See also @code{-map-last} (@pxref{-map-last}), @code{-remove-item} (@pxref{-remove-item}), and @code{-remove-first} (@pxref{-remove-first}). - -@example -@group -(-remove-last #'natnump '(1 3 5 4 7 8 10 -11)) - @result{} (1 3 5 4 7 8 -11) -@end group -@group -(-remove-last #'stringp '(1 2 "last" "second")) - @result{} (1 2 "last") -@end group -@group -(--remove-last (> it 3) '(1 2 3 4 5 6 7 8 9 10)) - @result{} (1 2 3 4 5 6 7 8 9) -@end group -@end example -@end defun - -@anchor{-remove-item} -@defun -remove-item (item list) -Return a copy of @var{list} with all occurrences of @var{item} removed. -The comparison is done with @code{equal}. - -@example -@group -(-remove-item 3 '(1 2 3 2 3 4 5 3)) - @result{} (1 2 2 4 5) -@end group -@group -(-remove-item 'foo '(foo bar baz foo)) - @result{} (bar baz) -@end group -@group -(-remove-item "bob" '("alice" "bob" "eve" "bob")) - @result{} ("alice" "eve") -@end group -@end example -@end defun - -@anchor{-non-nil} -@defun -non-nil (list) -Return a copy of @var{list} with all nil items removed. - -@example -@group -(-non-nil '(nil 1 nil 2 nil nil 3 4 nil 5 nil)) - @result{} (1 2 3 4 5) -@end group -@group -(-non-nil '((nil))) - @result{} ((nil)) -@end group -@group -(-non-nil ()) - @result{} () -@end group -@end example -@end defun - -@anchor{-slice} -@defun -slice (list from &optional to step) -Return copy of @var{list}, starting from index @var{from} to index @var{to}. - -@var{from} or @var{to} may be negative. These values are then interpreted -modulo the length of the list. - -If @var{step} is a number, only each STEPth item in the resulting -section is returned. Defaults to 1. - -@example -@group -(-slice '(1 2 3 4 5) 1) - @result{} (2 3 4 5) -@end group -@group -(-slice '(1 2 3 4 5) 0 3) - @result{} (1 2 3) -@end group -@group -(-slice '(1 2 3 4 5 6 7 8 9) 1 -1 2) - @result{} (2 4 6 8) -@end group -@end example -@end defun - -@anchor{-take} -@defun -take (n list) -Return a copy of the first @var{n} items in @var{list}. -Return a copy of @var{list} if it contains @var{n} items or fewer. -Return nil if @var{n} is zero or less. - -See also: @code{-take-last} (@pxref{-take-last}). - -@example -@group -(-take 3 '(1 2 3 4 5)) - @result{} (1 2 3) -@end group -@group -(-take 17 '(1 2 3 4 5)) - @result{} (1 2 3 4 5) -@end group -@group -(-take 0 '(1 2 3 4 5)) - @result{} () -@end group -@end example -@end defun - -@anchor{-take-last} -@defun -take-last (n list) -Return a copy of the last @var{n} items of @var{list} in order. -Return a copy of @var{list} if it contains @var{n} items or fewer. -Return nil if @var{n} is zero or less. - -See also: @code{-take} (@pxref{-take}). - -@example -@group -(-take-last 3 '(1 2 3 4 5)) - @result{} (3 4 5) -@end group -@group -(-take-last 17 '(1 2 3 4 5)) - @result{} (1 2 3 4 5) -@end group -@group -(-take-last 1 '(1 2 3 4 5)) - @result{} (5) -@end group -@end example -@end defun - -@anchor{-drop} -@defun -drop (n list) -Return the tail (not a copy) of @var{list} without the first @var{n} items. -Return nil if @var{list} contains @var{n} items or fewer. -Return @var{list} if @var{n} is zero or less. - -For another variant, see also @code{-drop-last} (@pxref{-drop-last}). - -@example -@group -(-drop 3 '(1 2 3 4 5)) - @result{} (4 5) -@end group -@group -(-drop 17 '(1 2 3 4 5)) - @result{} () -@end group -@group -(-drop 0 '(1 2 3 4 5)) - @result{} (1 2 3 4 5) -@end group -@end example -@end defun - -@anchor{-drop-last} -@defun -drop-last (n list) -Return a copy of @var{list} without its last @var{n} items. -Return a copy of @var{list} if @var{n} is zero or less. -Return nil if @var{list} contains @var{n} items or fewer. - -See also: @code{-drop} (@pxref{-drop}). - -@example -@group -(-drop-last 3 '(1 2 3 4 5)) - @result{} (1 2) -@end group -@group -(-drop-last 17 '(1 2 3 4 5)) - @result{} () -@end group -@group -(-drop-last 0 '(1 2 3 4 5)) - @result{} (1 2 3 4 5) -@end group -@end example -@end defun - -@anchor{-take-while} -@defun -take-while (pred list) -Take successive items from @var{list} for which @var{pred} returns non-nil. -@var{pred} is a function of one argument. Return a new list of the -successive elements from the start of @var{list} for which @var{pred} returns -non-nil. - -This function's anaphoric counterpart is @code{--take-while}. - -For another variant, see also @code{-drop-while} (@pxref{-drop-while}). - -@example -@group -(-take-while #'even? '(1 2 3 4)) - @result{} () -@end group -@group -(-take-while #'even? '(2 4 5 6)) - @result{} (2 4) -@end group -@group -(--take-while (< it 4) '(1 2 3 4 3 2 1)) - @result{} (1 2 3) -@end group -@end example -@end defun - -@anchor{-drop-while} -@defun -drop-while (pred list) -Drop successive items from @var{list} for which @var{pred} returns non-nil. -@var{pred} is a function of one argument. Return the tail (not a copy) -of @var{list} starting from its first element for which @var{pred} returns -nil. - -This function's anaphoric counterpart is @code{--drop-while}. - -For another variant, see also @code{-take-while} (@pxref{-take-while}). - -@example -@group -(-drop-while #'even? '(1 2 3 4)) - @result{} (1 2 3 4) -@end group -@group -(-drop-while #'even? '(2 4 5 6)) - @result{} (5 6) -@end group -@group -(--drop-while (< it 4) '(1 2 3 4 3 2 1)) - @result{} (4 3 2 1) -@end group -@end example -@end defun - -@anchor{-select-by-indices} -@defun -select-by-indices (indices list) -Return a list whose elements are elements from @var{list} selected -as `(nth i list)` for all i from @var{indices}. - -@example -@group -(-select-by-indices '(4 10 2 3 6) '("v" "e" "l" "o" "c" "i" "r" "a" "p" "t" "o" "r")) - @result{} ("c" "o" "l" "o" "r") -@end group -@group -(-select-by-indices '(2 1 0) '("a" "b" "c")) - @result{} ("c" "b" "a") -@end group -@group -(-select-by-indices '(0 1 2 0 1 3 3 1) '("f" "a" "r" "l")) - @result{} ("f" "a" "r" "f" "a" "l" "l" "a") -@end group -@end example -@end defun - -@anchor{-select-columns} -@defun -select-columns (columns table) -Select @var{columns} from @var{table}. - -@var{table} is a list of lists where each element represents one row. -It is assumed each row has the same length. - -Each row is transformed such that only the specified @var{columns} are -selected. - -See also: @code{-select-column} (@pxref{-select-column}), @code{-select-by-indices} (@pxref{-select-by-indices}) - -@example -@group -(-select-columns '(0 2) '((1 2 3) (a b c) (:a :b :c))) - @result{} ((1 3) (a c) (:a :c)) -@end group -@group -(-select-columns '(1) '((1 2 3) (a b c) (:a :b :c))) - @result{} ((2) (b) (:b)) -@end group -@group -(-select-columns nil '((1 2 3) (a b c) (:a :b :c))) - @result{} (nil nil nil) -@end group -@end example -@end defun - -@anchor{-select-column} -@defun -select-column (column table) -Select @var{column} from @var{table}. - -@var{table} is a list of lists where each element represents one row. -It is assumed each row has the same length. - -The single selected column is returned as a list. - -See also: @code{-select-columns} (@pxref{-select-columns}), @code{-select-by-indices} (@pxref{-select-by-indices}) - -@example -@group -(-select-column 1 '((1 2 3) (a b c) (:a :b :c))) - @result{} (2 b :b) -@end group -@end example -@end defun - -@node List to list -@section List to list - -Functions returning a modified copy of the input list. - -@anchor{-keep} -@defun -keep (fn list) -Return a new list of the non-nil results of applying @var{fn} to each item in @var{list}. -Like @code{-filter} (@pxref{-filter}), but returns the non-nil results of @var{fn} instead of -the corresponding elements of @var{list}. - -Its anaphoric counterpart is @code{--keep}. - -@example -@group -(-keep #'cdr '((1 2 3) (4 5) (6))) - @result{} ((2 3) (5)) -@end group -@group -(-keep (lambda (n) (and (> n 3) (* 10 n))) '(1 2 3 4 5 6)) - @result{} (40 50 60) -@end group -@group -(--keep (and (> it 3) (* 10 it)) '(1 2 3 4 5 6)) - @result{} (40 50 60) -@end group -@end example -@end defun - -@anchor{-concat} -@defun -concat (&rest lists) -Return a new list with the concatenation of the elements in the supplied @var{lists}. - -@example -@group -(-concat '(1)) - @result{} (1) -@end group -@group -(-concat '(1) '(2)) - @result{} (1 2) -@end group -@group -(-concat '(1) '(2 3) '(4)) - @result{} (1 2 3 4) -@end group -@end example -@end defun - -@anchor{-flatten} -@defun -flatten (l) -Take a nested list @var{l} and return its contents as a single, flat list. - -Note that because @code{nil} represents a list of zero elements (an -empty list), any mention of nil in @var{l} will disappear after -flattening. If you need to preserve nils, consider @code{-flatten-n} (@pxref{-flatten-n}) -or map them to some unique symbol and then map them back. - -Conses of two atoms are considered "terminals", that is, they -aren't flattened further. - -See also: @code{-flatten-n} (@pxref{-flatten-n}) - -@example -@group -(-flatten '((1))) - @result{} (1) -@end group -@group -(-flatten '((1 (2 3) (((4 (5))))))) - @result{} (1 2 3 4 5) -@end group -@group -(-flatten '(1 2 (3 . 4))) - @result{} (1 2 (3 . 4)) -@end group -@end example -@end defun - -@anchor{-flatten-n} -@defun -flatten-n (num list) -Flatten @var{num} levels of a nested @var{list}. - -See also: @code{-flatten} (@pxref{-flatten}) - -@example -@group -(-flatten-n 1 '((1 2) ((3 4) ((5 6))))) - @result{} (1 2 (3 4) ((5 6))) -@end group -@group -(-flatten-n 2 '((1 2) ((3 4) ((5 6))))) - @result{} (1 2 3 4 (5 6)) -@end group -@group -(-flatten-n 3 '((1 2) ((3 4) ((5 6))))) - @result{} (1 2 3 4 5 6) -@end group -@end example -@end defun - -@anchor{-replace} -@defun -replace (old new list) -Replace all @var{old} items in @var{list} with @var{new}. - -Elements are compared using @code{equal}. - -See also: @code{-replace-at} (@pxref{-replace-at}) - -@example -@group -(-replace 1 "1" '(1 2 3 4 3 2 1)) - @result{} ("1" 2 3 4 3 2 "1") -@end group -@group -(-replace "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) - @result{} ("a" "nice" "bar" "sentence" "about" "bar") -@end group -@group -(-replace 1 2 nil) - @result{} nil -@end group -@end example -@end defun - -@anchor{-replace-first} -@defun -replace-first (old new list) -Replace the first occurrence of @var{old} with @var{new} in @var{list}. - -Elements are compared using @code{equal}. - -See also: @code{-map-first} (@pxref{-map-first}) - -@example -@group -(-replace-first 1 "1" '(1 2 3 4 3 2 1)) - @result{} ("1" 2 3 4 3 2 1) -@end group -@group -(-replace-first "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) - @result{} ("a" "nice" "bar" "sentence" "about" "foo") -@end group -@group -(-replace-first 1 2 nil) - @result{} nil -@end group -@end example -@end defun - -@anchor{-replace-last} -@defun -replace-last (old new list) -Replace the last occurrence of @var{old} with @var{new} in @var{list}. - -Elements are compared using @code{equal}. - -See also: @code{-map-last} (@pxref{-map-last}) - -@example -@group -(-replace-last 1 "1" '(1 2 3 4 3 2 1)) - @result{} (1 2 3 4 3 2 "1") -@end group -@group -(-replace-last "foo" "bar" '("a" "nice" "foo" "sentence" "about" "foo")) - @result{} ("a" "nice" "foo" "sentence" "about" "bar") -@end group -@group -(-replace-last 1 2 nil) - @result{} nil -@end group -@end example -@end defun - -@anchor{-insert-at} -@defun -insert-at (n x list) -Return a list with @var{x} inserted into @var{list} at position @var{n}. - -See also: @code{-splice} (@pxref{-splice}), @code{-splice-list} (@pxref{-splice-list}) - -@example -@group -(-insert-at 1 'x '(a b c)) - @result{} (a x b c) -@end group -@group -(-insert-at 12 'x '(a b c)) - @result{} (a b c x) -@end group -@end example -@end defun - -@anchor{-replace-at} -@defun -replace-at (n x list) -Return a list with element at Nth position in @var{list} replaced with @var{x}. - -See also: @code{-replace} (@pxref{-replace}) - -@example -@group -(-replace-at 0 9 '(0 1 2 3 4 5)) - @result{} (9 1 2 3 4 5) -@end group -@group -(-replace-at 1 9 '(0 1 2 3 4 5)) - @result{} (0 9 2 3 4 5) -@end group -@group -(-replace-at 4 9 '(0 1 2 3 4 5)) - @result{} (0 1 2 3 9 5) -@end group -@end example -@end defun - -@anchor{-update-at} -@defun -update-at (n func list) -Return a list with element at Nth position in @var{list} replaced with `(func (nth n list))`. - -See also: @code{-map-when} (@pxref{-map-when}) - -@example -@group -(-update-at 0 (lambda (x) (+ x 9)) '(0 1 2 3 4 5)) - @result{} (9 1 2 3 4 5) -@end group -@group -(-update-at 1 (lambda (x) (+ x 8)) '(0 1 2 3 4 5)) - @result{} (0 9 2 3 4 5) -@end group -@group -(--update-at 2 (length it) '("foo" "bar" "baz" "quux")) - @result{} ("foo" "bar" 3 "quux") -@end group -@end example -@end defun - -@anchor{-remove-at} -@defun -remove-at (n list) -Return a list with element at Nth position in @var{list} removed. - -See also: @code{-remove-at-indices} (@pxref{-remove-at-indices}), @code{-remove} (@pxref{-remove}) - -@example -@group -(-remove-at 0 '("0" "1" "2" "3" "4" "5")) - @result{} ("1" "2" "3" "4" "5") -@end group -@group -(-remove-at 1 '("0" "1" "2" "3" "4" "5")) - @result{} ("0" "2" "3" "4" "5") -@end group -@group -(-remove-at 2 '("0" "1" "2" "3" "4" "5")) - @result{} ("0" "1" "3" "4" "5") -@end group -@end example -@end defun - -@anchor{-remove-at-indices} -@defun -remove-at-indices (indices list) -Return a list whose elements are elements from @var{list} without -elements selected as `(nth i list)` for all i -from @var{indices}. - -See also: @code{-remove-at} (@pxref{-remove-at}), @code{-remove} (@pxref{-remove}) - -@example -@group -(-remove-at-indices '(0) '("0" "1" "2" "3" "4" "5")) - @result{} ("1" "2" "3" "4" "5") -@end group -@group -(-remove-at-indices '(0 2 4) '("0" "1" "2" "3" "4" "5")) - @result{} ("1" "3" "5") -@end group -@group -(-remove-at-indices '(0 5) '("0" "1" "2" "3" "4" "5")) - @result{} ("1" "2" "3" "4") -@end group -@end example -@end defun - -@node Reductions -@section Reductions - -Functions reducing lists to a single value (which may also be a list). - -@anchor{-reduce-from} -@defun -reduce-from (fn init list) -Reduce the function @var{fn} across @var{list}, starting with @var{init}. -Return the result of applying @var{fn} to @var{init} and the first element of -@var{list}, then applying @var{fn} to that result and the second element, -etc. If @var{list} is empty, return @var{init} without calling @var{fn}. - -This function's anaphoric counterpart is @code{--reduce-from}. - -For other folds, see also @code{-reduce} (@pxref{-reduce}) and @code{-reduce-r} (@pxref{-reduce-r}). - -@example -@group -(-reduce-from #'- 10 '(1 2 3)) - @result{} 4 -@end group -@group -(-reduce-from #'list 10 '(1 2 3)) - @result{} (((10 1) 2) 3) -@end group -@group -(--reduce-from (concat acc " " it) "START" '("a" "b" "c")) - @result{} "START a b c" -@end group -@end example -@end defun - -@anchor{-reduce-r-from} -@defun -reduce-r-from (fn init list) -Reduce the function @var{fn} across @var{list} in reverse, starting with @var{init}. -Return the result of applying @var{fn} to the last element of @var{list} and -@var{init}, then applying @var{fn} to the second-to-last element and the -previous result of @var{fn}, etc. That is, the first argument of @var{fn} is -the current element, and its second argument the accumulated -value. If @var{list} is empty, return @var{init} without calling @var{fn}. - -This function is like @code{-reduce-from} (@pxref{-reduce-from}) but the operation associates -from the right rather than left. In other words, it starts from -the end of @var{list} and flips the arguments to @var{fn}. Conceptually, it -is like replacing the conses in @var{list} with applications of @var{fn}, and -its last link with @var{init}, and evaluating the resulting expression. - -This function's anaphoric counterpart is @code{--reduce-r-from}. - -For other folds, see also @code{-reduce-r} (@pxref{-reduce-r}) and @code{-reduce} (@pxref{-reduce}). - -@example -@group -(-reduce-r-from #'- 10 '(1 2 3)) - @result{} -8 -@end group -@group -(-reduce-r-from #'list 10 '(1 2 3)) - @result{} (1 (2 (3 10))) -@end group -@group -(--reduce-r-from (concat it " " acc) "END" '("a" "b" "c")) - @result{} "a b c END" -@end group -@end example -@end defun - -@anchor{-reduce} -@defun -reduce (fn list) -Reduce the function @var{fn} across @var{list}. -Return the result of applying @var{fn} to the first two elements of -@var{list}, then applying @var{fn} to that result and the third element, etc. -If @var{list} contains a single element, return it without calling @var{fn}. -If @var{list} is empty, return the result of calling @var{fn} with no -arguments. - -This function's anaphoric counterpart is @code{--reduce}. - -For other folds, see also @code{-reduce-from} (@pxref{-reduce-from}) and @code{-reduce-r} (@pxref{-reduce-r}). - -@example -@group -(-reduce #'- '(1 2 3 4)) - @result{} -8 -@end group -@group -(-reduce #'list '(1 2 3 4)) - @result{} (((1 2) 3) 4) -@end group -@group -(--reduce (format "%s-%d" acc it) '(1 2 3)) - @result{} "1-2-3" -@end group -@end example -@end defun - -@anchor{-reduce-r} -@defun -reduce-r (fn list) -Reduce the function @var{fn} across @var{list} in reverse. -Return the result of applying @var{fn} to the last two elements of -@var{list}, then applying @var{fn} to the third-to-last element and the -previous result of @var{fn}, etc. That is, the first argument of @var{fn} is -the current element, and its second argument the accumulated -value. If @var{list} contains a single element, return it without -calling @var{fn}. If @var{list} is empty, return the result of calling @var{fn} -with no arguments. - -This function is like @code{-reduce} (@pxref{-reduce}) but the operation associates from -the right rather than left. In other words, it starts from the -end of @var{list} and flips the arguments to @var{fn}. Conceptually, it is -like replacing the conses in @var{list} with applications of @var{fn}, -ignoring its last link, and evaluating the resulting expression. - -This function's anaphoric counterpart is @code{--reduce-r}. - -For other folds, see also @code{-reduce-r-from} (@pxref{-reduce-r-from}) and @code{-reduce} (@pxref{-reduce}). - -@example -@group -(-reduce-r #'- '(1 2 3 4)) - @result{} -2 -@end group -@group -(-reduce-r #'list '(1 2 3 4)) - @result{} (1 (2 (3 4))) -@end group -@group -(--reduce-r (format "%s-%d" acc it) '(1 2 3)) - @result{} "3-2-1" -@end group -@end example -@end defun - -@anchor{-reductions-from} -@defun -reductions-from (fn init list) -Return a list of @var{fn}'s intermediate reductions across @var{list}. -That is, a list of the intermediate values of the accumulator -when @code{-reduce-from} (@pxref{-reduce-from}) (which see) is called with the same -arguments. - -This function's anaphoric counterpart is @code{--reductions-from}. - -For other folds, see also @code{-reductions} (@pxref{-reductions}) and @code{-reductions-r} (@pxref{-reductions-r}). - -@example -@group -(-reductions-from #'max 0 '(2 1 4 3)) - @result{} (0 2 2 4 4) -@end group -@group -(-reductions-from #'* 1 '(1 2 3 4)) - @result{} (1 1 2 6 24) -@end group -@group -(--reductions-from (format "(FN %s %d)" acc it) "INIT" '(1 2 3)) - @result{} ("INIT" "(FN INIT 1)" "(FN (FN INIT 1) 2)" "(FN (FN (FN INIT 1) 2) 3)") -@end group -@end example -@end defun - -@anchor{-reductions-r-from} -@defun -reductions-r-from (fn init list) -Return a list of @var{fn}'s intermediate reductions across reversed @var{list}. -That is, a list of the intermediate values of the accumulator -when @code{-reduce-r-from} (@pxref{-reduce-r-from}) (which see) is called with the same -arguments. - -This function's anaphoric counterpart is @code{--reductions-r-from}. - -For other folds, see also @code{-reductions} (@pxref{-reductions}) and @code{-reductions-r} (@pxref{-reductions-r}). - -@example -@group -(-reductions-r-from #'max 0 '(2 1 4 3)) - @result{} (4 4 4 3 0) -@end group -@group -(-reductions-r-from #'* 1 '(1 2 3 4)) - @result{} (24 24 12 4 1) -@end group -@group -(--reductions-r-from (format "(FN %d %s)" it acc) "INIT" '(1 2 3)) - @result{} ("(FN 1 (FN 2 (FN 3 INIT)))" "(FN 2 (FN 3 INIT))" "(FN 3 INIT)" "INIT") -@end group -@end example -@end defun - -@anchor{-reductions} -@defun -reductions (fn list) -Return a list of @var{fn}'s intermediate reductions across @var{list}. -That is, a list of the intermediate values of the accumulator -when @code{-reduce} (@pxref{-reduce}) (which see) is called with the same arguments. - -This function's anaphoric counterpart is @code{--reductions}. - -For other folds, see also @code{-reductions} (@pxref{-reductions}) and @code{-reductions-r} (@pxref{-reductions-r}). - -@example -@group -(-reductions #'+ '(1 2 3 4)) - @result{} (1 3 6 10) -@end group -@group -(-reductions #'* '(1 2 3 4)) - @result{} (1 2 6 24) -@end group -@group -(--reductions (format "(FN %s %d)" acc it) '(1 2 3)) - @result{} (1 "(FN 1 2)" "(FN (FN 1 2) 3)") -@end group -@end example -@end defun - -@anchor{-reductions-r} -@defun -reductions-r (fn list) -Return a list of @var{fn}'s intermediate reductions across reversed @var{list}. -That is, a list of the intermediate values of the accumulator -when @code{-reduce-r} (@pxref{-reduce-r}) (which see) is called with the same arguments. - -This function's anaphoric counterpart is @code{--reductions-r}. - -For other folds, see also @code{-reductions-r-from} (@pxref{-reductions-r-from}) and -@code{-reductions} (@pxref{-reductions}). - -@example -@group -(-reductions-r #'+ '(1 2 3 4)) - @result{} (10 9 7 4) -@end group -@group -(-reductions-r #'* '(1 2 3 4)) - @result{} (24 24 12 4) -@end group -@group -(--reductions-r (format "(FN %d %s)" it acc) '(1 2 3)) - @result{} ("(FN 1 (FN 2 3))" "(FN 2 3)" 3) -@end group -@end example -@end defun - -@anchor{-count} -@defun -count (pred list) -Counts the number of items in @var{list} where (@var{pred} item) is non-nil. - -@example -@group -(-count 'even? '(1 2 3 4 5)) - @result{} 2 -@end group -@group -(--count (< it 4) '(1 2 3 4)) - @result{} 3 -@end group -@end example -@end defun - -@anchor{-sum} -@defun -sum (list) -Return the sum of @var{list}. - -@example -@group -(-sum ()) - @result{} 0 -@end group -@group -(-sum '(1)) - @result{} 1 -@end group -@group -(-sum '(1 2 3 4)) - @result{} 10 -@end group -@end example -@end defun - -@anchor{-running-sum} -@defun -running-sum (list) -Return a list with running sums of items in @var{list}. -@var{list} must be non-empty. - -@example -@group -(-running-sum '(1 2 3 4)) - @result{} (1 3 6 10) -@end group -@group -(-running-sum '(1)) - @result{} (1) -@end group -@group -(-running-sum ()) - @error{} Wrong type argument: consp, nil -@end group -@end example -@end defun - -@anchor{-product} -@defun -product (list) -Return the product of @var{list}. - -@example -@group -(-product ()) - @result{} 1 -@end group -@group -(-product '(1)) - @result{} 1 -@end group -@group -(-product '(1 2 3 4)) - @result{} 24 -@end group -@end example -@end defun - -@anchor{-running-product} -@defun -running-product (list) -Return a list with running products of items in @var{list}. -@var{list} must be non-empty. - -@example -@group -(-running-product '(1 2 3 4)) - @result{} (1 2 6 24) -@end group -@group -(-running-product '(1)) - @result{} (1) -@end group -@group -(-running-product ()) - @error{} Wrong type argument: consp, nil -@end group -@end example -@end defun - -@anchor{-inits} -@defun -inits (list) -Return all prefixes of @var{list}. - -@example -@group -(-inits '(1 2 3 4)) - @result{} (nil (1) (1 2) (1 2 3) (1 2 3 4)) -@end group -@group -(-inits nil) - @result{} (nil) -@end group -@group -(-inits '(1)) - @result{} (nil (1)) -@end group -@end example -@end defun - -@anchor{-tails} -@defun -tails (list) -Return all suffixes of @var{list} - -@example -@group -(-tails '(1 2 3 4)) - @result{} ((1 2 3 4) (2 3 4) (3 4) (4) nil) -@end group -@group -(-tails nil) - @result{} (nil) -@end group -@group -(-tails '(1)) - @result{} ((1) nil) -@end group -@end example -@end defun - -@anchor{-common-prefix} -@defun -common-prefix (&rest lists) -Return the longest common prefix of @var{lists}. - -@example -@group -(-common-prefix '(1)) - @result{} (1) -@end group -@group -(-common-prefix '(1 2) '(3 4) '(1 2)) - @result{} () -@end group -@group -(-common-prefix '(1 2) '(1 2 3) '(1 2 3 4)) - @result{} (1 2) -@end group -@end example -@end defun - -@anchor{-common-suffix} -@defun -common-suffix (&rest lists) -Return the longest common suffix of @var{lists}. - -@example -@group -(-common-suffix '(1)) - @result{} (1) -@end group -@group -(-common-suffix '(1 2) '(3 4) '(1 2)) - @result{} () -@end group -@group -(-common-suffix '(1 2 3 4) '(2 3 4) '(3 4)) - @result{} (3 4) -@end group -@end example -@end defun - -@anchor{-min} -@defun -min (list) -Return the smallest value from @var{list} of numbers or markers. - -@example -@group -(-min '(0)) - @result{} 0 -@end group -@group -(-min '(3 2 1)) - @result{} 1 -@end group -@group -(-min '(1 2 3)) - @result{} 1 -@end group -@end example -@end defun - -@anchor{-min-by} -@defun -min-by (comparator list) -Take a comparison function @var{comparator} and a @var{list} and return -the least element of the list by the comparison function. - -See also combinator @code{-on} (@pxref{-on}) which can transform the values before -comparing them. - -@example -@group -(-min-by '> '(4 3 6 1)) - @result{} 1 -@end group -@group -(--min-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) - @result{} (1 2 3) -@end group -@group -(--min-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) - @result{} (2) -@end group -@end example -@end defun - -@anchor{-max} -@defun -max (list) -Return the largest value from @var{list} of numbers or markers. - -@example -@group -(-max '(0)) - @result{} 0 -@end group -@group -(-max '(3 2 1)) - @result{} 3 -@end group -@group -(-max '(1 2 3)) - @result{} 3 -@end group -@end example -@end defun - -@anchor{-max-by} -@defun -max-by (comparator list) -Take a comparison function @var{comparator} and a @var{list} and return -the greatest element of the list by the comparison function. - -See also combinator @code{-on} (@pxref{-on}) which can transform the values before -comparing them. - -@example -@group -(-max-by '> '(4 3 6 1)) - @result{} 6 -@end group -@group -(--max-by (> (car it) (car other)) '((1 2 3) (2) (3 2))) - @result{} (3 2) -@end group -@group -(--max-by (> (length it) (length other)) '((1 2 3) (2) (3 2))) - @result{} (1 2 3) -@end group -@end example -@end defun - -@node Unfolding -@section Unfolding - -Operations dual to reductions, building lists from a seed -value rather than consuming a list to produce a single value. - -@anchor{-iterate} -@defun -iterate (fun init n) -Return a list of iterated applications of @var{fun} to @var{init}. - -This means a list of the form: - - (@var{init} (@var{fun} @var{init}) (@var{fun} (@var{fun} @var{init})) @dots{}) - -@var{n} is the length of the returned list. - -@example -@group -(-iterate #'1+ 1 10) - @result{} (1 2 3 4 5 6 7 8 9 10) -@end group -@group -(-iterate (lambda (x) (+ x x)) 2 5) - @result{} (2 4 8 16 32) -@end group -@group -(--iterate (* it it) 2 5) - @result{} (2 4 16 256 65536) -@end group -@end example -@end defun - -@anchor{-unfold} -@defun -unfold (fun seed) -Build a list from @var{seed} using @var{fun}. - -This is "dual" operation to @code{-reduce-r} (@pxref{-reduce-r}): while -reduce-r -consumes a list to produce a single value, @code{-unfold} (@pxref{-unfold}) takes a -seed value and builds a (potentially infinite!) list. - -@var{fun} should return @code{nil} to stop the generating process, or a -cons (@var{a} . @var{b}), where @var{a} will be prepended to the result and @var{b} is -the new seed. - -@example -@group -(-unfold (lambda (x) (unless (= x 0) (cons x (1- x)))) 10) - @result{} (10 9 8 7 6 5 4 3 2 1) -@end group -@group -(--unfold (when it (cons it (cdr it))) '(1 2 3 4)) - @result{} ((1 2 3 4) (2 3 4) (3 4) (4)) -@end group -@group -(--unfold (when it (cons it (butlast it))) '(1 2 3 4)) - @result{} ((1 2 3 4) (1 2 3) (1 2) (1)) -@end group -@end example -@end defun - -@node Predicates -@section Predicates - -Reductions of one or more lists to a boolean value. - -@anchor{-some} -@defun -some (pred list) -Return (@var{pred} x) for the first @var{list} item where (@var{pred} x) is non-nil, else nil. - -Alias: @code{-any}. - -This function's anaphoric counterpart is @code{--some}. - -@example -@group -(-some #'stringp '(1 "2" 3)) - @result{} t -@end group -@group -(--some (string-match-p "x" it) '("foo" "axe" "xor")) - @result{} 1 -@end group -@group -(--some (= it-index 3) '(0 1 2)) - @result{} nil -@end group -@end example -@end defun - -@anchor{-every} -@defun -every (pred list) -Return non-nil if @var{pred} returns non-nil for all items in @var{list}. -If so, return the last such result of @var{pred}. Otherwise, once an -item is reached for which @var{pred} returns nil, return nil without -calling @var{pred} on any further @var{list} elements. - -This function is like @code{-every-p}, but on success returns the last -non-nil result of @var{pred} instead of just t. - -This function's anaphoric counterpart is @code{--every}. - -@example -@group -(-every #'numberp '(1 2 3)) - @result{} t -@end group -@group -(--every (string-match-p "x" it) '("axe" "xor")) - @result{} 0 -@end group -@group -(--every (= it it-index) '(0 1 3)) - @result{} nil -@end group -@end example -@end defun - -@anchor{-any?} -@defun -any? (pred list) -Return t if (@var{pred} x) is non-nil for any x in @var{list}, else nil. - -Alias: @code{-any-p}, @code{-some?}, @code{-some-p} - -@example -@group -(-any? #'numberp '(nil 0 t)) - @result{} t -@end group -@group -(-any? #'numberp '(nil t t)) - @result{} nil -@end group -@group -(-any? #'null '(1 3 5)) - @result{} nil -@end group -@end example -@end defun - -@anchor{-all?} -@defun -all? (pred list) -Return t if (@var{pred} @var{x}) is non-nil for all @var{x} in @var{list}, else nil. -In the latter case, stop after the first @var{x} for which (@var{pred} @var{x}) is -nil, without calling @var{pred} on any subsequent elements of @var{list}. - -The similar function @code{-every} (@pxref{-every}) is more widely useful, since it -returns the last non-nil result of @var{pred} instead of just t on -success. - -Alias: @code{-all-p}, @code{-every-p}, @code{-every?}. - -This function's anaphoric counterpart is @code{--all?}. - -@example -@group -(-all? #'numberp '(1 2 3)) - @result{} t -@end group -@group -(-all? #'numberp '(2 t 6)) - @result{} nil -@end group -@group -(--all? (= 0 (% it 2)) '(2 4 6)) - @result{} t -@end group -@end example -@end defun - -@anchor{-none?} -@defun -none? (pred list) -Return t if (@var{pred} x) is nil for all x in @var{list}, else nil. - -Alias: @code{-none-p} - -@example -@group -(-none? 'even? '(1 2 3)) - @result{} nil -@end group -@group -(-none? 'even? '(1 3 5)) - @result{} t -@end group -@group -(--none? (= 0 (% it 2)) '(1 2 3)) - @result{} nil -@end group -@end example -@end defun - -@anchor{-only-some?} -@defun -only-some? (pred list) -Return `t` if at least one item of @var{list} matches @var{pred} and at least one item of @var{list} does not match @var{pred}. -Return `nil` both if all items match the predicate or if none of the items match the predicate. - -Alias: @code{-only-some-p} - -@example -@group -(-only-some? 'even? '(1 2 3)) - @result{} t -@end group -@group -(-only-some? 'even? '(1 3 5)) - @result{} nil -@end group -@group -(-only-some? 'even? '(2 4 6)) - @result{} nil -@end group -@end example -@end defun - -@anchor{-contains?} -@defun -contains? (list element) -Return non-nil if @var{list} contains @var{element}. - -The test for equality is done with @code{equal}, or with @code{-compare-fn} -if that's non-nil. - -Alias: @code{-contains-p} - -@example -@group -(-contains? '(1 2 3) 1) - @result{} t -@end group -@group -(-contains? '(1 2 3) 2) - @result{} t -@end group -@group -(-contains? '(1 2 3) 4) - @result{} nil -@end group -@end example -@end defun - -@anchor{-same-items?} -@defun -same-items? (list list2) -Return true if @var{list} and @var{list2} has the same items. - -The order of the elements in the lists does not matter. - -Alias: @code{-same-items-p} - -@example -@group -(-same-items? '(1 2 3) '(1 2 3)) - @result{} t -@end group -@group -(-same-items? '(1 2 3) '(3 2 1)) - @result{} t -@end group -@group -(-same-items? '(1 2 3) '(1 2 3 4)) - @result{} nil -@end group -@end example -@end defun - -@anchor{-is-prefix?} -@defun -is-prefix? (prefix list) -Return non-nil if @var{prefix} is a prefix of @var{list}. - -Alias: @code{-is-prefix-p}. - -@example -@group -(-is-prefix? '(1 2 3) '(1 2 3 4 5)) - @result{} t -@end group -@group -(-is-prefix? '(1 2 3 4 5) '(1 2 3)) - @result{} nil -@end group -@group -(-is-prefix? '(1 3) '(1 2 3 4 5)) - @result{} nil -@end group -@end example -@end defun - -@anchor{-is-suffix?} -@defun -is-suffix? (suffix list) -Return non-nil if @var{suffix} is a suffix of @var{list}. - -Alias: @code{-is-suffix-p}. - -@example -@group -(-is-suffix? '(3 4 5) '(1 2 3 4 5)) - @result{} t -@end group -@group -(-is-suffix? '(1 2 3 4 5) '(3 4 5)) - @result{} nil -@end group -@group -(-is-suffix? '(3 5) '(1 2 3 4 5)) - @result{} nil -@end group -@end example -@end defun - -@anchor{-is-infix?} -@defun -is-infix? (infix list) -Return non-nil if @var{infix} is infix of @var{list}. - -This operation runs in @var{o}(n^2) time - -Alias: @code{-is-infix-p} - -@example -@group -(-is-infix? '(1 2 3) '(1 2 3 4 5)) - @result{} t -@end group -@group -(-is-infix? '(2 3 4) '(1 2 3 4 5)) - @result{} t -@end group -@group -(-is-infix? '(3 4 5) '(1 2 3 4 5)) - @result{} t -@end group -@end example -@end defun - -@anchor{-cons-pair?} -@defun -cons-pair? (obj) -Return non-nil if @var{obj} is a true cons pair. -That is, a cons (@var{a} . @var{b}) where @var{b} is not a list. - -Alias: @code{-cons-pair-p}. - -@example -@group -(-cons-pair? '(1 . 2)) - @result{} t -@end group -@group -(-cons-pair? '(1 2)) - @result{} nil -@end group -@group -(-cons-pair? '(1)) - @result{} nil -@end group -@end example -@end defun - -@node Partitioning -@section Partitioning - -Functions partitioning the input list into a list of lists. - -@anchor{-split-at} -@defun -split-at (n list) -Split @var{list} into two sublists after the Nth element. -The result is a list of two elements (@var{take} @var{drop}) where @var{take} is a -new list of the first @var{n} elements of @var{list}, and @var{drop} is the -remaining elements of @var{list} (not a copy). @var{take} and @var{drop} are like -the results of @code{-take} (@pxref{-take}) and @code{-drop} (@pxref{-drop}), respectively, but the split -is done in a single list traversal. - -@example -@group -(-split-at 3 '(1 2 3 4 5)) - @result{} ((1 2 3) (4 5)) -@end group -@group -(-split-at 17 '(1 2 3 4 5)) - @result{} ((1 2 3 4 5) nil) -@end group -@group -(-split-at 0 '(1 2 3 4 5)) - @result{} (nil (1 2 3 4 5)) -@end group -@end example -@end defun - -@anchor{-split-with} -@defun -split-with (pred list) -Return a list of ((-take-while @var{pred} @var{list}) (-drop-while @var{pred} @var{list})), in no more than one pass through the list. - -@example -@group -(-split-with 'even? '(1 2 3 4)) - @result{} (nil (1 2 3 4)) -@end group -@group -(-split-with 'even? '(2 4 5 6)) - @result{} ((2 4) (5 6)) -@end group -@group -(--split-with (< it 4) '(1 2 3 4 3 2 1)) - @result{} ((1 2 3) (4 3 2 1)) -@end group -@end example -@end defun - -@anchor{-split-on} -@defmac -split-on (item list) -Split the @var{list} each time @var{item} is found. - -Unlike @code{-partition-by} (@pxref{-partition-by}), the @var{item} is discarded from the results. -Empty lists are also removed from the result. - -Comparison is done by @code{equal}. - -See also @code{-split-when} (@pxref{-split-when}) - -@example -@group -(-split-on '| '(Nil | Leaf a | Node [Tree a])) - @result{} ((Nil) (Leaf a) (Node [Tree a])) -@end group -@group -(-split-on :endgroup '("a" "b" :endgroup "c" :endgroup "d" "e")) - @result{} (("a" "b") ("c") ("d" "e")) -@end group -@group -(-split-on :endgroup '("a" "b" :endgroup :endgroup "d" "e")) - @result{} (("a" "b") ("d" "e")) -@end group -@end example -@end defmac - -@anchor{-split-when} -@defun -split-when (fn list) -Split the @var{list} on each element where @var{fn} returns non-nil. - -Unlike @code{-partition-by} (@pxref{-partition-by}), the "matched" element is discarded from -the results. Empty lists are also removed from the result. - -This function can be thought of as a generalization of -@code{split-string}. - -@example -@group -(-split-when 'even? '(1 2 3 4 5 6)) - @result{} ((1) (3) (5)) -@end group -@group -(-split-when 'even? '(1 2 3 4 6 8 9)) - @result{} ((1) (3) (9)) -@end group -@group -(--split-when (memq it '(&optional &rest)) '(a b &optional c d &rest args)) - @result{} ((a b) (c d) (args)) -@end group -@end example -@end defun - -@anchor{-separate} -@defun -separate (pred list) -Return a list of ((-filter @var{pred} @var{list}) (-remove @var{pred} @var{list})), in one pass through the list. - -@example -@group -(-separate (lambda (num) (= 0 (% num 2))) '(1 2 3 4 5 6 7)) - @result{} ((2 4 6) (1 3 5 7)) -@end group -@group -(--separate (< it 5) '(3 7 5 9 3 2 1 4 6)) - @result{} ((3 3 2 1 4) (7 5 9 6)) -@end group -@group -(-separate 'cdr '((1 2) (1) (1 2 3) (4))) - @result{} (((1 2) (1 2 3)) ((1) (4))) -@end group -@end example -@end defun - -@anchor{-partition} -@defun -partition (n list) -Return a new list with the items in @var{list} grouped into @var{n}-sized sublists. -If there are not enough items to make the last group @var{n}-sized, -those items are discarded. - -@example -@group -(-partition 2 '(1 2 3 4 5 6)) - @result{} ((1 2) (3 4) (5 6)) -@end group -@group -(-partition 2 '(1 2 3 4 5 6 7)) - @result{} ((1 2) (3 4) (5 6)) -@end group -@group -(-partition 3 '(1 2 3 4 5 6 7)) - @result{} ((1 2 3) (4 5 6)) -@end group -@end example -@end defun - -@anchor{-partition-all} -@defun -partition-all (n list) -Return a new list with the items in @var{list} grouped into @var{n}-sized sublists. -The last group may contain less than @var{n} items. - -@example -@group -(-partition-all 2 '(1 2 3 4 5 6)) - @result{} ((1 2) (3 4) (5 6)) -@end group -@group -(-partition-all 2 '(1 2 3 4 5 6 7)) - @result{} ((1 2) (3 4) (5 6) (7)) -@end group -@group -(-partition-all 3 '(1 2 3 4 5 6 7)) - @result{} ((1 2 3) (4 5 6) (7)) -@end group -@end example -@end defun - -@anchor{-partition-in-steps} -@defun -partition-in-steps (n step list) -Return a new list with the items in @var{list} grouped into @var{n}-sized sublists at offsets @var{step} apart. -If there are not enough items to make the last group @var{n}-sized, -those items are discarded. - -@example -@group -(-partition-in-steps 2 1 '(1 2 3 4)) - @result{} ((1 2) (2 3) (3 4)) -@end group -@group -(-partition-in-steps 3 2 '(1 2 3 4)) - @result{} ((1 2 3)) -@end group -@group -(-partition-in-steps 3 2 '(1 2 3 4 5)) - @result{} ((1 2 3) (3 4 5)) -@end group -@end example -@end defun - -@anchor{-partition-all-in-steps} -@defun -partition-all-in-steps (n step list) -Return a new list with the items in @var{list} grouped into @var{n}-sized sublists at offsets @var{step} apart. -The last groups may contain less than @var{n} items. - -@example -@group -(-partition-all-in-steps 2 1 '(1 2 3 4)) - @result{} ((1 2) (2 3) (3 4) (4)) -@end group -@group -(-partition-all-in-steps 3 2 '(1 2 3 4)) - @result{} ((1 2 3) (3 4)) -@end group -@group -(-partition-all-in-steps 3 2 '(1 2 3 4 5)) - @result{} ((1 2 3) (3 4 5) (5)) -@end group -@end example -@end defun - -@anchor{-partition-by} -@defun -partition-by (fn list) -Apply @var{fn} to each item in @var{list}, splitting it each time @var{fn} returns a new value. - -@example -@group -(-partition-by 'even? ()) - @result{} () -@end group -@group -(-partition-by 'even? '(1 1 2 2 2 3 4 6 8)) - @result{} ((1 1) (2 2 2) (3) (4 6 8)) -@end group -@group -(--partition-by (< it 3) '(1 2 3 4 3 2 1)) - @result{} ((1 2) (3 4 3) (2 1)) -@end group -@end example -@end defun - -@anchor{-partition-by-header} -@defun -partition-by-header (fn list) -Apply @var{fn} to the first item in @var{list}. That is the header -value. Apply @var{fn} to each item in @var{list}, splitting it each time @var{fn} -returns the header value, but only after seeing at least one -other value (the body). - -@example -@group -(--partition-by-header (= it 1) '(1 2 3 1 2 1 2 3 4)) - @result{} ((1 2 3) (1 2) (1 2 3 4)) -@end group -@group -(--partition-by-header (> it 0) '(1 2 0 1 0 1 2 3 0)) - @result{} ((1 2 0) (1 0) (1 2 3 0)) -@end group -@group -(-partition-by-header 'even? '(2 1 1 1 4 1 3 5 6 6 1)) - @result{} ((2 1 1 1) (4 1 3 5) (6 6 1)) -@end group -@end example -@end defun - -@anchor{-partition-after-pred} -@defun -partition-after-pred (pred list) -Partition @var{list} after each element for which @var{pred} returns non-nil. - -This function's anaphoric counterpart is @code{--partition-after-pred}. - -@example -@group -(-partition-after-pred #'booleanp ()) - @result{} () -@end group -@group -(-partition-after-pred #'booleanp '(t t)) - @result{} ((t) (t)) -@end group -@group -(-partition-after-pred #'booleanp '(0 0 t t 0 t)) - @result{} ((0 0 t) (t) (0 t)) -@end group -@end example -@end defun - -@anchor{-partition-before-pred} -@defun -partition-before-pred (pred list) -Partition directly before each time @var{pred} is true on an element of @var{list}. - -@example -@group -(-partition-before-pred #'booleanp ()) - @result{} () -@end group -@group -(-partition-before-pred #'booleanp '(0 t)) - @result{} ((0) (t)) -@end group -@group -(-partition-before-pred #'booleanp '(0 0 t 0 t t)) - @result{} ((0 0) (t 0) (t) (t)) -@end group -@end example -@end defun - -@anchor{-partition-before-item} -@defun -partition-before-item (item list) -Partition directly before each time @var{item} appears in @var{list}. - -@example -@group -(-partition-before-item 3 ()) - @result{} () -@end group -@group -(-partition-before-item 3 '(1)) - @result{} ((1)) -@end group -@group -(-partition-before-item 3 '(3)) - @result{} ((3)) -@end group -@end example -@end defun - -@anchor{-partition-after-item} -@defun -partition-after-item (item list) -Partition directly after each time @var{item} appears in @var{list}. - -@example -@group -(-partition-after-item 3 ()) - @result{} () -@end group -@group -(-partition-after-item 3 '(1)) - @result{} ((1)) -@end group -@group -(-partition-after-item 3 '(3)) - @result{} ((3)) -@end group -@end example -@end defun - -@anchor{-group-by} -@defun -group-by (fn list) -Separate @var{list} into an alist whose keys are @var{fn} applied to the -elements of @var{list}. Keys are compared by @code{equal}. - -@example -@group -(-group-by 'even? ()) - @result{} () -@end group -@group -(-group-by 'even? '(1 1 2 2 2 3 4 6 8)) - @result{} ((nil 1 1 3) (t 2 2 2 4 6 8)) -@end group -@group -(--group-by (car (split-string it "/")) '("a/b" "c/d" "a/e")) - @result{} (("a" "a/b" "a/e") ("c" "c/d")) -@end group -@end example -@end defun - -@node Indexing -@section Indexing - -Functions retrieving or sorting based on list indices and -related predicates. - -@anchor{-elem-index} -@defun -elem-index (elem list) -Return the index of the first element in the given @var{list} which -is equal to the query element @var{elem}, or nil if there is no -such element. - -@example -@group -(-elem-index 2 '(6 7 8 2 3 4)) - @result{} 3 -@end group -@group -(-elem-index "bar" '("foo" "bar" "baz")) - @result{} 1 -@end group -@group -(-elem-index '(1 2) '((3) (5 6) (1 2) nil)) - @result{} 2 -@end group -@end example -@end defun - -@anchor{-elem-indices} -@defun -elem-indices (elem list) -Return the indices of all elements in @var{list} equal to the query -element @var{elem}, in ascending order. - -@example -@group -(-elem-indices 2 '(6 7 8 2 3 4 2 1)) - @result{} (3 6) -@end group -@group -(-elem-indices "bar" '("foo" "bar" "baz")) - @result{} (1) -@end group -@group -(-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) - @result{} (1 3) -@end group -@end example -@end defun - -@anchor{-find-index} -@defun -find-index (pred list) -Take a predicate @var{pred} and a @var{list} and return the index of the -first element in the list satisfying the predicate, or nil if -there is no such element. - -See also @code{-first} (@pxref{-first}). - -@example -@group -(-find-index 'even? '(2 4 1 6 3 3 5 8)) - @result{} 0 -@end group -@group -(--find-index (< 5 it) '(2 4 1 6 3 3 5 8)) - @result{} 3 -@end group -@group -(-find-index (-partial 'string-lessp "baz") '("bar" "foo" "baz")) - @result{} 1 -@end group -@end example -@end defun - -@anchor{-find-last-index} -@defun -find-last-index (pred list) -Take a predicate @var{pred} and a @var{list} and return the index of the -last element in the list satisfying the predicate, or nil if -there is no such element. - -See also @code{-last} (@pxref{-last}). - -@example -@group -(-find-last-index 'even? '(2 4 1 6 3 3 5 8)) - @result{} 7 -@end group -@group -(--find-last-index (< 5 it) '(2 7 1 6 3 8 5 2)) - @result{} 5 -@end group -@group -(-find-last-index (-partial 'string-lessp "baz") '("q" "foo" "baz")) - @result{} 1 -@end group -@end example -@end defun - -@anchor{-find-indices} -@defun -find-indices (pred list) -Return the indices of all elements in @var{list} satisfying the -predicate @var{pred}, in ascending order. - -@example -@group -(-find-indices 'even? '(2 4 1 6 3 3 5 8)) - @result{} (0 1 3 7) -@end group -@group -(--find-indices (< 5 it) '(2 4 1 6 3 3 5 8)) - @result{} (3 7) -@end group -@group -(-find-indices (-partial 'string-lessp "baz") '("bar" "foo" "baz")) - @result{} (1) -@end group -@end example -@end defun - -@anchor{-grade-up} -@defun -grade-up (comparator list) -Grade elements of @var{list} using @var{comparator} relation. -This yields a permutation vector such that applying this -permutation to @var{list} sorts it in ascending order. - -@example -@group -(-grade-up #'< '(3 1 4 2 1 3 3)) - @result{} (1 4 3 0 5 6 2) -@end group -@group -(let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-up #'< l) l)) - @result{} (1 1 2 3 3 3 4) -@end group -@end example -@end defun - -@anchor{-grade-down} -@defun -grade-down (comparator list) -Grade elements of @var{list} using @var{comparator} relation. -This yields a permutation vector such that applying this -permutation to @var{list} sorts it in descending order. - -@example -@group -(-grade-down #'< '(3 1 4 2 1 3 3)) - @result{} (2 0 5 6 3 1 4) -@end group -@group -(let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-down #'< l) l)) - @result{} (4 3 3 3 2 1 1) -@end group -@end example -@end defun - -@node Set operations -@section Set operations - -Operations pretending lists are sets. - -@anchor{-union} -@defun -union (list list2) -Return a new list containing the elements of @var{list} and elements of @var{list2} that are not in @var{list}. -The test for equality is done with @code{equal}, -or with @code{-compare-fn} if that's non-nil. - -@example -@group -(-union '(1 2 3) '(3 4 5)) - @result{} (1 2 3 4 5) -@end group -@group -(-union '(1 2 3 4) ()) - @result{} (1 2 3 4) -@end group -@group -(-union '(1 1 2 2) '(3 2 1)) - @result{} (1 1 2 2 3) -@end group -@end example -@end defun - -@anchor{-difference} -@defun -difference (list list2) -Return a new list with only the members of @var{list} that are not in @var{list2}. -The test for equality is done with @code{equal}, -or with @code{-compare-fn} if that's non-nil. - -@example -@group -(-difference () ()) - @result{} () -@end group -@group -(-difference '(1 2 3) '(4 5 6)) - @result{} (1 2 3) -@end group -@group -(-difference '(1 2 3 4) '(3 4 5 6)) - @result{} (1 2) -@end group -@end example -@end defun - -@anchor{-intersection} -@defun -intersection (list list2) -Return a new list containing only the elements that are members of both @var{list} and @var{list2}. -The test for equality is done with @code{equal}, -or with @code{-compare-fn} if that's non-nil. - -@example -@group -(-intersection () ()) - @result{} () -@end group -@group -(-intersection '(1 2 3) '(4 5 6)) - @result{} () -@end group -@group -(-intersection '(1 2 3 4) '(3 4 5 6)) - @result{} (3 4) -@end group -@end example -@end defun - -@anchor{-powerset} -@defun -powerset (list) -Return the power set of @var{list}. - -@example -@group -(-powerset ()) - @result{} (nil) -@end group -@group -(-powerset '(x y z)) - @result{} ((x y z) (x y) (x z) (x) (y z) (y) (z) nil) -@end group -@end example -@end defun - -@anchor{-permutations} -@defun -permutations (list) -Return the permutations of @var{list}. - -@example -@group -(-permutations ()) - @result{} (nil) -@end group -@group -(-permutations '(1 2)) - @result{} ((1 2) (2 1)) -@end group -@group -(-permutations '(a b c)) - @result{} ((a b c) (a c b) (b a c) (b c a) (c a b) (c b a)) -@end group -@end example -@end defun - -@anchor{-distinct} -@defun -distinct (list) -Return a new list with all duplicates removed. -The test for equality is done with @code{equal}, -or with @code{-compare-fn} if that's non-nil. - -Alias: @code{-uniq} - -@example -@group -(-distinct ()) - @result{} () -@end group -@group -(-distinct '(1 2 2 4)) - @result{} (1 2 4) -@end group -@group -(-distinct '(t t t)) - @result{} (t) -@end group -@end example -@end defun - -@node Other list operations -@section Other list operations - -Other list functions not fit to be classified elsewhere. - -@anchor{-rotate} -@defun -rotate (n list) -Rotate @var{list} @var{n} places to the right (left if @var{n} is negative). -The time complexity is @var{o}(n). - -@example -@group -(-rotate 3 '(1 2 3 4 5 6 7)) - @result{} (5 6 7 1 2 3 4) -@end group -@group -(-rotate -3 '(1 2 3 4 5 6 7)) - @result{} (4 5 6 7 1 2 3) -@end group -@group -(-rotate 16 '(1 2 3 4 5 6 7)) - @result{} (6 7 1 2 3 4 5) -@end group -@end example -@end defun - -@anchor{-repeat} -@defun -repeat (n x) -Return a new list of length @var{n} with each element being @var{x}. -Return nil if @var{n} is less than 1. - -@example -@group -(-repeat 3 :a) - @result{} (:a :a :a) -@end group -@group -(-repeat 1 :a) - @result{} (:a) -@end group -@group -(-repeat 0 :a) - @result{} nil -@end group -@end example -@end defun - -@anchor{-cons*} -@defun -cons* (&rest args) -Make a new list from the elements of @var{args}. -The last 2 elements of @var{args} are used as the final cons of the -result, so if the final element of @var{args} is not a list, the result -is a dotted list. With no @var{args}, return nil. - -@example -@group -(-cons* 1 2) - @result{} (1 . 2) -@end group -@group -(-cons* 1 2 3) - @result{} (1 2 . 3) -@end group -@group -(-cons* 1) - @result{} 1 -@end group -@end example -@end defun - -@anchor{-snoc} -@defun -snoc (list elem &rest elements) -Append @var{elem} to the end of the list. - -This is like @code{cons}, but operates on the end of list. - -If @var{elements} is non nil, append these to the list as well. - -@example -@group -(-snoc '(1 2 3) 4) - @result{} (1 2 3 4) -@end group -@group -(-snoc '(1 2 3) 4 5 6) - @result{} (1 2 3 4 5 6) -@end group -@group -(-snoc '(1 2 3) '(4 5 6)) - @result{} (1 2 3 (4 5 6)) -@end group -@end example -@end defun - -@anchor{-interpose} -@defun -interpose (sep list) -Return a new list of all elements in @var{list} separated by @var{sep}. - -@example -@group -(-interpose "-" ()) - @result{} () -@end group -@group -(-interpose "-" '("a")) - @result{} ("a") -@end group -@group -(-interpose "-" '("a" "b" "c")) - @result{} ("a" "-" "b" "-" "c") -@end group -@end example -@end defun - -@anchor{-interleave} -@defun -interleave (&rest lists) -Return a new list of the first item in each list, then the second etc. - -@example -@group -(-interleave '(1 2) '("a" "b")) - @result{} (1 "a" 2 "b") -@end group -@group -(-interleave '(1 2) '("a" "b") '("A" "B")) - @result{} (1 "a" "A" 2 "b" "B") -@end group -@group -(-interleave '(1 2 3) '("a" "b")) - @result{} (1 "a" 2 "b") -@end group -@end example -@end defun - -@anchor{-iota} -@defun -iota (count &optional start step) -Return a list containing @var{count} numbers. -Starts from @var{start} and adds @var{step} each time. The default @var{start} is -zero, the default @var{step} is 1. -This function takes its name from the corresponding primitive in -the @var{apl} language. - -@example -@group -(-iota 6) - @result{} (0 1 2 3 4 5) -@end group -@group -(-iota 4 2.5 -2) - @result{} (2.5 0.5 -1.5 -3.5) -@end group -@group -(-iota -1) - @error{} Wrong type argument: natnump, -1 -@end group -@end example -@end defun - -@anchor{-zip-with} -@defun -zip-with (fn list1 list2) -Zip the two lists @var{list1} and @var{list2} using a function @var{fn}. This -function is applied pairwise taking as first argument element of -@var{list1} and as second argument element of @var{list2} at corresponding -position. - -The anaphoric form @code{--zip-with} binds the elements from @var{list1} as symbol @code{it}, -and the elements from @var{list2} as symbol @code{other}. - -@example -@group -(-zip-with '+ '(1 2 3) '(4 5 6)) - @result{} (5 7 9) -@end group -@group -(-zip-with 'cons '(1 2 3) '(4 5 6)) - @result{} ((1 . 4) (2 . 5) (3 . 6)) -@end group -@group -(--zip-with (concat it " and " other) '("Batman" "Jekyll") '("Robin" "Hyde")) - @result{} ("Batman and Robin" "Jekyll and Hyde") -@end group -@end example -@end defun - -@anchor{-zip} -@defun -zip (&rest lists) -Zip @var{lists} together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. - -If two lists are provided as arguments, return the groupings as a list -of cons cells. Otherwise, return the groupings as a list of lists. - -Use @code{-zip-lists} (@pxref{-zip-lists}) if you need the return value to always be a list -of lists. - -Alias: @code{-zip-pair} - -See also: @code{-zip-lists} (@pxref{-zip-lists}) - -@example -@group -(-zip '(1 2 3) '(4 5 6)) - @result{} ((1 . 4) (2 . 5) (3 . 6)) -@end group -@group -(-zip '(1 2 3) '(4 5 6 7)) - @result{} ((1 . 4) (2 . 5) (3 . 6)) -@end group -@group -(-zip '(1 2) '(3 4 5) '(6)) - @result{} ((1 3 6)) -@end group -@end example -@end defun - -@anchor{-zip-lists} -@defun -zip-lists (&rest lists) -Zip @var{lists} together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. - -The return value is always list of lists, which is a difference -from @code{-zip-pair} which returns a cons-cell in case two input -lists are provided. - -See also: @code{-zip} (@pxref{-zip}) - -@example -@group -(-zip-lists '(1 2 3) '(4 5 6)) - @result{} ((1 4) (2 5) (3 6)) -@end group -@group -(-zip-lists '(1 2 3) '(4 5 6 7)) - @result{} ((1 4) (2 5) (3 6)) -@end group -@group -(-zip-lists '(1 2) '(3 4 5) '(6)) - @result{} ((1 3 6)) -@end group -@end example -@end defun - -@anchor{-zip-fill} -@defun -zip-fill (fill-value &rest lists) -Zip @var{lists}, with @var{fill-value} padded onto the shorter lists. The -lengths of the returned groupings are equal to the length of the -longest input list. - -@example -@group -(-zip-fill 0 '(1 2 3 4 5) '(6 7 8 9)) - @result{} ((1 . 6) (2 . 7) (3 . 8) (4 . 9) (5 . 0)) -@end group -@end example -@end defun - -@anchor{-unzip} -@defun -unzip (lists) -Unzip @var{lists}. - -This works just like @code{-zip} (@pxref{-zip}) but takes a list of lists instead of -a variable number of arguments, such that - - (-unzip (-zip @var{l1} @var{l2} @var{l3} @dots{})) - -is identity (given that the lists are the same length). - -Note in particular that calling this on a list of two lists will -return a list of cons-cells such that the above identity works. - -See also: @code{-zip} (@pxref{-zip}) - -@example -@group -(-unzip (-zip '(1 2 3) '(a b c) '("e" "f" "g"))) - @result{} ((1 2 3) (a b c) ("e" "f" "g")) -@end group -@group -(-unzip '((1 2) (3 4) (5 6) (7 8) (9 10))) - @result{} ((1 3 5 7 9) (2 4 6 8 10)) -@end group -@group -(-unzip '((1 2) (3 4))) - @result{} ((1 . 3) (2 . 4)) -@end group -@end example -@end defun - -@anchor{-cycle} -@defun -cycle (list) -Return an infinite circular copy of @var{list}. -The returned list cycles through the elements of @var{list} and repeats -from the beginning. - -@example -@group -(-take 5 (-cycle '(1 2 3))) - @result{} (1 2 3 1 2) -@end group -@group -(-take 7 (-cycle '(1 "and" 3))) - @result{} (1 "and" 3 1 "and" 3 1) -@end group -@group -(-zip (-cycle '(1 2 3)) '(1 2)) - @result{} ((1 . 1) (2 . 2)) -@end group -@end example -@end defun - -@anchor{-pad} -@defun -pad (fill-value &rest lists) -Appends @var{fill-value} to the end of each list in @var{lists} such that they -will all have the same length. - -@example -@group -(-pad 0 ()) - @result{} (nil) -@end group -@group -(-pad 0 '(1)) - @result{} ((1)) -@end group -@group -(-pad 0 '(1 2 3) '(4 5)) - @result{} ((1 2 3) (4 5 0)) -@end group -@end example -@end defun - -@anchor{-table} -@defun -table (fn &rest lists) -Compute outer product of @var{lists} using function @var{fn}. - -The function @var{fn} should have the same arity as the number of -supplied lists. - -The outer product is computed by applying fn to all possible -combinations created by taking one element from each list in -order. The dimension of the result is (length lists). - -See also: @code{-table-flat} (@pxref{-table-flat}) - -@example -@group -(-table '* '(1 2 3) '(1 2 3)) - @result{} ((1 2 3) (2 4 6) (3 6 9)) -@end group -@group -(-table (lambda (a b) (-sum (-zip-with '* a b))) '((1 2) (3 4)) '((1 3) (2 4))) - @result{} ((7 15) (10 22)) -@end group -@group -(apply '-table 'list (-repeat 3 '(1 2))) - @result{} ((((1 1 1) (2 1 1)) ((1 2 1) (2 2 1))) (((1 1 2) (2 1 2)) ((1 2 2) (2 2 2)))) -@end group -@end example -@end defun - -@anchor{-table-flat} -@defun -table-flat (fn &rest lists) -Compute flat outer product of @var{lists} using function @var{fn}. - -The function @var{fn} should have the same arity as the number of -supplied lists. - -The outer product is computed by applying fn to all possible -combinations created by taking one element from each list in -order. The results are flattened, ignoring the tensor structure -of the result. This is equivalent to calling: - - (-flatten-n (1- (length lists)) (apply '-table fn lists)) - -but the implementation here is much more efficient. - -See also: @code{-flatten-n} (@pxref{-flatten-n}), @code{-table} (@pxref{-table}) - -@example -@group -(-table-flat 'list '(1 2 3) '(a b c)) - @result{} ((1 a) (2 a) (3 a) (1 b) (2 b) (3 b) (1 c) (2 c) (3 c)) -@end group -@group -(-table-flat '* '(1 2 3) '(1 2 3)) - @result{} (1 2 3 2 4 6 3 6 9) -@end group -@group -(apply '-table-flat 'list (-repeat 3 '(1 2))) - @result{} ((1 1 1) (2 1 1) (1 2 1) (2 2 1) (1 1 2) (2 1 2) (1 2 2) (2 2 2)) -@end group -@end example -@end defun - -@anchor{-first} -@defun -first (pred list) -Return the first item in @var{list} for which @var{pred} returns non-nil. -Return nil if no such element is found. -To get the first item in the list no questions asked, use @code{car}. - -Alias: @code{-find}. - -This function's anaphoric counterpart is @code{--first}. - -@example -@group -(-first #'natnump '(-1 0 1)) - @result{} 0 -@end group -@group -(-first #'null '(1 2 3)) - @result{} nil -@end group -@group -(--first (> it 2) '(1 2 3)) - @result{} 3 -@end group -@end example -@end defun - -@anchor{-last} -@defun -last (pred list) -Return the last x in @var{list} where (@var{pred} x) is non-nil, else nil. - -@example -@group -(-last 'even? '(1 2 3 4 5 6 3 3 3)) - @result{} 6 -@end group -@group -(-last 'even? '(1 3 7 5 9)) - @result{} nil -@end group -@group -(--last (> (length it) 3) '("a" "looong" "word" "and" "short" "one")) - @result{} "short" -@end group -@end example -@end defun - -@anchor{-first-item} -@defun -first-item (list) -Return the first item of @var{list}, or nil on an empty list. - -See also: @code{-second-item} (@pxref{-second-item}), @code{-last-item} (@pxref{-last-item}). - -@example -@group -(-first-item '(1 2 3)) - @result{} 1 -@end group -@group -(-first-item nil) - @result{} nil -@end group -@group -(let ((list (list 1 2 3))) (setf (-first-item list) 5) list) - @result{} (5 2 3) -@end group -@end example -@end defun - -@anchor{-second-item} -@defun -second-item (list) -Return the second item of @var{list}, or nil if @var{list} is too short. - -See also: @code{-third-item} (@pxref{-third-item}). - -@example -@group -(-second-item '(1 2 3)) - @result{} 2 -@end group -@group -(-second-item nil) - @result{} nil -@end group -@end example -@end defun - -@anchor{-third-item} -@defun -third-item (list) -Return the third item of @var{list}, or nil if @var{list} is too short. - -See also: @code{-fourth-item} (@pxref{-fourth-item}). - -@example -@group -(-third-item '(1 2 3)) - @result{} 3 -@end group -@group -(-third-item nil) - @result{} nil -@end group -@end example -@end defun - -@anchor{-fourth-item} -@defun -fourth-item (list) -Return the fourth item of @var{list}, or nil if @var{list} is too short. - -See also: @code{-fifth-item} (@pxref{-fifth-item}). - -@example -@group -(-fourth-item '(1 2 3 4)) - @result{} 4 -@end group -@group -(-fourth-item nil) - @result{} nil -@end group -@end example -@end defun - -@anchor{-fifth-item} -@defun -fifth-item (list) -Return the fifth item of @var{list}, or nil if @var{list} is too short. - -See also: @code{-last-item} (@pxref{-last-item}). - -@example -@group -(-fifth-item '(1 2 3 4 5)) - @result{} 5 -@end group -@group -(-fifth-item nil) - @result{} nil -@end group -@end example -@end defun - -@anchor{-last-item} -@defun -last-item (list) -Return the last item of @var{list}, or nil on an empty list. - -@example -@group -(-last-item '(1 2 3)) - @result{} 3 -@end group -@group -(-last-item nil) - @result{} nil -@end group -@group -(let ((list (list 1 2 3))) (setf (-last-item list) 5) list) - @result{} (1 2 5) -@end group -@end example -@end defun - -@anchor{-butlast} -@defun -butlast (list) -Return a list of all items in list except for the last. - -@example -@group -(-butlast '(1 2 3)) - @result{} (1 2) -@end group -@group -(-butlast '(1 2)) - @result{} (1) -@end group -@group -(-butlast '(1)) - @result{} nil -@end group -@end example -@end defun - -@anchor{-sort} -@defun -sort (comparator list) -Sort @var{list}, stably, comparing elements using @var{comparator}. -Return the sorted list. @var{list} is @var{not} modified by side effects. -@var{comparator} is called with two elements of @var{list}, and should return non-nil -if the first element should sort before the second. - -@example -@group -(-sort '< '(3 1 2)) - @result{} (1 2 3) -@end group -@group -(-sort '> '(3 1 2)) - @result{} (3 2 1) -@end group -@group -(--sort (< it other) '(3 1 2)) - @result{} (1 2 3) -@end group -@end example -@end defun - -@anchor{-list} -@defun -list (arg) -Ensure @var{arg} is a list. -If @var{arg} is already a list, return it as is (not a copy). -Otherwise, return a new list with @var{arg} as its only element. - -Another supported calling convention is (-list &rest @var{args}). -In this case, if @var{arg} is not a list, a new list with all of -@var{args} as elements is returned. This use is supported for -backward compatibility and is otherwise deprecated. - -@example -@group -(-list 1) - @result{} (1) -@end group -@group -(-list ()) - @result{} () -@end group -@group -(-list '(1 2 3)) - @result{} (1 2 3) -@end group -@end example -@end defun - -@anchor{-fix} -@defun -fix (fn list) -Compute the (least) fixpoint of @var{fn} with initial input @var{list}. - -@var{fn} is called at least once, results are compared with @code{equal}. - -@example -@group -(-fix (lambda (l) (-non-nil (--mapcat (-split-at (/ (length it) 2) it) l))) '((1 2 3))) - @result{} ((1) (2) (3)) -@end group -@group -(let ((l '((starwars scifi) (jedi starwars warrior)))) (--fix (-uniq (--mapcat (cons it (cdr (assq it l))) it)) '(jedi book))) - @result{} (jedi starwars warrior scifi book) -@end group -@end example -@end defun - -@node Tree operations -@section Tree operations - -Functions pretending lists are trees. - -@anchor{-tree-seq} -@defun -tree-seq (branch children tree) -Return a sequence of the nodes in @var{tree}, in depth-first search order. - -@var{branch} is a predicate of one argument that returns non-nil if the -passed argument is a branch, that is, a node that can have children. - -@var{children} is a function of one argument that returns the children -of the passed branch node. - -Non-branch nodes are simply copied. - -@example -@group -(-tree-seq 'listp 'identity '(1 (2 3) 4 (5 (6 7)))) - @result{} ((1 (2 3) 4 (5 (6 7))) 1 (2 3) 2 3 4 (5 (6 7)) 5 (6 7) 6 7) -@end group -@group -(-tree-seq 'listp 'reverse '(1 (2 3) 4 (5 (6 7)))) - @result{} ((1 (2 3) 4 (5 (6 7))) (5 (6 7)) (6 7) 7 6 5 4 (2 3) 3 2 1) -@end group -@group -(--tree-seq (vectorp it) (append it nil) [1 [2 3] 4 [5 [6 7]]]) - @result{} ([1 [2 3] 4 [5 [6 7]]] 1 [2 3] 2 3 4 [5 [6 7]] 5 [6 7] 6 7) -@end group -@end example -@end defun - -@anchor{-tree-map} -@defun -tree-map (fn tree) -Apply @var{fn} to each element of @var{tree} while preserving the tree structure. - -@example -@group -(-tree-map '1+ '(1 (2 3) (4 (5 6) 7))) - @result{} (2 (3 4) (5 (6 7) 8)) -@end group -@group -(-tree-map '(lambda (x) (cons x (expt 2 x))) '(1 (2 3) 4)) - @result{} ((1 . 2) ((2 . 4) (3 . 8)) (4 . 16)) -@end group -@group -(--tree-map (length it) '("" ("

" "text" "

") "")) - @result{} (6 (3 4 4) 7) -@end group -@end example -@end defun - -@anchor{-tree-map-nodes} -@defun -tree-map-nodes (pred fun tree) -Call @var{fun} on each node of @var{tree} that satisfies @var{pred}. - -If @var{pred} returns nil, continue descending down this node. If @var{pred} -returns non-nil, apply @var{fun} to this node and do not descend -further. - -@example -@group -(-tree-map-nodes 'vectorp (lambda (x) (-sum (append x nil))) '(1 [2 3] 4 (5 [6 7] 8))) - @result{} (1 5 4 (5 13 8)) -@end group -@group -(-tree-map-nodes 'keywordp (lambda (x) (symbol-name x)) '(1 :foo 4 ((5 6 :bar) :baz 8))) - @result{} (1 ":foo" 4 ((5 6 ":bar") ":baz" 8)) -@end group -@group -(--tree-map-nodes (eq (car-safe it) 'add-mode) (-concat it (list :mode 'emacs-lisp-mode)) '(with-mode emacs-lisp-mode (foo bar) (add-mode a b) (baz (add-mode c d)))) - @result{} (with-mode emacs-lisp-mode (foo bar) (add-mode a b :mode emacs-lisp-mode) (baz (add-mode c d :mode emacs-lisp-mode))) -@end group -@end example -@end defun - -@anchor{-tree-reduce} -@defun -tree-reduce (fn tree) -Use @var{fn} to reduce elements of list @var{tree}. -If elements of @var{tree} are lists themselves, apply the reduction recursively. - -@var{fn} is first applied to first element of the list and second -element, then on this result and third element from the list etc. - -See @code{-reduce-r} (@pxref{-reduce-r}) for how exactly are lists of zero or one element handled. - -@example -@group -(-tree-reduce '+ '(1 (2 3) (4 5))) - @result{} 15 -@end group -@group -(-tree-reduce 'concat '("strings" (" on" " various") ((" levels")))) - @result{} "strings on various levels" -@end group -@group -(--tree-reduce (cond ((stringp it) (concat it " " acc)) (t (let ((sn (symbol-name it))) (concat "<" sn ">" acc "")))) '(body (p "some words") (div "more" (b "bold") "words"))) - @result{} "

some words

more bold words
" -@end group -@end example -@end defun - -@anchor{-tree-reduce-from} -@defun -tree-reduce-from (fn init-value tree) -Use @var{fn} to reduce elements of list @var{tree}. -If elements of @var{tree} are lists themselves, apply the reduction recursively. - -@var{fn} is first applied to @var{init-value} and first element of the list, -then on this result and second element from the list etc. - -The initial value is ignored on cons pairs as they always contain -two elements. - -@example -@group -(-tree-reduce-from '+ 1 '(1 (1 1) ((1)))) - @result{} 8 -@end group -@group -(--tree-reduce-from (-concat acc (list it)) nil '(1 (2 3 (4 5)) (6 7))) - @result{} ((7 6) ((5 4) 3 2) 1) -@end group -@end example -@end defun - -@anchor{-tree-mapreduce} -@defun -tree-mapreduce (fn folder tree) -Apply @var{fn} to each element of @var{tree}, and make a list of the results. -If elements of @var{tree} are lists themselves, apply @var{fn} recursively to -elements of these nested lists. - -Then reduce the resulting lists using @var{folder} and initial value -@var{init-value}. See @code{-reduce-r-from} (@pxref{-reduce-r-from}). - -This is the same as calling @code{-tree-reduce} (@pxref{-tree-reduce}) after @code{-tree-map} (@pxref{-tree-map}) -but is twice as fast as it only traverse the structure once. - -@example -@group -(-tree-mapreduce 'list 'append '(1 (2 (3 4) (5 6)) (7 (8 9)))) - @result{} (1 2 3 4 5 6 7 8 9) -@end group -@group -(--tree-mapreduce 1 (+ it acc) '(1 (2 (4 9) (2 1)) (7 (4 3)))) - @result{} 9 -@end group -@group -(--tree-mapreduce 0 (max acc (1+ it)) '(1 (2 (4 9) (2 1)) (7 (4 3)))) - @result{} 3 -@end group -@end example -@end defun - -@anchor{-tree-mapreduce-from} -@defun -tree-mapreduce-from (fn folder init-value tree) -Apply @var{fn} to each element of @var{tree}, and make a list of the results. -If elements of @var{tree} are lists themselves, apply @var{fn} recursively to -elements of these nested lists. - -Then reduce the resulting lists using @var{folder} and initial value -@var{init-value}. See @code{-reduce-r-from} (@pxref{-reduce-r-from}). - -This is the same as calling @code{-tree-reduce-from} (@pxref{-tree-reduce-from}) after @code{-tree-map} (@pxref{-tree-map}) -but is twice as fast as it only traverse the structure once. - -@example -@group -(-tree-mapreduce-from 'identity '* 1 '(1 (2 (3 4) (5 6)) (7 (8 9)))) - @result{} 362880 -@end group -@group -(--tree-mapreduce-from (+ it it) (cons it acc) nil '(1 (2 (4 9) (2 1)) (7 (4 3)))) - @result{} (2 (4 (8 18) (4 2)) (14 (8 6))) -@end group -@group -(concat "@{" (--tree-mapreduce-from (cond ((-cons-pair? it) (concat (symbol-name (car it)) " -> " (symbol-name (cdr it)))) (t (concat (symbol-name it) " : @{"))) (concat it (unless (or (equal acc "@}") (equal (substring it (1- (length it))) "@{")) ", ") acc) "@}" '((elisp-mode (foo (bar . booze)) (baz . qux)) (c-mode (foo . bla) (bum . bam))))) - @result{} "@{elisp-mode : @{foo : @{bar -> booze@}, baz -> qux@}, c-mode : @{foo -> bla, bum -> bam@}@}" -@end group -@end example -@end defun - -@anchor{-clone} -@defun -clone (list) -Create a deep copy of @var{list}. -The new list has the same elements and structure but all cons are -replaced with new ones. This is useful when you need to clone a -structure such as plist or alist. - -@example -@group -(let* ((a '(1 2 3)) (b (-clone a))) (nreverse a) b) - @result{} (1 2 3) -@end group -@end example -@end defun - -@node Threading macros -@section Threading macros - -Macros that conditionally combine sequential forms for brevity -or readability. - -@anchor{->} -@defmac -> (x &optional form &rest more) -Thread the expr through the forms. Insert @var{x} as the second item -in the first form, making a list of it if it is not a list -already. If there are more forms, insert the first form as the -second item in second form, etc. - -@example -@group -(-> '(2 3 5)) - @result{} (2 3 5) -@end group -@group -(-> '(2 3 5) (append '(8 13))) - @result{} (2 3 5 8 13) -@end group -@group -(-> '(2 3 5) (append '(8 13)) (-slice 1 -1)) - @result{} (3 5 8) -@end group -@end example -@end defmac - -@anchor{->>} -@defmac ->> (x &optional form &rest more) -Thread the expr through the forms. Insert @var{x} as the last item -in the first form, making a list of it if it is not a list -already. If there are more forms, insert the first form as the -last item in second form, etc. - -@example -@group -(->> '(1 2 3) (-map 'square)) - @result{} (1 4 9) -@end group -@group -(->> '(1 2 3) (-map 'square) (-remove 'even?)) - @result{} (1 9) -@end group -@group -(->> '(1 2 3) (-map 'square) (-reduce '+)) - @result{} 14 -@end group -@end example -@end defmac - -@anchor{-->} -@defmac --> (x &rest forms) -Starting with the value of @var{x}, thread each expression through @var{forms}. - -Insert @var{x} at the position signified by the symbol @code{it} in the first -form. If there are more forms, insert the first form at the position -signified by @code{it} in in second form, etc. - -@example -@group -(--> "def" (concat "abc" it "ghi")) - @result{} "abcdefghi" -@end group -@group -(--> "def" (concat "abc" it "ghi") (upcase it)) - @result{} "ABCDEFGHI" -@end group -@group -(--> "def" (concat "abc" it "ghi") upcase) - @result{} "ABCDEFGHI" -@end group -@end example -@end defmac - -@anchor{-as->} -@defmac -as-> (value variable &rest forms) -Starting with @var{value}, thread @var{variable} through @var{forms}. - -In the first form, bind @var{variable} to @var{value}. In the second form, bind -@var{variable} to the result of the first form, and so forth. - -@example -@group -(-as-> 3 my-var (1+ my-var) (list my-var) (mapcar (lambda (ele) (* 2 ele)) my-var)) - @result{} (8) -@end group -@group -(-as-> 3 my-var 1+) - @result{} 4 -@end group -@group -(-as-> 3 my-var) - @result{} 3 -@end group -@end example -@end defmac - -@anchor{-some->} -@defmac -some-> (x &optional form &rest more) -When expr is non-nil, thread it through the first form (via @code{->} (@pxref{->})), -and when that result is non-nil, through the next form, etc. - -@example -@group -(-some-> '(2 3 5)) - @result{} (2 3 5) -@end group -@group -(-some-> 5 square) - @result{} 25 -@end group -@group -(-some-> 5 even? square) - @result{} nil -@end group -@end example -@end defmac - -@anchor{-some->>} -@defmac -some->> (x &optional form &rest more) -When expr is non-nil, thread it through the first form (via @code{->>} (@pxref{->>})), -and when that result is non-nil, through the next form, etc. - -@example -@group -(-some->> '(1 2 3) (-map 'square)) - @result{} (1 4 9) -@end group -@group -(-some->> '(1 3 5) (-last 'even?) (+ 100)) - @result{} nil -@end group -@group -(-some->> '(2 4 6) (-last 'even?) (+ 100)) - @result{} 106 -@end group -@end example -@end defmac - -@anchor{-some-->} -@defmac -some--> (expr &rest forms) -Thread @var{expr} through @var{forms} via @code{-->} (@pxref{-->}), while the result is non-nil. -When @var{expr} evaluates to non-nil, thread the result through the -first of @var{forms}, and when that result is non-nil, thread it -through the next form, etc. - -@example -@group -(-some--> "def" (concat "abc" it "ghi")) - @result{} "abcdefghi" -@end group -@group -(-some--> nil (concat "abc" it "ghi")) - @result{} nil -@end group -@group -(-some--> '(0 1) (-remove #'natnump it) (append it it) (-map #'1+ it)) - @result{} () -@end group -@end example -@end defmac - -@anchor{-doto} -@defmac -doto (init &rest forms) -Evaluate @var{init} and pass it as argument to @var{forms} with @code{->} (@pxref{->}). -The @var{result} of evaluating @var{init} is threaded through each of @var{forms} -individually using @code{->} (@pxref{->}), which see. The return value is @var{result}, -which @var{forms} may have modified by side effect. - -@example -@group -(-doto (list 1 2 3) pop pop) - @result{} (3) -@end group -@group -(-doto (cons 1 2) (setcar 3) (setcdr 4)) - @result{} (3 . 4) -@end group -@group -(gethash 'k (--doto (make-hash-table) (puthash 'k 'v it))) - @result{} v -@end group -@end example -@end defmac - -@node Binding -@section Binding - -Macros that combine @code{let} and @code{let*} with destructuring and flow control. - -@anchor{-when-let} -@defmac -when-let ((var val) &rest body) -If @var{val} evaluates to non-nil, bind it to @var{var} and execute body. - -Note: binding is done according to @code{-let} (@pxref{-let}). - -@example -@group -(-when-let (match-index (string-match "d" "abcd")) (+ match-index 2)) - @result{} 5 -@end group -@group -(-when-let ((&plist :foo foo) (list :foo "foo")) foo) - @result{} "foo" -@end group -@group -(-when-let ((&plist :foo foo) (list :bar "bar")) foo) - @result{} nil -@end group -@end example -@end defmac - -@anchor{-when-let*} -@defmac -when-let* (vars-vals &rest body) -If all @var{vals} evaluate to true, bind them to their corresponding -@var{vars} and execute body. @var{vars-vals} should be a list of (@var{var} @var{val}) -pairs. - -Note: binding is done according to @code{-let*} (@pxref{-let*}). @var{vals} are evaluated -sequentially, and evaluation stops after the first nil @var{val} is -encountered. - -@example -@group -(-when-let* ((x 5) (y 3) (z (+ y 4))) (+ x y z)) - @result{} 15 -@end group -@group -(-when-let* ((x 5) (y nil) (z 7)) (+ x y z)) - @result{} nil -@end group -@end example -@end defmac - -@anchor{-if-let} -@defmac -if-let ((var val) then &rest else) -If @var{val} evaluates to non-nil, bind it to @var{var} and do @var{then}, -otherwise do @var{else}. - -Note: binding is done according to @code{-let} (@pxref{-let}). - -@example -@group -(-if-let (match-index (string-match "d" "abc")) (+ match-index 3) 7) - @result{} 7 -@end group -@group -(--if-let (even? 4) it nil) - @result{} t -@end group -@end example -@end defmac - -@anchor{-if-let*} -@defmac -if-let* (vars-vals then &rest else) -If all @var{vals} evaluate to true, bind them to their corresponding -@var{vars} and do @var{then}, otherwise do @var{else}. @var{vars-vals} should be a list -of (@var{var} @var{val}) pairs. - -Note: binding is done according to @code{-let*} (@pxref{-let*}). @var{vals} are evaluated -sequentially, and evaluation stops after the first nil @var{val} is -encountered. - -@example -@group -(-if-let* ((x 5) (y 3) (z 7)) (+ x y z) "foo") - @result{} 15 -@end group -@group -(-if-let* ((x 5) (y nil) (z 7)) (+ x y z) "foo") - @result{} "foo" -@end group -@group -(-if-let* (((_ _ x) '(nil nil 7))) x) - @result{} 7 -@end group -@end example -@end defmac - -@anchor{-let} -@defmac -let (varlist &rest body) -Bind variables according to @var{varlist} then eval @var{body}. - -@var{varlist} is a list of lists of the form (@var{pattern} @var{source}). Each -@var{pattern} is matched against the @var{source} "structurally". @var{source} -is only evaluated once for each @var{pattern}. Each @var{pattern} is matched -recursively, and can therefore contain sub-patterns which are -matched against corresponding sub-expressions of @var{source}. - -All the SOURCEs are evalled before any symbols are -bound (i.e. "in parallel"). - -If @var{varlist} only contains one (@var{pattern} @var{source}) element, you can -optionally specify it using a vector and discarding the -outer-most parens. Thus - - (-let ((@var{pattern} @var{source})) @dots{}) - -becomes - - (-let [@var{pattern} @var{source}] @dots{}). - -@code{-let} (@pxref{-let}) uses a convention of not binding places (symbols) starting -with _ whenever it's possible. You can use this to skip over -entries you don't care about. However, this is not *always* -possible (as a result of implementation) and these symbols might -get bound to undefined values. - -Following is the overview of supported patterns. Remember that -patterns can be matched recursively, so every a, b, aK in the -following can be a matching construct and not necessarily a -symbol/variable. - -Symbol: - - a - bind the @var{source} to @var{a}. This is just like regular @code{let}. - -Conses and lists: - - (a) - bind @code{car} of cons/list to @var{a} - - (a . b) - bind car of cons to @var{a} and @code{cdr} to @var{b} - - (a b) - bind car of list to @var{a} and @code{cadr} to @var{b} - - (a1 a2 a3 @dots{}) - bind 0th car of list to @var{a1}, 1st to @var{a2}, 2nd to @var{a3}@enddots{} - - (a1 a2 a3 @dots{} aN . rest) - as above, but bind the Nth cdr to @var{rest}. - -Vectors: - - [a] - bind 0th element of a non-list sequence to @var{a} (works with - vectors, strings, bit arrays@dots{}) - - [a1 a2 a3 @dots{}] - bind 0th element of non-list sequence to @var{a0}, 1st to - @var{a1}, 2nd to @var{a2}, @enddots{} - If the @var{pattern} is shorter than @var{source}, the values at - places not in @var{pattern} are ignored. - If the @var{pattern} is longer than @var{source}, an @code{error} is - thrown. - - [a1 a2 a3 @dots{} &rest rest] - as above, but bind the rest of - the sequence to @var{rest}. This is - conceptually the same as improper list - matching (a1 a2 @dots{} aN . rest) - -Key/value stores: - - (&plist key0 a0 @dots{} keyN aN) - bind value mapped by keyK in the - @var{source} plist to aK. If the - value is not found, aK is nil. - Uses @code{plist-get} to fetch values. - - (&alist key0 a0 @dots{} keyN aN) - bind value mapped by keyK in the - @var{source} alist to aK. If the - value is not found, aK is nil. - Uses @code{assoc} to fetch values. - - (&hash key0 a0 @dots{} keyN aN) - bind value mapped by keyK in the - @var{source} hash table to aK. If the - value is not found, aK is nil. - Uses @code{gethash} to fetch values. - -Further, special keyword &keys supports "inline" matching of -plist-like key-value pairs, similarly to &keys keyword of -@code{cl-defun}. - - (a1 a2 @dots{} aN &keys key1 b1 @dots{} keyN bK) - -This binds @var{n} values from the list to a1 @dots{} aN, then interprets -the cdr as a plist (see key/value matching above). - -@var{a} shorthand notation for kv-destructuring exists which allows the -patterns be optionally left out and derived from the key name in -the following fashion: - -- a key :foo is converted into @code{foo} pattern, -- a key 'bar is converted into @code{bar} pattern, -- a key "baz" is converted into @code{baz} pattern. - -That is, the entire value under the key is bound to the derived -variable without any further destructuring. - -This is possible only when the form following the key is not a -valid pattern (i.e. not a symbol, a cons cell or a vector). -Otherwise the matching proceeds as usual and in case of an -invalid spec fails with an error. - -Thus the patterns are normalized as follows: - - ;; derive all the missing patterns - (&plist :foo 'bar "baz") => (&plist :foo foo 'bar bar "baz" baz) - - ;; we can specify some but not others - (&plist :foo 'bar explicit-bar) => (&plist :foo foo 'bar explicit-bar) - - ;; nothing happens, we store :foo in x - (&plist :foo x) => (&plist :foo x) - - ;; nothing happens, we match recursively - (&plist :foo (a b c)) => (&plist :foo (a b c)) - -You can name the source using the syntax @var{symbol} &as @var{pattern}. -This syntax works with lists (proper or improper), vectors and -all types of maps. - - (list &as a b c) (list 1 2 3) - -binds @var{a} to 1, @var{b} to 2, @var{c} to 3 and @var{list} to (1 2 3). - -Similarly: - - (bounds &as beg . end) (cons 1 2) - -binds @var{beg} to 1, @var{end} to 2 and @var{bounds} to (1 . 2). - - (items &as first . rest) (list 1 2 3) - -binds @var{first} to 1, @var{rest} to (2 3) and @var{items} to (1 2 3) - - [vect &as _ b c] [1 2 3] - -binds @var{b} to 2, @var{c} to 3 and @var{vect} to [1 2 3] (_ avoids binding as usual). - - (plist &as &plist :b b) (list :a 1 :b 2 :c 3) - -binds @var{b} to 2 and @var{plist} to (:a 1 :b 2 :c 3). Same for &alist and &hash. - -This is especially useful when we want to capture the result of a -computation and destructure at the same time. Consider the -form (function-returning-complex-structure) returning a list of -two vectors with two items each. We want to capture this entire -result and pass it to another computation, but at the same time -we want to get the second item from each vector. We can achieve -it with pattern - - (result &as [_ a] [_ b]) (function-returning-complex-structure) - -Note: Clojure programmers may know this feature as the ":as -binding". The difference is that we put the &as at the front -because we need to support improper list binding. - -@example -@group -(-let (([a (b c) d] [1 (2 3) 4])) (list a b c d)) - @result{} (1 2 3 4) -@end group -@group -(-let [(a b c . d) (list 1 2 3 4 5 6)] (list a b c d)) - @result{} (1 2 3 (4 5 6)) -@end group -@group -(-let [(&plist :foo foo :bar bar) (list :baz 3 :foo 1 :qux 4 :bar 2)] (list foo bar)) - @result{} (1 2) -@end group -@end example -@end defmac - -@anchor{-let*} -@defmac -let* (varlist &rest body) -Bind variables according to @var{varlist} then eval @var{body}. - -@var{varlist} is a list of lists of the form (@var{pattern} @var{source}). Each -@var{pattern} is matched against the @var{source} structurally. @var{source} is -only evaluated once for each @var{pattern}. - -Each @var{source} can refer to the symbols already bound by this -@var{varlist}. This is useful if you want to destructure @var{source} -recursively but also want to name the intermediate structures. - -See @code{-let} (@pxref{-let}) for the list of all possible patterns. - -@example -@group -(-let* (((a . b) (cons 1 2)) ((c . d) (cons 3 4))) (list a b c d)) - @result{} (1 2 3 4) -@end group -@group -(-let* (((a . b) (cons 1 (cons 2 3))) ((c . d) b)) (list a b c d)) - @result{} (1 (2 . 3) 2 3) -@end group -@group -(-let* (((&alist "foo" foo "bar" bar) (list (cons "foo" 1) (cons "bar" (list 'a 'b 'c)))) ((a b c) bar)) (list foo a b c bar)) - @result{} (1 a b c (a b c)) -@end group -@end example -@end defmac - -@anchor{-lambda} -@defmac -lambda (match-form &rest body) -Return a lambda which destructures its input as @var{match-form} and executes @var{body}. - -Note that you have to enclose the @var{match-form} in a pair of parens, -such that: - - (-lambda (x) body) - (-lambda (x y @dots{}) body) - -has the usual semantics of @code{lambda}. Furthermore, these get -translated into normal @code{lambda}, so there is no performance -penalty. - -See @code{-let} (@pxref{-let}) for a description of the destructuring mechanism. - -@example -@group -(-map (-lambda ((x y)) (+ x y)) '((1 2) (3 4) (5 6))) - @result{} (3 7 11) -@end group -@group -(-map (-lambda ([x y]) (+ x y)) '([1 2] [3 4] [5 6])) - @result{} (3 7 11) -@end group -@group -(funcall (-lambda ((_ . a) (_ . b)) (-concat a b)) '(1 2 3) '(4 5 6)) - @result{} (2 3 5 6) -@end group -@end example -@end defmac - -@anchor{-setq} -@defmac -setq ([match-form val] ...) -Bind each @var{match-form} to the value of its @var{val}. - -@var{match-form} destructuring is done according to the rules of @code{-let} (@pxref{-let}). - -This macro allows you to bind multiple variables by destructuring -the value, so for example: - - (-setq (a b) x - (&plist :c c) plist) - -expands roughly speaking to the following code - - (setq a (car x) - b (cadr x) - c (plist-get plist :c)) - -Care is taken to only evaluate each @var{val} once so that in case of -multiple assignments it does not cause unexpected side effects. - -@example -@group -(let (a) (-setq a 1) a) - @result{} 1 -@end group -@group -(let (a b) (-setq (a b) (list 1 2)) (list a b)) - @result{} (1 2) -@end group -@group -(let (c) (-setq (&plist :c c) (list :c "c")) c) - @result{} "c" -@end group -@end example -@end defmac - -@node Side effects -@section Side effects - -Functions iterating over lists for side effect only. - -@anchor{-each} -@defun -each (list fn) -Call @var{fn} on each element of @var{list}. -Return nil; this function is intended for side effects. - -Its anaphoric counterpart is @code{--each}. - -For access to the current element's index in @var{list}, see -@code{-each-indexed} (@pxref{-each-indexed}). - -@example -@group -(let (l) (-each '(1 2 3) (lambda (x) (push x l))) l) - @result{} (3 2 1) -@end group -@group -(let (l) (--each '(1 2 3) (push it l)) l) - @result{} (3 2 1) -@end group -@group -(-each '(1 2 3) #'identity) - @result{} nil -@end group -@end example -@end defun - -@anchor{-each-while} -@defun -each-while (list pred fn) -Call @var{fn} on each @var{item} in @var{list}, while (@var{pred} @var{item}) is non-nil. -Once an @var{item} is reached for which @var{pred} returns nil, @var{fn} is no -longer called. Return nil; this function is intended for side -effects. - -Its anaphoric counterpart is @code{--each-while}. - -@example -@group -(let (l) (-each-while '(2 4 5 6) #'even? (lambda (x) (push x l))) l) - @result{} (4 2) -@end group -@group -(let (l) (--each-while '(1 2 3 4) (< it 3) (push it l)) l) - @result{} (2 1) -@end group -@group -(let ((s 0)) (--each-while '(1 3 4 5) (< it 5) (setq s (+ s it))) s) - @result{} 8 -@end group -@end example -@end defun - -@anchor{-each-indexed} -@defun -each-indexed (list fn) -Call @var{fn} on each index and element of @var{list}. -For each @var{item} at @var{index} in @var{list}, call (funcall @var{fn} @var{index} @var{item}). -Return nil; this function is intended for side effects. - -See also: @code{-map-indexed} (@pxref{-map-indexed}). - -@example -@group -(let (l) (-each-indexed '(a b c) (lambda (i x) (push (list x i) l))) l) - @result{} ((c 2) (b 1) (a 0)) -@end group -@group -(let (l) (--each-indexed '(a b c) (push (list it it-index) l)) l) - @result{} ((c 2) (b 1) (a 0)) -@end group -@group -(let (l) (--each-indexed () (push it l)) l) - @result{} () -@end group -@end example -@end defun - -@anchor{-each-r} -@defun -each-r (list fn) -Call @var{fn} on each element of @var{list} in reversed order. -Return nil; this function is intended for side effects. - -Its anaphoric counterpart is @code{--each-r}. - -@example -@group -(let (l) (-each-r '(1 2 3) (lambda (x) (push x l))) l) - @result{} (1 2 3) -@end group -@group -(let (l) (--each-r '(1 2 3) (push it l)) l) - @result{} (1 2 3) -@end group -@group -(-each-r '(1 2 3) #'identity) - @result{} nil -@end group -@end example -@end defun - -@anchor{-each-r-while} -@defun -each-r-while (list pred fn) -Call @var{fn} on each @var{item} in reversed @var{list}, while (@var{pred} @var{item}) is non-nil. -Once an @var{item} is reached for which @var{pred} returns nil, @var{fn} is no -longer called. Return nil; this function is intended for side -effects. - -Its anaphoric counterpart is @code{--each-r-while}. - -@example -@group -(let (l) (-each-r-while '(2 4 5 6) #'even? (lambda (x) (push x l))) l) - @result{} (6) -@end group -@group -(let (l) (--each-r-while '(1 2 3 4) (>= it 3) (push it l)) l) - @result{} (3 4) -@end group -@group -(let ((s 0)) (--each-r-while '(1 2 3 5) (> it 1) (setq s (+ s it))) s) - @result{} 10 -@end group -@end example -@end defun - -@anchor{-dotimes} -@defun -dotimes (num fn) -Call @var{fn} @var{num} times, presumably for side effects. -@var{fn} is called with a single argument on successive integers -running from 0, inclusive, to @var{num}, exclusive. @var{fn} is not called -if @var{num} is less than 1. - -This function's anaphoric counterpart is @code{--dotimes}. - -@example -@group -(let (s) (-dotimes 3 (lambda (n) (push n s))) s) - @result{} (2 1 0) -@end group -@group -(let (s) (-dotimes 0 (lambda (n) (push n s))) s) - @result{} () -@end group -@group -(let (s) (--dotimes 5 (push it s)) s) - @result{} (4 3 2 1 0) -@end group -@end example -@end defun - -@node Destructive operations -@section Destructive operations - -Macros that modify variables holding lists. - -@anchor{!cons} -@defmac !cons (car cdr) -Destructive: Set @var{cdr} to the cons of @var{car} and @var{cdr}. - -@example -@group -(let (l) (!cons 5 l) l) - @result{} (5) -@end group -@group -(let ((l '(3))) (!cons 5 l) l) - @result{} (5 3) -@end group -@end example -@end defmac - -@anchor{!cdr} -@defmac !cdr (list) -Destructive: Set @var{list} to the cdr of @var{list}. - -@example -@group -(let ((l '(3))) (!cdr l) l) - @result{} () -@end group -@group -(let ((l '(3 5))) (!cdr l) l) - @result{} (5) -@end group -@end example -@end defmac - -@node Function combinators -@section Function combinators - -Functions that manipulate and compose other functions. - -@anchor{-partial} -@defun -partial (fun &rest args) -Return a function that is a partial application of @var{fun} to @var{args}. -@var{args} is a list of the first @var{n} arguments to pass to @var{fun}. -The result is a new function which does the same as @var{fun}, except that -the first @var{n} arguments are fixed at the values with which this function -was called. - -@example -@group -(funcall (-partial #'+ 5)) - @result{} 5 -@end group -@group -(funcall (-partial #'- 5) 3) - @result{} 2 -@end group -@group -(funcall (-partial #'+ 5 2) 3) - @result{} 10 -@end group -@end example -@end defun - -@anchor{-rpartial} -@defun -rpartial (fn &rest args) -Return a function that is a partial application of @var{fn} to @var{args}. -@var{args} is a list of the last @var{n} arguments to pass to @var{fn}. The result -is a new function which does the same as @var{fn}, except that the last -@var{n} arguments are fixed at the values with which this function was -called. This is like @code{-partial} (@pxref{-partial}), except the arguments are fixed -starting from the right rather than the left. - -@example -@group -(funcall (-rpartial #'- 5)) - @result{} -5 -@end group -@group -(funcall (-rpartial #'- 5) 8) - @result{} 3 -@end group -@group -(funcall (-rpartial #'- 5 2) 10) - @result{} 3 -@end group -@end example -@end defun - -@anchor{-juxt} -@defun -juxt (&rest fns) -Return a function that is the juxtaposition of @var{fns}. -The returned function takes a variable number of @var{args}, applies -each of @var{fns} in turn to @var{args}, and returns the list of results. - -@example -@group -(funcall (-juxt) 1 2) - @result{} () -@end group -@group -(funcall (-juxt #'+ #'- #'* #'/) 7 5) - @result{} (12 2 35 1) -@end group -@group -(mapcar (-juxt #'number-to-string #'1+) '(1 2)) - @result{} (("1" 2) ("2" 3)) -@end group -@end example -@end defun - -@anchor{-compose} -@defun -compose (&rest fns) -Compose @var{fns} into a single composite function. -Return a function that takes a variable number of @var{args}, applies -the last function in @var{fns} to @var{args}, and returns the result of -calling each remaining function on the result of the previous -function, right-to-left. If no @var{fns} are given, return a variadic -@code{identity} function. - -@example -@group -(funcall (-compose #'- #'1+ #'+) 1 2 3) - @result{} -7 -@end group -@group -(funcall (-compose #'identity #'1+) 3) - @result{} 4 -@end group -@group -(mapcar (-compose #'not #'stringp) '(nil "")) - @result{} (t nil) -@end group -@end example -@end defun - -@anchor{-applify} -@defun -applify (fn) -Return a function that applies @var{fn} to a single list of args. -This changes the arity of @var{fn} from taking @var{n} distinct arguments to -taking 1 argument which is a list of @var{n} arguments. - -@example -@group -(funcall (-applify #'+) nil) - @result{} 0 -@end group -@group -(mapcar (-applify #'+) '((1 1 1) (1 2 3) (5 5 5))) - @result{} (3 6 15) -@end group -@group -(funcall (-applify #'<) '(3 6)) - @result{} t -@end group -@end example -@end defun - -@anchor{-on} -@defun -on (op trans) -Return a function that calls @var{trans} on each arg and @var{op} on the results. -The returned function takes a variable number of arguments, calls -the function @var{trans} on each one in turn, and then passes those -results as the list of arguments to @var{op}, in the same order. - -For example, the following pairs of expressions are morally -equivalent: - - (funcall (-on #'+ #'1+) 1 2 3) = (+ (1+ 1) (1+ 2) (1+ 3)) - (funcall (-on #'+ #'1+)) = (+) - -@example -@group -(-sort (-on #'< #'length) '((1 2 3) (1) (1 2))) - @result{} ((1) (1 2) (1 2 3)) -@end group -@group -(funcall (-on #'min #'string-to-number) "22" "2" "1" "12") - @result{} 1 -@end group -@group -(-min-by (-on #'> #'length) '((1 2 3) (4) (1 2))) - @result{} (4) -@end group -@end example -@end defun - -@anchor{-flip} -@defun -flip (fn) -Return a function that calls @var{fn} with its arguments reversed. -The returned function takes the same number of arguments as @var{fn}. - -For example, the following two expressions are morally -equivalent: - - (funcall (-flip #'-) 1 2) = (- 2 1) - -See also: @code{-rotate-args} (@pxref{-rotate-args}). - -@example -@group -(-sort (-flip #'<) '(4 3 6 1)) - @result{} (6 4 3 1) -@end group -@group -(funcall (-flip #'-) 3 2 1 10) - @result{} 4 -@end group -@group -(funcall (-flip #'1+) 1) - @result{} 2 -@end group -@end example -@end defun - -@anchor{-rotate-args} -@defun -rotate-args (n fn) -Return a function that calls @var{fn} with args rotated @var{n} places to the right. -The returned function takes the same number of arguments as @var{fn}, -rotates the list of arguments @var{n} places to the right (left if @var{n} is -negative) just like @code{-rotate} (@pxref{-rotate}), and applies @var{fn} to the result. - -See also: @code{-flip} (@pxref{-flip}). - -@example -@group -(funcall (-rotate-args -1 #'list) 1 2 3 4) - @result{} (2 3 4 1) -@end group -@group -(funcall (-rotate-args 1 #'-) 1 10 100) - @result{} 89 -@end group -@group -(funcall (-rotate-args 2 #'list) 3 4 5 1 2) - @result{} (1 2 3 4 5) -@end group -@end example -@end defun - -@anchor{-const} -@defun -const (c) -Return a function that returns @var{c} ignoring any additional arguments. - -In types: a -> b -> a - -@example -@group -(funcall (-const 2) 1 3 "foo") - @result{} 2 -@end group -@group -(mapcar (-const 1) '("a" "b" "c" "d")) - @result{} (1 1 1 1) -@end group -@group -(-sum (mapcar (-const 1) '("a" "b" "c" "d"))) - @result{} 4 -@end group -@end example -@end defun - -@anchor{-cut} -@defmac -cut (&rest params) -Take n-ary function and n arguments and specialize some of them. -Arguments denoted by <> will be left unspecialized. - -See @var{srfi-26} for detailed description. - -@example -@group -(funcall (-cut list 1 <> 3 <> 5) 2 4) - @result{} (1 2 3 4 5) -@end group -@group -(-map (-cut funcall <> 5) `(1+ 1- ,(lambda (x) (/ 1.0 x)))) - @result{} (6 4 0.2) -@end group -@group -(-map (-cut <> 1 2 3) '(list vector string)) - @result{} ((1 2 3) [1 2 3] "\1\2\3") -@end group -@end example -@end defmac - -@anchor{-not} -@defun -not (pred) -Return a predicate that negates the result of @var{pred}. -The returned predicate passes its arguments to @var{pred}. If @var{pred} -returns nil, the result is non-nil; otherwise the result is nil. - -See also: @code{-andfn} (@pxref{-andfn}) and @code{-orfn} (@pxref{-orfn}). - -@example -@group -(funcall (-not #'numberp) "5") - @result{} t -@end group -@group -(-sort (-not #'<) '(5 2 1 0 6)) - @result{} (6 5 2 1 0) -@end group -@group -(-filter (-not (-partial #'< 4)) '(1 2 3 4 5 6 7 8)) - @result{} (1 2 3 4) -@end group -@end example -@end defun - -@anchor{-orfn} -@defun -orfn (&rest preds) -Return a predicate that returns the first non-nil result of @var{preds}. -The returned predicate takes a variable number of arguments, -passes them to each predicate in @var{preds} in turn until one of them -returns non-nil, and returns that non-nil result without calling -the remaining @var{preds}. If all @var{preds} return nil, or if no @var{preds} are -given, the returned predicate returns nil. - -See also: @code{-andfn} (@pxref{-andfn}) and @code{-not} (@pxref{-not}). - -@example -@group -(-filter (-orfn #'natnump #'booleanp) '(1 nil "a" -4 b c t)) - @result{} (1 nil t) -@end group -@group -(funcall (-orfn #'symbolp (-cut string-match-p "x" <>)) "axe") - @result{} 1 -@end group -@group -(funcall (-orfn #'= #'+) 1 1) - @result{} t -@end group -@end example -@end defun - -@anchor{-andfn} -@defun -andfn (&rest preds) -Return a predicate that returns non-nil if all @var{preds} do so. -The returned predicate @var{p} takes a variable number of arguments and -passes them to each predicate in @var{preds} in turn. If any one of -@var{preds} returns nil, @var{p} also returns nil without calling the -remaining @var{preds}. If all @var{preds} return non-nil, @var{p} returns the last -such value. If no @var{preds} are given, @var{p} always returns non-nil. - -See also: @code{-orfn} (@pxref{-orfn}) and @code{-not} (@pxref{-not}). - -@example -@group -(-filter (-andfn #'numberp (-cut < <> 5)) '(a 1 b 6 c 2)) - @result{} (1 2) -@end group -@group -(mapcar (-andfn #'numberp #'1+) '(a 1 b 6)) - @result{} (nil 2 nil 7) -@end group -@group -(funcall (-andfn #'= #'+) 1 1) - @result{} 2 -@end group -@end example -@end defun - -@anchor{-iteratefn} -@defun -iteratefn (fn n) -Return a function @var{fn} composed @var{n} times with itself. - -@var{fn} is a unary function. If you need to use a function of higher -arity, use @code{-applify} (@pxref{-applify}) first to turn it into a unary function. - -With n = 0, this acts as identity function. - -In types: (a -> a) -> Int -> a -> a. - -This function satisfies the following law: - - (funcall (-iteratefn fn n) init) = (-last-item (-iterate fn init (1+ n))). - -@example -@group -(funcall (-iteratefn (lambda (x) (* x x)) 3) 2) - @result{} 256 -@end group -@group -(funcall (-iteratefn '1+ 3) 1) - @result{} 4 -@end group -@group -(funcall (-iteratefn 'cdr 3) '(1 2 3 4 5)) - @result{} (4 5) -@end group -@end example -@end defun - -@anchor{-fixfn} -@defun -fixfn (fn &optional equal-test halt-test) -Return a function that computes the (least) fixpoint of @var{fn}. - -@var{fn} must be a unary function. The returned lambda takes a single -argument, @var{x}, the initial value for the fixpoint iteration. The -iteration halts when either of the following conditions is satisfied: - - 1. Iteration converges to the fixpoint, with equality being - tested using @var{equal-test}. If @var{equal-test} is not specified, - @code{equal} is used. For functions over the floating point - numbers, it may be necessary to provide an appropriate - approximate comparison test. - - 2. @var{halt-test} returns a non-nil value. @var{halt-test} defaults to a - simple counter that returns t after @code{-fixfn-max-iterations}, - to guard against infinite iteration. Otherwise, @var{halt-test} - must be a function that accepts a single argument, the - current value of @var{x}, and returns non-nil as long as iteration - should continue. In this way, a more sophisticated - convergence test may be supplied by the caller. - -The return value of the lambda is either the fixpoint or, if -iteration halted before converging, a cons with car @code{halted} and -cdr the final output from @var{halt-test}. - -In types: (a -> a) -> a -> a. - -@example -@group -(funcall (-fixfn #'cos #'approx=) 0.7) - @result{} 0.7390851332151607 -@end group -@group -(funcall (-fixfn (lambda (x) (expt (+ x 10) 0.25))) 2.0) - @result{} 1.8555845286409378 -@end group -@group -(funcall (-fixfn #'sin #'approx=) 0.1) - @result{} (halted . t) -@end group -@end example -@end defun - -@anchor{-prodfn} -@defun -prodfn (&rest fns) -Take a list of n functions and return a function that takes a -list of length n, applying i-th function to i-th element of the -input list. Returns a list of length n. - -In types (for n=2): ((a -> b), (c -> d)) -> (a, c) -> (b, d) - -This function satisfies the following laws: - - (-compose (-prodfn f g @dots{}) (-prodfn f' g' @dots{})) = (-prodfn (-compose f f') (-compose g g') @dots{}) - (-prodfn f g @dots{}) = (-juxt (-compose f (-partial 'nth 0)) (-compose g (-partial 'nth 1)) @dots{}) - (-compose (-prodfn f g @dots{}) (-juxt f' g' @dots{})) = (-juxt (-compose f f') (-compose g g') @dots{}) - (-compose (-partial 'nth n) (-prod f1 f2 @dots{})) = (-compose fn (-partial 'nth n)) - -@example -@group -(funcall (-prodfn '1+ '1- 'number-to-string) '(1 2 3)) - @result{} (2 1 "3") -@end group -@group -(-map (-prodfn '1+ '1-) '((1 2) (3 4) (5 6) (7 8))) - @result{} ((2 1) (4 3) (6 5) (8 7)) -@end group -@group -(apply '+ (funcall (-prodfn 'length 'string-to-number) '((1 2 3) "15"))) - @result{} 18 -@end group -@end example -@end defun - -@node Development -@chapter Development - -The Dash repository is hosted on GitHub at -@url{https://github.com/magnars/dash.el}. - -@menu -* Contribute:: How to contribute. -* Contributors:: List of contributors. -@end menu - -@node Contribute -@section Contribute - -Yes, please do. Pure functions in the list manipulation realm only, -please. There's a suite of examples/tests in @file{dev/examples.el}, -so remember to add tests for your additions, or they may get broken -later. - -Run the tests with @samp{make check}. Regenerate the docs with -@samp{make docs}. Contributors are encouraged to install these -commands as a Git pre-commit hook, so that the tests are always -running and the docs are always in sync: - -@example -$ cp dev/pre-commit.sh .git/hooks/pre-commit -@end example - -Oh, and don't edit @file{README.md} or @file{dash.texi} directly, as -they are auto-generated. Instead, change their respective templates -@file{readme-template.md} or @file{dash-template.texi}. - -To ensure that Dash can be distributed with GNU ELPA or Emacs, we -require that all contributors assign copyright to the Free Software -Foundation. For more on this, @pxref{Copyright Assignment,,, emacs, -The GNU Emacs Manual}. - -@node Contributors -@section Contributors - -@itemize -@item -@url{https://github.com/Fuco1, Matus Goljer} contributed lots of -features and functions. -@item -@url{https://github.com/tkf, Takafumi Arakaki} contributed -@code{-group-by}. -@item -@url{https://github.com/tali713, tali713} is the author of -@code{-applify}. -@item -@url{https://github.com/vemv, V@'{i}ctor M. Valenzuela} contributed -@code{-repeat}. -@item -@url{https://github.com/nicferrier, Nic Ferrier} contributed -@code{-cons*}. -@item -@url{https://github.com/Wilfred, Wilfred Hughes} contributed -@code{-slice}, @code{-first-item}, and @code{-last-item}. -@item -@url{https://github.com/shosti, Emanuel Evans} contributed -@code{-if-let}, @code{-when-let}, and @code{-insert-at}. -@item -@url{https://github.com/rejeep, Johan Andersson} contributed -@code{-sum}, @code{-product}, and @code{-same-items?}. -@item -@url{https://github.com/kurisuwhyte, Christina Whyte} contributed -@code{-compose}. -@item -@url{https://github.com/steventlamb, Steve Lamb} contributed -@code{-cycle}, @code{-pad}, @code{-annotate}, @code{-zip-fill}, and a -variadic version of @code{-zip}. -@item -@url{https://github.com/fbergroth, Fredrik Bergroth} made the -@code{-if-let} family use @code{-let} destructuring and improved the -script for generating documentation. -@item -@url{https://github.com/holomorph, Mark Oteiza} contributed -@code{-iota} and the script to create an Info manual. -@item -@url{https://github.com/wasamasa, Vasilij Schneidermann} contributed -@code{-some}. -@item -@url{https://github.com/occidens, William West} made @code{-fixfn} -more robust at handling floats. -@item -@url{https://github.com/camsaul, Cam Saul} contributed @code{-some->}, -@code{-some->>}, and @code{-some-->}. -@item -@url{https://github.com/basil-conto, Basil L. Contovounesios} -contributed @code{-common-prefix}, @code{-common-suffix}, and various -other improvements. -@item -@url{https://github.com/doublep, Paul Pogonyshev} contributed -@code{-each-r} and @code{-each-r-while}. -@end itemize - -Thanks! - -New contributors are very welcome. @xref{Contribute}. - -@c Appendices. - -@node FDL -@appendix GNU Free Documentation License -@include doc/fdl.texi - -@node GPL -@appendix GNU General Public License -@include doc/gpl.texi - -@node Index -@unnumbered Index -@printindex fn - -@bye blob - 7d473f4904467b1343a010a5f5add668eada6ef0 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/dir +++ /dev/null @@ -1,18 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs -* Dash: (dash.info). A modern list library for GNU Emacs. blob - eaf3da0e92dcb5f82d495be23dec933dfa86fb84 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/doc/fdl.texi +++ /dev/null @@ -1,505 +0,0 @@ -@c The GNU Free Documentation License. -@center Version 1.3, 3 November 2008 - -@c This file is intended to be included within another document, -@c hence no sectioning command or @node. - -@display -Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. -@uref{https://fsf.org/} - -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. -@end display - -@enumerate 0 -@item -PREAMBLE - -The purpose of this License is to make a manual, textbook, or other -functional and useful document @dfn{free} in the sense of freedom: to -assure everyone the effective freedom to copy and redistribute it, -with or without modifying it, either commercially or noncommercially. -Secondarily, this License preserves for the author and publisher a way -to get credit for their work, while not being considered responsible -for modifications made by others. - -This License is a kind of ``copyleft'', which means that derivative -works of the document must themselves be free in the same sense. It -complements the GNU General Public License, which is a copyleft -license designed for free software. - -We have designed this License in order to use it for manuals for free -software, because free software needs free documentation: a free -program should come with manuals providing the same freedoms that the -software does. But this License is not limited to software manuals; -it can be used for any textual work, regardless of subject matter or -whether it is published as a printed book. We recommend this License -principally for works whose purpose is instruction or reference. - -@item -APPLICABILITY AND DEFINITIONS - -This License applies to any manual or other work, in any medium, that -contains a notice placed by the copyright holder saying it can be -distributed under the terms of this License. Such a notice grants a -world-wide, royalty-free license, unlimited in duration, to use that -work under the conditions stated herein. The ``Document'', below, -refers to any such manual or work. Any member of the public is a -licensee, and is addressed as ``you''. You accept the license if you -copy, modify or distribute the work in a way requiring permission -under copyright law. - -A ``Modified Version'' of the Document means any work containing the -Document or a portion of it, either copied verbatim, or with -modifications and/or translated into another language. - -A ``Secondary Section'' is a named appendix or a front-matter section -of the Document that deals exclusively with the relationship of the -publishers or authors of the Document to the Document's overall -subject (or to related matters) and contains nothing that could fall -directly within that overall subject. (Thus, if the Document is in -part a textbook of mathematics, a Secondary Section may not explain -any mathematics.) The relationship could be a matter of historical -connection with the subject or with related matters, or of legal, -commercial, philosophical, ethical or political position regarding -them. - -The ``Invariant Sections'' are certain Secondary Sections whose titles -are designated, as being those of Invariant Sections, in the notice -that says that the Document is released under this License. If a -section does not fit the above definition of Secondary then it is not -allowed to be designated as Invariant. The Document may contain zero -Invariant Sections. If the Document does not identify any Invariant -Sections then there are none. - -The ``Cover Texts'' are certain short passages of text that are listed, -as Front-Cover Texts or Back-Cover Texts, in the notice that says that -the Document is released under this License. A Front-Cover Text may -be at most 5 words, and a Back-Cover Text may be at most 25 words. - -A ``Transparent'' copy of the Document means a machine-readable copy, -represented in a format whose specification is available to the -general public, that is suitable for revising the document -straightforwardly with generic text editors or (for images composed of -pixels) generic paint programs or (for drawings) some widely available -drawing editor, and that is suitable for input to text formatters or -for automatic translation to a variety of formats suitable for input -to text formatters. A copy made in an otherwise Transparent file -format whose markup, or absence of markup, has been arranged to thwart -or discourage subsequent modification by readers is not Transparent. -An image format is not Transparent if used for any substantial amount -of text. A copy that is not ``Transparent'' is called ``Opaque''. - -Examples of suitable formats for Transparent copies include plain -ASCII without markup, Texinfo input format, La@TeX{} input -format, SGML or XML using a publicly available -DTD, and standard-conforming simple HTML, -PostScript or PDF designed for human modification. Examples -of transparent image formats include PNG, XCF and -JPG@. Opaque formats include proprietary formats that can be -read and edited only by proprietary word processors, SGML or -XML for which the DTD and/or processing tools are -not generally available, and the machine-generated HTML, -PostScript or PDF produced by some word processors for -output purposes only. - -The ``Title Page'' means, for a printed book, the title page itself, -plus such following pages as are needed to hold, legibly, the material -this License requires to appear in the title page. For works in -formats which do not have any title page as such, ``Title Page'' means -the text near the most prominent appearance of the work's title, -preceding the beginning of the body of the text. - -The ``publisher'' means any person or entity that distributes copies -of the Document to the public. - -A section ``Entitled XYZ'' means a named subunit of the Document whose -title either is precisely XYZ or contains XYZ in parentheses following -text that translates XYZ in another language. (Here XYZ stands for a -specific section name mentioned below, such as ``Acknowledgements'', -``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' -of such a section when you modify the Document means that it remains a -section ``Entitled XYZ'' according to this definition. - -The Document may include Warranty Disclaimers next to the notice which -states that this License applies to the Document. These Warranty -Disclaimers are considered to be included by reference in this -License, but only as regards disclaiming warranties: any other -implication that these Warranty Disclaimers may have is void and has -no effect on the meaning of this License. - -@item -VERBATIM COPYING - -You may copy and distribute the Document in any medium, either -commercially or noncommercially, provided that this License, the -copyright notices, and the license notice saying this License applies -to the Document are reproduced in all copies, and that you add no other -conditions whatsoever to those of this License. You may not use -technical measures to obstruct or control the reading or further -copying of the copies you make or distribute. However, you may accept -compensation in exchange for copies. If you distribute a large enough -number of copies you must also follow the conditions in section 3. - -You may also lend copies, under the same conditions stated above, and -you may publicly display copies. - -@item -COPYING IN QUANTITY - -If you publish printed copies (or copies in media that commonly have -printed covers) of the Document, numbering more than 100, and the -Document's license notice requires Cover Texts, you must enclose the -copies in covers that carry, clearly and legibly, all these Cover -Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on -the back cover. Both covers must also clearly and legibly identify -you as the publisher of these copies. The front cover must present -the full title with all words of the title equally prominent and -visible. You may add other material on the covers in addition. -Copying with changes limited to the covers, as long as they preserve -the title of the Document and satisfy these conditions, can be treated -as verbatim copying in other respects. - -If the required texts for either cover are too voluminous to fit -legibly, you should put the first ones listed (as many as fit -reasonably) on the actual cover, and continue the rest onto adjacent -pages. - -If you publish or distribute Opaque copies of the Document numbering -more than 100, you must either include a machine-readable Transparent -copy along with each Opaque copy, or state in or with each Opaque copy -a computer-network location from which the general network-using -public has access to download using public-standard network protocols -a complete Transparent copy of the Document, free of added material. -If you use the latter option, you must take reasonably prudent steps, -when you begin distribution of Opaque copies in quantity, to ensure -that this Transparent copy will remain thus accessible at the stated -location until at least one year after the last time you distribute an -Opaque copy (directly or through your agents or retailers) of that -edition to the public. - -It is requested, but not required, that you contact the authors of the -Document well before redistributing any large number of copies, to give -them a chance to provide you with an updated version of the Document. - -@item -MODIFICATIONS - -You may copy and distribute a Modified Version of the Document under -the conditions of sections 2 and 3 above, provided that you release -the Modified Version under precisely this License, with the Modified -Version filling the role of the Document, thus licensing distribution -and modification of the Modified Version to whoever possesses a copy -of it. In addition, you must do these things in the Modified Version: - -@enumerate A -@item -Use in the Title Page (and on the covers, if any) a title distinct -from that of the Document, and from those of previous versions -(which should, if there were any, be listed in the History section -of the Document). You may use the same title as a previous version -if the original publisher of that version gives permission. - -@item -List on the Title Page, as authors, one or more persons or entities -responsible for authorship of the modifications in the Modified -Version, together with at least five of the principal authors of the -Document (all of its principal authors, if it has fewer than five), -unless they release you from this requirement. - -@item -State on the Title page the name of the publisher of the -Modified Version, as the publisher. - -@item -Preserve all the copyright notices of the Document. - -@item -Add an appropriate copyright notice for your modifications -adjacent to the other copyright notices. - -@item -Include, immediately after the copyright notices, a license notice -giving the public permission to use the Modified Version under the -terms of this License, in the form shown in the Addendum below. - -@item -Preserve in that license notice the full lists of Invariant Sections -and required Cover Texts given in the Document's license notice. - -@item -Include an unaltered copy of this License. - -@item -Preserve the section Entitled ``History'', Preserve its Title, and add -to it an item stating at least the title, year, new authors, and -publisher of the Modified Version as given on the Title Page. If -there is no section Entitled ``History'' in the Document, create one -stating the title, year, authors, and publisher of the Document as -given on its Title Page, then add an item describing the Modified -Version as stated in the previous sentence. - -@item -Preserve the network location, if any, given in the Document for -public access to a Transparent copy of the Document, and likewise -the network locations given in the Document for previous versions -it was based on. These may be placed in the ``History'' section. -You may omit a network location for a work that was published at -least four years before the Document itself, or if the original -publisher of the version it refers to gives permission. - -@item -For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve -the Title of the section, and preserve in the section all the -substance and tone of each of the contributor acknowledgements and/or -dedications given therein. - -@item -Preserve all the Invariant Sections of the Document, -unaltered in their text and in their titles. Section numbers -or the equivalent are not considered part of the section titles. - -@item -Delete any section Entitled ``Endorsements''. Such a section -may not be included in the Modified Version. - -@item -Do not retitle any existing section to be Entitled ``Endorsements'' or -to conflict in title with any Invariant Section. - -@item -Preserve any Warranty Disclaimers. -@end enumerate - -If the Modified Version includes new front-matter sections or -appendices that qualify as Secondary Sections and contain no material -copied from the Document, you may at your option designate some or all -of these sections as invariant. To do this, add their titles to the -list of Invariant Sections in the Modified Version's license notice. -These titles must be distinct from any other section titles. - -You may add a section Entitled ``Endorsements'', provided it contains -nothing but endorsements of your Modified Version by various -parties---for example, statements of peer review or that the text has -been approved by an organization as the authoritative definition of a -standard. - -You may add a passage of up to five words as a Front-Cover Text, and a -passage of up to 25 words as a Back-Cover Text, to the end of the list -of Cover Texts in the Modified Version. Only one passage of -Front-Cover Text and one of Back-Cover Text may be added by (or -through arrangements made by) any one entity. If the Document already -includes a cover text for the same cover, previously added by you or -by arrangement made by the same entity you are acting on behalf of, -you may not add another; but you may replace the old one, on explicit -permission from the previous publisher that added the old one. - -The author(s) and publisher(s) of the Document do not by this License -give permission to use their names for publicity for or to assert or -imply endorsement of any Modified Version. - -@item -COMBINING DOCUMENTS - -You may combine the Document with other documents released under this -License, under the terms defined in section 4 above for modified -versions, provided that you include in the combination all of the -Invariant Sections of all of the original documents, unmodified, and -list them all as Invariant Sections of your combined work in its -license notice, and that you preserve all their Warranty Disclaimers. - -The combined work need only contain one copy of this License, and -multiple identical Invariant Sections may be replaced with a single -copy. If there are multiple Invariant Sections with the same name but -different contents, make the title of each such section unique by -adding at the end of it, in parentheses, the name of the original -author or publisher of that section if known, or else a unique number. -Make the same adjustment to the section titles in the list of -Invariant Sections in the license notice of the combined work. - -In the combination, you must combine any sections Entitled ``History'' -in the various original documents, forming one section Entitled -``History''; likewise combine any sections Entitled ``Acknowledgements'', -and any sections Entitled ``Dedications''. You must delete all -sections Entitled ``Endorsements.'' - -@item -COLLECTIONS OF DOCUMENTS - -You may make a collection consisting of the Document and other documents -released under this License, and replace the individual copies of this -License in the various documents with a single copy that is included in -the collection, provided that you follow the rules of this License for -verbatim copying of each of the documents in all other respects. - -You may extract a single document from such a collection, and distribute -it individually under this License, provided you insert a copy of this -License into the extracted document, and follow this License in all -other respects regarding verbatim copying of that document. - -@item -AGGREGATION WITH INDEPENDENT WORKS - -A compilation of the Document or its derivatives with other separate -and independent documents or works, in or on a volume of a storage or -distribution medium, is called an ``aggregate'' if the copyright -resulting from the compilation is not used to limit the legal rights -of the compilation's users beyond what the individual works permit. -When the Document is included in an aggregate, this License does not -apply to the other works in the aggregate which are not themselves -derivative works of the Document. - -If the Cover Text requirement of section 3 is applicable to these -copies of the Document, then if the Document is less than one half of -the entire aggregate, the Document's Cover Texts may be placed on -covers that bracket the Document within the aggregate, or the -electronic equivalent of covers if the Document is in electronic form. -Otherwise they must appear on printed covers that bracket the whole -aggregate. - -@item -TRANSLATION - -Translation is considered a kind of modification, so you may -distribute translations of the Document under the terms of section 4. -Replacing Invariant Sections with translations requires special -permission from their copyright holders, but you may include -translations of some or all Invariant Sections in addition to the -original versions of these Invariant Sections. You may include a -translation of this License, and all the license notices in the -Document, and any Warranty Disclaimers, provided that you also include -the original English version of this License and the original versions -of those notices and disclaimers. In case of a disagreement between -the translation and the original version of this License or a notice -or disclaimer, the original version will prevail. - -If a section in the Document is Entitled ``Acknowledgements'', -``Dedications'', or ``History'', the requirement (section 4) to Preserve -its Title (section 1) will typically require changing the actual -title. - -@item -TERMINATION - -You may not copy, modify, sublicense, or distribute the Document -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense, or distribute it is void, and -will automatically terminate your rights under this License. - -However, if you cease all violation of this License, then your license -from a particular copyright holder is reinstated (a) provisionally, -unless and until the copyright holder explicitly and finally -terminates your license, and (b) permanently, if the copyright holder -fails to notify you of the violation by some reasonable means prior to -60 days after the cessation. - -Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, receipt of a copy of some or all of the same material does -not give you any rights to use it. - -@item -FUTURE REVISIONS OF THIS LICENSE - -The Free Software Foundation may publish new, revised versions -of the GNU Free Documentation License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. See -@uref{https://www.gnu.org/licenses/}. - -Each version of the License is given a distinguishing version number. -If the Document specifies that a particular numbered version of this -License ``or any later version'' applies to it, you have the option of -following the terms and conditions either of that specified version or -of any later version that has been published (not as a draft) by the -Free Software Foundation. If the Document does not specify a version -number of this License, you may choose any version ever published (not -as a draft) by the Free Software Foundation. If the Document -specifies that a proxy can decide which future versions of this -License can be used, that proxy's public statement of acceptance of a -version permanently authorizes you to choose that version for the -Document. - -@item -RELICENSING - -``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any -World Wide Web server that publishes copyrightable works and also -provides prominent facilities for anybody to edit those works. A -public wiki that anybody can edit is an example of such a server. A -``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the -site means any set of copyrightable works thus published on the MMC -site. - -``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 -license published by Creative Commons Corporation, a not-for-profit -corporation with a principal place of business in San Francisco, -California, as well as future copyleft versions of that license -published by that same organization. - -``Incorporate'' means to publish or republish a Document, in whole or -in part, as part of another Document. - -An MMC is ``eligible for relicensing'' if it is licensed under this -License, and if all works that were first published under this License -somewhere other than this MMC, and subsequently incorporated in whole -or in part into the MMC, (1) had no cover texts or invariant sections, -and (2) were thus incorporated prior to November 1, 2008. - -The operator of an MMC Site may republish an MMC contained in the site -under CC-BY-SA on the same site at any time before August 1, 2009, -provided the MMC is eligible for relicensing. - -@end enumerate - -@page -@heading ADDENDUM: How to use this License for your documents - -To use this License in a document you have written, include a copy of -the License in the document and put the following copyright and -license notices just after the title page: - -@smallexample -@group - Copyright (C) @var{year} @var{your name}. - Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.3 - or any later version published by the Free Software Foundation; - with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - Texts. A copy of the license is included in the section entitled ``GNU - Free Documentation License''. -@end group -@end smallexample - -If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, -replace the ``with@dots{}Texts.''@: line with this: - -@smallexample -@group - with the Invariant Sections being @var{list their titles}, with - the Front-Cover Texts being @var{list}, and with the Back-Cover Texts - being @var{list}. -@end group -@end smallexample - -If you have Invariant Sections without Cover Texts, or some other -combination of the three, merge those two alternatives to suit the -situation. - -If your document contains nontrivial examples of program code, we -recommend releasing these examples in parallel under your choice of -free software license, such as the GNU General Public License, -to permit their use in free software. - -@c Local Variables: -@c ispell-local-pdict: "ispell-dict" -@c End: blob - c007dc06966de590c467311a9027d88b038c9ffb (mode 644) blob + /dev/null --- elpa/dash-2.19.1/doc/gpl.texi +++ /dev/null @@ -1,717 +0,0 @@ -@c The GNU General Public License. -@center Version 3, 29 June 2007 - -@c This file is intended to be included within another document, -@c hence no sectioning command or @node. - -@display -Copyright @copyright{} 2007 Free Software Foundation, Inc. @url{https://fsf.org/} - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. -@end display - -@heading Preamble - -The GNU General Public License is a free, copyleft license for -software and other kinds of works. - -The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom -to share and change all versions of a program---to make sure it remains -free software for all its users. We, the Free Software Foundation, -use the GNU General Public License for most of our software; it -applies also to any other work released this way by its authors. You -can apply it to your programs, too. - -When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - -To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you -have certain responsibilities if you distribute copies of the -software, or if you modify it: responsibilities to respect the freedom -of others. - -For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, -receive or can get the source code. And you must show them these -terms so they know their rights. - -Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - -For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - -Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the -manufacturer can do so. This is fundamentally incompatible with the -aim of protecting users' freedom to change the software. The -systematic pattern of such abuse occurs in the area of products for -individuals to use, which is precisely where it is most unacceptable. -Therefore, we have designed this version of the GPL to prohibit the -practice for those products. If such problems arise substantially in -other domains, we stand ready to extend this provision to those -domains in future versions of the GPL, as needed to protect the -freedom of users. - -Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish -to avoid the special danger that patents applied to a free program -could make it effectively proprietary. To prevent this, the GPL -assures that patents cannot be used to render the program non-free. - -The precise terms and conditions for copying, distribution and -modification follow. - -@heading TERMS AND CONDITIONS - -@enumerate 0 -@item Definitions. - -``This License'' refers to version 3 of the GNU General Public License. - -``Copyright'' also means copyright-like laws that apply to other kinds -of works, such as semiconductor masks. - -``The Program'' refers to any copyrightable work licensed under this -License. Each licensee is addressed as ``you''. ``Licensees'' and -``recipients'' may be individuals or organizations. - -To ``modify'' a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of -an exact copy. The resulting work is called a ``modified version'' of -the earlier work or a work ``based on'' the earlier work. - -A ``covered work'' means either the unmodified Program or a work based -on the Program. - -To ``propagate'' a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - -To ``convey'' a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user -through a computer network, with no transfer of a copy, is not -conveying. - -An interactive user interface displays ``Appropriate Legal Notices'' to -the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - -@item Source Code. - -The ``source code'' for a work means the preferred form of the work for -making modifications to it. ``Object code'' means any non-source form -of a work. - -A ``Standard Interface'' means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - -The ``System Libraries'' of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -``Major Component'', in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - -The ``Corresponding Source'' for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - -The Corresponding Source need not include anything that users can -regenerate automatically from other parts of the Corresponding Source. - -The Corresponding Source for a work in source code form is that same -work. - -@item Basic Permissions. - -All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - -You may make, run and propagate covered works that you do not convey, -without conditions so long as your license otherwise remains in force. -You may convey covered works to others for the sole purpose of having -them make modifications exclusively for you, or provide you with -facilities for running those works, provided that you comply with the -terms of this License in conveying all material for which you do not -control copyright. Those thus making or running the covered works for -you must do so exclusively on your behalf, under your direction and -control, on terms that prohibit them from making any copies of your -copyrighted material outside their relationship with you. - -Conveying under any other circumstances is permitted solely under the -conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - -@item Protecting Users' Legal Rights From Anti-Circumvention Law. - -No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - -When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such -circumvention is effected by exercising rights under this License with -respect to the covered work, and you disclaim any intention to limit -operation or modification of the work as a means of enforcing, against -the work's users, your or third parties' legal rights to forbid -circumvention of technological measures. - -@item Conveying Verbatim Copies. - -You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - -You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - -@item Conveying Modified Source Versions. - -You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these -conditions: - -@enumerate a -@item -The work must carry prominent notices stating that you modified it, -and giving a relevant date. - -@item -The work must carry prominent notices stating that it is released -under this License and any conditions added under section 7. This -requirement modifies the requirement in section 4 to ``keep intact all -notices''. - -@item -You must license the entire work, as a whole, under this License to -anyone who comes into possession of a copy. This License will -therefore apply, along with any applicable section 7 additional terms, -to the whole of the work, and all its parts, regardless of how they -are packaged. This License gives no permission to license the work in -any other way, but it does not invalidate such permission if you have -separately received it. - -@item -If the work has interactive user interfaces, each must display -Appropriate Legal Notices; however, if the Program has interactive -interfaces that do not display Appropriate Legal Notices, your work -need not make them do so. -@end enumerate - -A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -``aggregate'' if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - -@item Conveying Non-Source Forms. - -You may convey a covered work in object code form under the terms of -sections 4 and 5, provided that you also convey the machine-readable -Corresponding Source under the terms of this License, in one of these -ways: - -@enumerate a -@item -Convey the object code in, or embodied in, a physical product -(including a physical distribution medium), accompanied by the -Corresponding Source fixed on a durable physical medium customarily -used for software interchange. - -@item -Convey the object code in, or embodied in, a physical product -(including a physical distribution medium), accompanied by a written -offer, valid for at least three years and valid for as long as you -offer spare parts or customer support for that product model, to give -anyone who possesses the object code either (1) a copy of the -Corresponding Source for all the software in the product that is -covered by this License, on a durable physical medium customarily used -for software interchange, for a price no more than your reasonable -cost of physically performing this conveying of source, or (2) access -to copy the Corresponding Source from a network server at no charge. - -@item -Convey individual copies of the object code with a copy of the written -offer to provide the Corresponding Source. This alternative is -allowed only occasionally and noncommercially, and only if you -received the object code with such an offer, in accord with subsection -6b. - -@item -Convey the object code by offering access from a designated place -(gratis or for a charge), and offer equivalent access to the -Corresponding Source in the same way through the same place at no -further charge. You need not require recipients to copy the -Corresponding Source along with the object code. If the place to copy -the object code is a network server, the Corresponding Source may be -on a different server (operated by you or a third party) that supports -equivalent copying facilities, provided you maintain clear directions -next to the object code saying where to find the Corresponding Source. -Regardless of what server hosts the Corresponding Source, you remain -obligated to ensure that it is available for as long as needed to -satisfy these requirements. - -@item -Convey the object code using peer-to-peer transmission, provided you -inform other peers where the object code and Corresponding Source of -the work are being offered to the general public at no charge under -subsection 6d. - -@end enumerate - -A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - -A ``User Product'' is either (1) a ``consumer product'', which means any -tangible personal property which is normally used for personal, -family, or household purposes, or (2) anything designed or sold for -incorporation into a dwelling. In determining whether a product is a -consumer product, doubtful cases shall be resolved in favor of -coverage. For a particular product received by a particular user, -``normally used'' refers to a typical or common use of that class of -product, regardless of the status of the particular user or of the way -in which the particular user actually uses, or expects or is expected -to use, the product. A product is a consumer product regardless of -whether the product has substantial commercial, industrial or -non-consumer uses, unless such uses represent the only significant -mode of use of the product. - -``Installation Information'' for a User Product means any methods, -procedures, authorization keys, or other information required to -install and execute modified versions of a covered work in that User -Product from a modified version of its Corresponding Source. The -information must suffice to ensure that the continued functioning of -the modified object code is in no case prevented or interfered with -solely because modification has been made. - -If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - -The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or -updates for a work that has been modified or installed by the -recipient, or for the User Product in which it has been modified or -installed. Access to a network may be denied when the modification -itself materially and adversely affects the operation of the network -or violates the rules and protocols for communication across the -network. - -Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - -@item Additional Terms. - -``Additional permissions'' are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - -Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders -of that material) supplement the terms of this License with terms: - -@enumerate a -@item -Disclaiming warranty or limiting liability differently from the terms -of sections 15 and 16 of this License; or - -@item -Requiring preservation of specified reasonable legal notices or author -attributions in that material or in the Appropriate Legal Notices -displayed by works containing it; or - -@item -Prohibiting misrepresentation of the origin of that material, or -requiring that modified versions of such material be marked in -reasonable ways as different from the original version; or - -@item -Limiting the use for publicity purposes of names of licensors or -authors of the material; or - -@item -Declining to grant rights under trademark law for use of some trade -names, trademarks, or service marks; or - -@item -Requiring indemnification of licensors and authors of that material by -anyone who conveys the material (or modified versions of it) with -contractual assumptions of liability to the recipient, for any -liability that these contractual assumptions directly impose on those -licensors and authors. -@end enumerate - -All other non-permissive additional terms are considered ``further -restrictions'' within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - -If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; the -above requirements apply either way. - -@item Termination. - -You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - -However, if you cease all violation of this License, then your license -from a particular copyright holder is reinstated (a) provisionally, -unless and until the copyright holder explicitly and finally -terminates your license, and (b) permanently, if the copyright holder -fails to notify you of the violation by some reasonable means prior to -60 days after the cessation. - -Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - -@item Acceptance Not Required for Having Copies. - -You are not required to accept this License in order to receive or run -a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - -@item Automatic Licensing of Downstream Recipients. - -Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - -An ``entity transaction'' is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - -@item Patents. - -A ``contributor'' is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's ``contributor version''. - -A contributor's ``essential patent claims'' are all patent claims owned -or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, ``control'' includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - -In the following three paragraphs, a ``patent license'' is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To ``grant'' such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - -If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. ``Knowingly relying'' means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - -A patent license is ``discriminatory'' if it does not include within the -scope of its coverage, prohibits the exercise of, or is conditioned on -the non-exercise of one or more of the rights that are specifically -granted under this License. You may not convey a covered work if you -are a party to an arrangement with a third party that is in the -business of distributing software, under which you make payment to the -third party based on the extent of your activity of conveying the -work, and under which the third party grants, to any of the parties -who would receive the covered work from you, a discriminatory patent -license (a) in connection with copies of the covered work conveyed by -you (or copies made from those copies), or (b) primarily for and in -connection with specific products or compilations that contain the -covered work, unless you entered into that arrangement, or that patent -license was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - -@item No Surrender of Others' Freedom. - -If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey -a covered work so as to satisfy simultaneously your obligations under -this License and any other pertinent obligations, then as a -consequence you may not convey it at all. For example, if you agree -to terms that obligate you to collect a royalty for further conveying -from those to whom you convey the Program, the only way you could -satisfy both those terms and this License would be to refrain entirely -from conveying the Program. - -@item Use with the GNU Affero General Public License. - -Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - -@item Revised Versions of this License. - -The Free Software Foundation may publish revised and/or new versions -of the GNU General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies that a certain numbered version of the GNU General Public -License ``or any later version'' applies to it, you have the option of -following the terms and conditions either of that numbered version or -of any later version published by the Free Software Foundation. If -the Program does not specify a version number of the GNU General -Public License, you may choose any version ever published by the Free -Software Foundation. - -If the Program specifies that a proxy can decide which future versions -of the GNU General Public License can be used, that proxy's public -statement of acceptance of a version permanently authorizes you to -choose that version for the Program. - -Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - -@item Disclaimer of Warranty. - -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW@. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM ``AS IS'' WITHOUT -WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE@. THE ENTIRE RISK AS TO THE QUALITY AND -PERFORMANCE OF THE PROGRAM IS WITH YOU@. SHOULD THE PROGRAM PROVE -DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR -CORRECTION. - -@item Limitation of Liability. - -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR -CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT -NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR -LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM -TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER -PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -@item Interpretation of Sections 15 and 16. - -If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - -@end enumerate - -@heading END OF TERMS AND CONDITIONS - -@heading How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - -To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the ``copyright'' line and a pointer to where the full notice is found. - -@smallexample -@var{one line to give the program's name and a brief idea of what it does.} -Copyright (C) @var{year} @var{name of author} - -This program 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 of the License, or (at -your option) any later version. - -This program 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 this program. If not, see @url{https://www.gnu.org/licenses/}. -@end smallexample - -Also add information on how to contact you by electronic and paper mail. - -If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - -@smallexample -@var{program} Copyright (C) @var{year} @var{name of author} -This program comes with ABSOLUTELY NO WARRANTY; for details type @samp{show w}. -This is free software, and you are welcome to redistribute it -under certain conditions; type @samp{show c} for details. -@end smallexample - -The hypothetical commands @samp{show w} and @samp{show c} should show -the appropriate parts of the General Public License. Of course, your -program's commands might be different; for a GUI interface, you would -use an ``about box''. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a ``copyright disclaimer'' for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -@url{https://www.gnu.org/licenses/}. - -The GNU General Public License does not permit incorporating your -program into proprietary programs. If your program is a subroutine -library, you may consider it more useful to permit linking proprietary -applications with the library. If this is what you want to do, use -the GNU Lesser General Public License instead of this License. But -first, please read @url{https://www.gnu.org/licenses/why-not-lgpl.html}. blob - 59c40ed69d564fed5833a7aa94c5269561ef06fa (mode 644) blob + /dev/null Binary files elpa/dash-2.19.1/rainbow-dash.png and /dev/null differ blob - 3065d2c80a6631a5701363e9d72cfc8898fa8685 (mode 644) blob + /dev/null --- elpa/dash-2.19.1/readme-template.md +++ /dev/null @@ -1,211 +0,0 @@ -[![CI](https://github.com/magnars/dash.el/actions/workflows/test.yml/badge.svg)](https://github.com/magnars/dash.el/actions/workflows/test.yml) -[![GNU ELPA](https://elpa.gnu.org/packages/dash.svg)](https://elpa.gnu.org/packages/dash.html) -[![GNU-devel ELPA](https://elpa.gnu.org/devel/dash.svg)](https://elpa.gnu.org/devel/dash.html) -[![MELPA Stable](https://stable.melpa.org/packages/dash-badge.svg)](https://stable.melpa.org/#/dash) -[![MELPA](https://melpa.org/packages/dash-badge.svg)](https://melpa.org/#/dash) - -# dash.el - -A modern list API for Emacs. No -[`'cl`](https://gnu.org/software/emacs/manual/html_node/cl/) required. - -See the end of the file for license conditions. - -## Contents - -* [Change log](#change-log) - * [Upcoming breaking change!](#upcoming-breaking-change) -* [Installation](#installation) -* [Functions](#functions) -* [Contribute](#contribute) -* [Contributors](#contributors) -* [License](#license) - -## Change log - -See the [`NEWS.md`](NEWS.md) file. - -### Upcoming breaking change! - -- For backward compatibility reasons, `-zip` when called with two - lists returns a list of cons cells, rather than a list of proper - lists. This is a clunky API, and may be changed in a future release - to always return a list of proper lists, as `-zip-lists` currently - does. - - **N.B.:** Do not rely on the current behavior of `-zip` for two - lists. Instead, use `-zip-pair` for a list of cons cells, and - `-zip-lists` for a list of proper lists. - -## Installation - -Dash is available on [GNU ELPA](https://elpa.gnu.org/), [GNU-devel -ELPA](https://elpa.gnu.org/devel/), and [MELPA](https://melpa.org/), -and can be installed with the standard command `package-install`: - - M-x package-install RET dash RET - -See [`(info "(emacs) Package -Installation")`](https://gnu.org/software/emacs/manual/html_node/emacs/Package-Installation.html). - -Alternatively, you can just dump `dash.el` in your `load-path` -somewhere. See [`(info "(emacs) Lisp -Libraries")`](https://gnu.org/software/emacs/manual/html_node/emacs/Lisp-Libraries.html). - -### Using in a package - -Add something like this to the library's headers: - - ;; Package-Requires: ((dash "[[ dash-version ]]")) - -See [`(info "(elisp) Library -Headers")`](https://gnu.org/software/emacs/manual/html_node/elisp/Library-Headers.html). - -### Fontification of special variables - -Font lock of special Dash variables (`it`, `acc`, etc.) in Emacs Lisp -buffers can optionally be enabled with the autoloaded minor mode -`dash-fontify-mode`. In older Emacs versions which do not dynamically -detect macros, the minor mode also fontifies Dash macro calls. - -To automatically enable the minor mode in all Emacs Lisp buffers, just -call its autoloaded global counterpart `global-dash-fontify-mode`, -either interactively or from your `user-init-file`: - -```el -(global-dash-fontify-mode) -``` - -### Info symbol lookup - -While editing Elisp files, you can use `C-h S` (`info-lookup-symbol`) -to look up Elisp symbols in the relevant Info manuals (see [`(emacs) -Info -Lookup`](https://gnu.org/software/emacs/manual/html_node/emacs/Info-Lookup.html)). -To enable the same for Dash symbols, use the command -`dash-register-info-lookup`. It can be called directly when needed, -or automatically from your `user-init-file`. For example: - -```el -(with-eval-after-load 'info-look - (dash-register-info-lookup)) -``` - -## Functions - -All functions and constructs in the library use a dash (`-`) prefix. - -The library also provides anaphoric macro versions of functions where -that makes sense. The names of these macros are prefixed with two -dashes (`--`) instead of one. - -While `-map` applies a function to each element of a list, its -anaphoric counterpart `--map` evaluates a form with the local variable -`it` temporarily bound to the current list element instead. For -example: - -```el -(-map (lambda (n) (* n n)) '(1 2 3 4)) ; Normal version. -(--map (* it it) '(1 2 3 4)) ; Anaphoric version. -``` - -The normal version can of course also be written as follows: - -```el -(defun my-square (n) - "Return N multiplied by itself." - (* n n)) - -(-map #'my-square '(1 2 3 4)) -``` - -This demonstrates the utility of both versions. -[[ function-list ]] - -[[ function-docs ]] -## Contribute - -Yes, please do. Pure functions in the list manipulation realm only, -please. There's a suite of examples/tests in `dev/examples.el`, so -remember to add tests for your additions, or I might break them later. - -You'll find the repo at: - - https://github.com/magnars/dash.el - -Run the tests with: - - make check - -Regenerate the docs with: - - make docs - -I highly recommend that you install these as a pre-commit hook, so -that the tests are always running and the docs are always in sync: - - cp dev/pre-commit.sh .git/hooks/pre-commit - -Oh, and don't edit `README.md` or `dash.texi` directly; they are -auto-generated. Change `readme-template.md` or `dash-template.texi` -instead, respectively. - -To ensure that `dash.el` can be distributed with GNU ELPA or Emacs, we -require that all contributors assign copyright to the Free Software -Foundation. For more on this, see [`(info "(emacs) Copyright -Assignment")`](https://gnu.org/software/emacs/manual/html_node/emacs/Copyright-Assignment.html). - -## Contributors - -- [Matus Goljer](https://github.com/Fuco1) contributed lots of features and - functions. -- [Takafumi Arakaki](https://github.com/tkf) contributed `-group-by`. -- [tali713](https://github.com/tali713) is the author of `-applify`. -- [Víctor M. Valenzuela](https://github.com/vemv) contributed `-repeat`. -- [Nic Ferrier](https://github.com/nicferrier) contributed `-cons*`. -- [Wilfred Hughes](https://github.com/Wilfred) contributed `-slice`, - `-first-item`, and `-last-item`. -- [Emanuel Evans](https://github.com/shosti) contributed `-if-let`, `-when-let`, - and `-insert-at`. -- [Johan Andersson](https://github.com/rejeep) contributed `-sum`, `-product`, - and `-same-items?`. -- [Christina Whyte](https://github.com/kurisuwhyte) contributed `-compose`. -- [Steve Lamb](https://github.com/steventlamb) contributed `-cycle`, `-pad`, - `-annotate`, `-zip-fill`, and a variadic version of `-zip`. -- [Fredrik Bergroth](https://github.com/fbergroth) made the `-if-let` family use - `-let` destructuring and improved the script for generating documentation. -- [Mark Oteiza](https://github.com/holomorph) contributed `-iota` and - the script to create an Info manual. -- [Vasilij Schneidermann](https://github.com/wasamasa) contributed `-some`. -- [William West](https://github.com/occidens) made `-fixfn` more robust at - handling floats. -- [Cam Saul](https://github.com/camsaul) contributed `-some->`, `-some->>`, and - `-some-->`. -- [Basil L. Contovounesios](https://github.com/basil-conto) contributed - `-common-prefix`, `-common-suffix`, and various other improvements. -- [Paul Pogonyshev](https://github.com/doublep) contributed `-each-r` and - `-each-r-while`. - -Thanks! - -New contributors are very welcome. See the -[`Contribute`](#contribute) section above. - -## License - -Copyright (C) 2012-2021 Free Software Foundation, Inc. - -Author: Magnar Sveen - -This program 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 of the License, or -(at your option) any later version. - -This program 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 this program. If not, see . blob - cc09d95a8ac59bcca0ed08caef5f309eca4e810e (mode 644) blob + /dev/null --- elpa/dash-2.19.1.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2021-08-26T23:05:01+0200 using RSA \ No newline at end of file blob - 12355a0d5c3ea950424d1ec247cf7c61e3fb67b7 (mode 644) blob + /dev/null --- elpa/deadgrep-0.12/deadgrep-autoloads.el +++ /dev/null @@ -1,40 +0,0 @@ -;;; deadgrep-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from deadgrep.el - -(autoload 'deadgrep "deadgrep" "\ -Start a ripgrep search for SEARCH-TERM in DIRECTORY. - -If not provided, DIR defaults to the directory as determined by -`deadgrep-project-root-function'. - -See also `deadgrep-project-root-overrides'. - -If called with a prefix argument, create the results buffer but -don't actually start the search. - -(fn SEARCH-TERM &optional DIRECTORY)" t) -(register-definition-prefixes "deadgrep" '("deadgrep-")) - -;;; End of scraped data - -(provide 'deadgrep-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; deadgrep-autoloads.el ends here blob - f186af8b089c5796418e3ad60eab8e3041b9906e (mode 644) blob + /dev/null --- elpa/deadgrep-0.12/deadgrep-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; Generated package description from deadgrep.el -*- no-byte-compile: t -*- -(define-package "deadgrep" "0.12" "fast, friendly searching with ripgrep" '((emacs "25.1") (dash "2.12.0") (s "1.11.0") (spinner "1.7.3")) :commit "d32fe49079c1e9e0af95387120fa4990d4107778" :authors '(("Wilfred Hughes" . "me@wilfred.me.uk")) :maintainers '(("Wilfred Hughes" . "me@wilfred.me.uk")) :maintainer '("Wilfred Hughes" . "me@wilfred.me.uk") :keywords '("tools") :url "https://github.com/Wilfred/deadgrep") blob - 3ecf81cfe292fa958e447511970cffb883a3da4d (mode 644) blob + /dev/null --- elpa/deadgrep-0.12/deadgrep.el +++ /dev/null @@ -1,1672 +0,0 @@ -;;; deadgrep.el --- fast, friendly searching with ripgrep -*- lexical-binding: t; -*- - -;; Copyright (C) 2018 Wilfred Hughes - -;; Author: Wilfred Hughes -;; URL: https://github.com/Wilfred/deadgrep -;; Package-Version: 0.12 -;; Package-Commit: d32fe49079c1e9e0af95387120fa4990d4107778 -;; Keywords: tools -;; Version: 0.12 -;; Package-Requires: ((emacs "25.1") (dash "2.12.0") (s "1.11.0") (spinner "1.7.3")) - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Perform text searches with the speed of ripgrep and the comfort of -;; Emacs. This is a bespoke mode that does not rely on -;; compilation-mode, but tries to be a perfect fit for ripgrep. - -;; Install from MELPA, then `M-x deadgrep' will do a search! - -;;; Code: - -(require 'cl-lib) -(require 's) -(require 'dash) -(require 'spinner) -(require 'project) - -(defgroup deadgrep nil - "A powerful text search UI using ripgrep." - :group 'tools - :group 'matching) - -(defcustom deadgrep-executable - "rg" - "The rg executable used by deadgrep. -This will be looked up on `exec-path' if it isn't an absolute -path to the binary." - :type 'string - :group 'deadgrep) - -(defcustom deadgrep-max-buffers - 4 - "Deadgrep will kill the least recently used results buffer -if there are more than this many. - -To disable cleanup entirely, set this variable to nil." - :type '(choice - (number :tag "Maximum of buffers allowed") - (const :tag "Disable cleanup" nil)) - :group 'deadgrep) - -(defcustom deadgrep-project-root-function - #'deadgrep--project-root - "Function called by `deadgrep' to work out the root directory -to search from. - -See also `deadgrep-project-root-overrides'." - :type 'function - :group 'deadgrep) - -(defvar deadgrep-project-root-overrides nil - "An alist associating project directories with the desired -search directory. - -This is useful for large repos where you only want to search a -subdirectory. It's also handy for nested repos where you want to -search from the parent. - -This affects the behaviour of `deadgrep--project-root', so this -variable has no effect if you change -`deadgrep-project-root-function'.") - -(defvar deadgrep-history - nil - "A list of the previous search terms.") - -(defvar deadgrep-max-line-length - 500 - "Truncate lines if they are longer than this. - -Emacs performance can be really poor with long lines, so this -ensures that searching minified files does not slow down movement -in results buffers. - -In extreme cases (100KiB+ single-line files), we can get a stack -overflow on our regexp matchers if we don't apply this.") - -(defcustom deadgrep-display-buffer-function - 'switch-to-buffer-other-window - "Function used to show the deadgrep result buffer. - -This function is called with one argument, the results buffer to -display." - :type 'function - :group 'deadgrep) - -(defface deadgrep-meta-face - '((t :inherit font-lock-comment-face)) - "Face used for deadgrep UI text." - :group 'deadgrep) - -(defface deadgrep-filename-face - '((t :inherit bold)) - "Face used for filename headings in results buffers." - :group 'deadgrep) - -(defface deadgrep-search-term-face - '((t :inherit font-lock-variable-name-face)) - "Face used for the search term in results buffers." - :group 'deadgrep) - -(defface deadgrep-regexp-metachar-face - '((t :inherit - ;; TODO: I've seen a more appropriate face in some themes, - ;; find out what to use instead here. - font-lock-constant-face)) - "Face used for regexp metacharacters in search terms." - :group 'deadgrep) - -(defface deadgrep-match-face - '((t :inherit match)) - "Face used for the portion of a line that matches the search term." - :group 'deadgrep) - -(defvar-local deadgrep--search-term nil) -;; Ensure this variable is ignored by `kill-all-local-variables' when -;; switching between `deadgrep-mode' and `deadgrep-edit-mode'. -(put 'deadgrep--search-term 'permanent-local t) - -(defvar-local deadgrep--search-type 'string) -(put 'deadgrep--search-type 'permanent-local t) -(defvar-local deadgrep--search-case 'smart) -(put 'deadgrep--search-case 'permanent-local t) -(defvar-local deadgrep--file-type 'all) -(put 'deadgrep--file-type 'permanent-local t) - -(defvar-local deadgrep--context nil - "When set, also show context of results. -This is stored as a cons cell of integers (lines-before . lines-after).") -(put 'deadgrep--context 'permanent-local t) -(defvar-local deadgrep--initial-filename nil - "The filename of the buffer that deadgrep was started from. -Used to offer better default values for file options.") -(put 'deadgrep--initial-filename 'permanent-local t) - -(defvar-local deadgrep--current-file nil - "The file we're currently inserting results for.") -(defvar-local deadgrep--spinner nil) -(defvar-local deadgrep--remaining-output nil - "We can't guarantee that our process filter will always receive whole lines. -We save the last line here, in case we need to append more text to it.") -(defvar-local deadgrep--postpone-start nil - "If non-nil, don't (re)start searches.") -(defvar-local deadgrep--running nil - "If non-nil, a search is still running.") - -(defvar-local deadgrep--debug-command nil) -(put 'deadgrep--debug-command 'permanent-local t) -(defvar-local deadgrep--debug-first-output nil) -(put 'deadgrep--debug-first-output 'permanent-local t) - -(defvar-local deadgrep--imenu-alist nil - "Alist that stores filename and position for each matched files. -It is used to create `imenu' index.") - -(defconst deadgrep--position-column-width 5) - -(defconst deadgrep--color-code - (rx "\x1b[" (+ digit) "m") - "Regular expression for an ANSI color code.") - -(defvar deadgrep--incremental-active nil) - -(defun deadgrep--insert-output (output &optional finished) - "Propertize OUTPUT from rigrep and write to the current buffer." - ;; If we had an unfinished line from our last call, include that. - (when deadgrep--remaining-output - (setq output (concat deadgrep--remaining-output output)) - (setq deadgrep--remaining-output nil)) - - (let ((inhibit-read-only t) - (lines (s-lines output)) - prev-line-num) - ;; Process filters run asynchronously, and don't guarantee that - ;; OUTPUT ends with a complete line. Save the last line for - ;; later processing. - (unless finished - (setq deadgrep--remaining-output (-last-item lines)) - (setq lines (butlast lines))) - - (save-excursion - (goto-char (point-max)) - (dolist (line lines) - (cond - ;; Ignore blank lines. - ((s-blank? line)) - ;; Lines of just -- are used as a context separator when - ;; calling ripgrep with context flags. - ((string= line "--") - (let ((separator "--")) - ;; Try to make the separator length match the previous - ;; line numbers. - (when prev-line-num - (setq separator - (s-repeat (log prev-line-num 10) "-"))) - (insert - (propertize (concat separator "\n") - 'face 'deadgrep-meta-face - 'deadgrep-separator t)))) - ;; If we have a warning or don't have a color code, ripgrep - ;; must be complaining about something (e.g. zero matches for - ;; a glob, or permission denied on some directories). - ((or - (s-starts-with-p "WARNING: " line) - (not (s-matches-p deadgrep--color-code line))) - (when deadgrep--current-file - (setq deadgrep--current-file nil) - (insert "\n")) - (insert line "\n\n")) - (t - (-let* ((truncate-p (> (length line) deadgrep-max-line-length)) - (line - (if truncate-p - (substring line 0 deadgrep-max-line-length) - line)) - ((filename line-num content) (deadgrep--split-line line)) - (formatted-line-num - (s-pad-right deadgrep--position-column-width " " - (number-to-string line-num))) - (pretty-line-num - (propertize formatted-line-num - 'face 'deadgrep-meta-face - 'deadgrep-filename filename - 'deadgrep-line-number line-num - 'read-only t - 'front-sticky t - 'rear-nonsticky t)) - (pretty-filename - (propertize filename - 'face 'deadgrep-filename-face - 'deadgrep-filename filename - 'read-only t - 'front-sticky t))) - (cond - ;; This is the first file we've seen, print the heading. - ((null deadgrep--current-file) - (push (cons filename (point)) deadgrep--imenu-alist) - (insert pretty-filename "\n")) - ;; This is a new file, print the heading with a spacer. - ((not (equal deadgrep--current-file filename)) - (push (cons filename (1+ (point))) deadgrep--imenu-alist) - (insert "\n" pretty-filename "\n"))) - (setq deadgrep--current-file filename) - - ;; TODO: apply the invisible property if the user decided - ;; to hide this filename before we finished finding - ;; results in it. - (insert pretty-line-num content) - (when truncate-p - (insert - (propertize " ... (truncated)" - 'face 'deadgrep-meta-face))) - (insert "\n") - - (setq prev-line-num line-num)))))))) - -(defcustom deadgrep-finished-hook nil - "Hook run when `deadgrep' search is finished." - :type 'hook - :group 'deadgrep) - -(defun deadgrep--process-sentinel (process output) - "Update the deadgrep buffer associated with PROCESS as complete." - (let ((buffer (process-buffer process)) - (finished-p (string= output "finished\n"))) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (setq deadgrep--running nil) - ;; rg has terminated, so stop the spinner. - (spinner-stop deadgrep--spinner) - - (deadgrep--insert-output "" finished-p) - - ;; Report any errors that occurred. - (unless (member output - (list - "exited abnormally with code 1\n" - "interrupt\n" - "finished\n")) - (save-excursion - (let ((inhibit-read-only t)) - (goto-char (point-max)) - (insert output)))) - - (run-hooks 'deadgrep-finished-hook) - (unless deadgrep--incremental-active - (message "Deadgrep finished")))))) - -(defun deadgrep--process-filter (process output) - ;; Searches may see a lot of output, but it's really useful to have - ;; a snippet of output when debugging. Store the first output received. - (unless deadgrep--debug-first-output - (setq deadgrep--debug-first-output output)) - - ;; If we had an unfinished line from our last call, include that. - (when deadgrep--remaining-output - (setq output (concat deadgrep--remaining-output output)) - (setq deadgrep--remaining-output nil)) - - (when (buffer-live-p (process-buffer process)) - (with-current-buffer (process-buffer process) - (deadgrep--insert-output output)))) - -(defun deadgrep--extract-regexp (pattern s) - "Search for PATTERN in S, and return the content of the first group." - (string-match pattern s) - (match-string 1 s)) - -(defconst deadgrep--filename-regexp - (rx bos "\x1b[0m\x1b[3" (or "5" "6") "m" - (? "./") - (group (+? anything)) - "\x1b[") - "Extracts the filename from a ripgrep line with ANSI color sequences. -We use the color sequences to extract the filename exactly, even -if the path contains colons.") - -(defconst deadgrep--line-num-regexp - (rx "\x1b[32m" (group (+ digit))) - "Extracts the line number from a ripgrep line with ANSI color sequences. -Ripgrep uses a unique color for line numbers, so we use that to -extract the linue number exactly.") - -(defconst deadgrep--line-contents-regexp - (rx "\x1b[32m" (+ digit) "\x1b[0m" (or ":" "-") (group (* anything))) - "Extract the line contents from a ripgrep line with ANSI color sequences. -Use the unique color for line numbers to ensure we start at the -correct colon. - -Note that the text in the group will still contain color codes -highlighting which parts matched the user's search term.") - -(defconst deadgrep--hit-regexp - (rx-to-string - `(seq - ;; A reset color code. - "\x1b[0m" - ;; Two color codes, bold and color (any order). - (regexp ,deadgrep--color-code) - (regexp ,deadgrep--color-code) - ;; The actual text. - (group (+? anything)) - ;; A reset color code again. - "\x1b[0m")) - "Extract the portion of a line found by ripgrep that matches the user's input. -This may occur multiple times in one line.") - -(defun deadgrep--split-line (line) - "Split out the components of a raw LINE of output from rg. -Return the filename, line number, and the line content with ANSI -color codes replaced with string properties." - (list - (deadgrep--extract-regexp deadgrep--filename-regexp line) - (string-to-number - (deadgrep--extract-regexp deadgrep--line-num-regexp line)) - (deadgrep--propertize-hits - (deadgrep--extract-regexp deadgrep--line-contents-regexp line)))) - -(defun deadgrep--escape-backslash (s) - "Escape occurrences of backslashes in S. - -This differs from `regexp-quote', which outputs a regexp pattern. -Instead, we provide a string suitable for REP in -`replace-regexp-in-string'." - (s-replace "\\" "\\\\" s)) - -(defun deadgrep--propertize-hits (line-contents) - "Given LINE-CONTENTS from ripgrep, replace ANSI color codes -with a text face property `deadgrep-match-face'." - (replace-regexp-in-string - deadgrep--hit-regexp - (lambda (s) - (propertize - (deadgrep--escape-backslash (match-string 1 s)) - 'face 'deadgrep-match-face)) - line-contents)) - -(define-button-type 'deadgrep-search-term - 'action #'deadgrep--search-term - 'help-echo "Change search term") - -(defun deadgrep--search-prompt (&optional default) - "The prompt shown to the user when starting a deadgrep search." - (let ((kind (if (eq deadgrep--search-type 'regexp) - "by regexp" "for text"))) - (if default - (format "Search %s (default %s): " kind default) - (format "Search %s: " kind)))) - -(defun deadgrep--search-term (_button) - (deadgrep-search-term)) - -(defun deadgrep-search-term () - "Change the current search term and restart the search." - (interactive) - (setq deadgrep--search-term - (read-from-minibuffer - (deadgrep--search-prompt) - deadgrep--search-term)) - (rename-buffer - (deadgrep--buffer-name deadgrep--search-term default-directory) t) - (deadgrep-restart)) - -(define-button-type 'deadgrep-type - 'action #'deadgrep--search-type - 'search-type nil - 'help-echo "Change search type") - -(defun deadgrep--search-type (button) - (setq deadgrep--search-type (button-get button 'search-type)) - (deadgrep-restart)) - -(define-button-type 'deadgrep-case - 'action #'deadgrep--case - 'case nil - 'help-echo "Change case sensitivity") - -(defun deadgrep--case (button) - (setq deadgrep--search-case (button-get button 'case)) - (deadgrep-restart)) - -(define-button-type 'deadgrep-context - 'action #'deadgrep--context - 'context nil - 'help-echo "Show/hide context around match") - -(defun deadgrep--context (button) - ;; deadgrep--context takes the value of (before . after) when set. - (setq deadgrep--context - (cl-case (button-get button 'context) - ((nil) - nil) - (before - (cons - (read-number "Show N lines before: ") - (or (cdr-safe deadgrep--context) 0))) - (after - (cons - (or (car-safe deadgrep--context) 0) - (read-number "Show N lines after: "))) - (t - (error "Unknown context type")))) - - (deadgrep-restart)) - -(defun deadgrep--type-list () - "Query the rg executable for available file types." - (let* ((output (with-output-to-string - (with-current-buffer standard-output - (process-file-shell-command - (format "%s --type-list" deadgrep-executable) - nil '(t nil))))) - (lines (s-lines (s-trim output))) - (types-and-globs - (--map - (s-split (rx ": ") it) - lines))) - (-map - (-lambda ((type globs)) - (list type (s-split (rx ", ") globs))) - types-and-globs))) - -(define-button-type 'deadgrep-file-type - 'action #'deadgrep--file-type - 'case nil - 'help-echo "Change file type") - -(defun deadgrep--format-file-type (file-type extensions) - (let* ((max-exts 4) - (truncated (> (length extensions) max-exts))) - (when truncated - (setq extensions - (append (-take max-exts extensions) - (list "...")))) - (format "%s (%s)" - file-type - (s-join ", " extensions)))) - -(defun deadgrep--glob-regexp (glob) - "Convert GLOB pattern to the equivalent elisp regexp." - (let* ((i 0) - (result "^")) - (while (< i (length glob)) - (let* ((char (elt glob i))) - (cond - ;; . matches a literal . in globs. - ((eq char ?.) - (setq result (concat result "\\.")) - (cl-incf i)) - ;; ? matches a single char in globs. - ((eq char ??) - (setq result (concat result ".")) - (cl-incf i)) - ;; * matches zero or more of anything. - ((eq char ?*) - (setq result (concat result ".*")) - (cl-incf i)) - ;; [ab] matches a literal a or b. - ;; [a-z] matches characters between a and z inclusive. - ;; [?] matches a literal ?. - ((eq char ?\[) - ;; Find the matching ]. - (let ((j (1+ i))) - (while (and (< j (length glob)) - (not (eq (elt glob j) ?\]))) - (cl-incf j)) - (cl-incf j) - (setq result (concat result - (substring glob i j))) - (setq i j))) - (t - (setq result (concat result (char-to-string char))) - (cl-incf i))))) - (concat result "$"))) - -(defun deadgrep--matches-globs-p (filename globs) - "Return non-nil if FILENAME matches any glob pattern in GLOBS." - (when filename - (--any (string-match-p (deadgrep--glob-regexp it) filename) - globs))) - -(defun deadgrep--relevant-file-type (filename types-and-globs) - "Try to find the most relevant item in TYPES-AND-GLOBS for FILENAME." - (let (;; Find all the items in TYPES-AND-GLOBS whose glob match - ;; FILENAME. - (matching (-filter (-lambda ((_type globs)) - (deadgrep--matches-globs-p filename globs)) - types-and-globs))) - (->> matching - ;; Prefer longer names, so "markdown" over "md" for the type - ;; name. - (-sort (-lambda ((type1 _) (type2 _)) - (< (length type1) (length type2)))) - ;; Prefer types with more extensions, as they tend to be more - ;; common languages (e.g. 'ocaml' over 'ml'). - (-sort (-lambda ((_ globs1) (_ globs2)) - (< (length globs1) (length globs2)))) - ;; But prefer elisp over lisp for .el files. - (-sort (-lambda ((type1 _) (type2 _)) - ;; Return t if we're comparing elisp with lisp, nil - ;; otherwise. - (and (equal type1 "lisp") - (equal type2 "elisp")))) - ;; Take the highest scoring matching. - (-last-item)))) - -(defun deadgrep--read-file-type (filename) - "Read a ripgrep file type, defaulting to the type that matches FILENAME." - (let* (;; Get the list of types we can offer. - (types-and-globs (deadgrep--type-list)) - ;; Build a list mapping the formatted types to the type name. - (type-choices - (-map - (-lambda ((type globs)) - (list - (deadgrep--format-file-type type globs) - type)) - types-and-globs)) - ;; Work out the default type name based on the filename. - (default-type-and-globs - (deadgrep--relevant-file-type filename types-and-globs)) - (default - (-when-let ((default-type default-globs) default-type-and-globs) - (deadgrep--format-file-type default-type default-globs))) - ;; Prompt the user for a file type. - (chosen - (completing-read - "File type: " type-choices nil t nil nil default))) - (nth 1 (assoc chosen type-choices)))) - -(defun deadgrep--file-type (button) - (let ((button-type (button-get button 'file-type))) - (cond - ((eq button-type 'all) - (setq deadgrep--file-type 'all)) - ((eq button-type 'type) - (let ((new-file-type - (deadgrep--read-file-type deadgrep--initial-filename))) - (setq deadgrep--file-type (cons 'type new-file-type)))) - ((eq button-type 'glob) - (let* ((initial-value - (cond - ;; If we already have a glob pattern, edit it. - ((eq (car-safe deadgrep--file-type) 'glob) - (cdr deadgrep--file-type)) - ;; If the initial file had a file name of the form - ;; foo.bar, offer *.bar as the initial glob. - ((and deadgrep--initial-filename - (file-name-extension deadgrep--initial-filename)) - (format "*.%s" - (file-name-extension deadgrep--initial-filename))) - (t - "*"))) - (prompt - (if (string= initial-value "*") - ;; Show an example to avoid confusion with regexp syntax. - "Glob (e.g. *.js): " - "Glob: ")) - (glob - (read-from-minibuffer - prompt - initial-value))) - (setq deadgrep--file-type (cons 'glob glob)))) - (t - (error "Unknown button type: %S" button-type)))) - (deadgrep-restart)) - -(define-button-type 'deadgrep-directory - 'action #'deadgrep--directory - 'help-echo "Change base directory") - -(defun deadgrep--directory (_button) - (deadgrep-directory)) - -(defun deadgrep-directory () - "Prompt the user for a new search directory, then restart the search." - (interactive) - (setq default-directory - (expand-file-name - (read-directory-name "Search files in: "))) - (rename-buffer - (deadgrep--buffer-name deadgrep--search-term default-directory)) - (deadgrep-restart)) - -(defun deadgrep-parent-directory () - "Restart the search in the parent directory." - (interactive) - (setq default-directory - (file-name-directory (directory-file-name default-directory))) - (rename-buffer - (deadgrep--buffer-name deadgrep--search-term default-directory)) - (deadgrep-restart)) - -(defun deadgrep--button (text type &rest properties) - ;; `make-text-button' mutates the string to add properties, so copy - ;; TEXT first. - (setq text (substring-no-properties text)) - (apply #'make-text-button text nil :type type properties)) - -(defun deadgrep--arguments (search-term search-type case context) - "Return a list of command line arguments that we can execute in a shell -to obtain ripgrep results." - (let (args) - (push "--color=ansi" args) - (push "--line-number" args) - (push "--no-heading" args) - (push "--no-column" args) - (push "--with-filename" args) - - (cond - ((eq search-type 'string) - (push "--fixed-strings" args)) - ((eq search-type 'words) - (push "--fixed-strings" args) - (push "--word-regexp" args)) - ((eq search-type 'regexp)) - (t - (error "Unknown search type: %s" search-type))) - - (cond - ((eq case 'smart) - (push "--smart-case" args)) - ((eq case 'sensitive) - (push "--case-sensitive" args)) - ((eq case 'ignore) - (push "--ignore-case" args)) - (t - (error "Unknown case: %s" case))) - - (cond - ((eq deadgrep--file-type 'all)) - ((eq (car-safe deadgrep--file-type) 'type) - (push (format "--type=%s" (cdr deadgrep--file-type)) args)) - ((eq (car-safe deadgrep--file-type) 'glob) - (push (format "--type-add=custom:%s" (cdr deadgrep--file-type)) args) - (push "--type=custom" args)) - (t - (error "Unknown file-type: %S" deadgrep--file-type))) - - (when context - (push (format "--before-context=%s" (car context)) args) - (push (format "--after-context=%s" (cdr context)) args)) - - (push "--" args) - (push search-term args) - (push "." args) - - (nreverse args))) - -(defun deadgrep--write-heading () - "Write the deadgrep heading with buttons reflecting the current -search settings." - (let ((start-pos (point)) - (inhibit-read-only t)) - (insert (propertize "Search term: " - 'face 'deadgrep-meta-face) - (if (eq deadgrep--search-type 'regexp) - (deadgrep--propertize-regexp deadgrep--search-term) - (propertize - deadgrep--search-term - 'face 'deadgrep-search-term-face)) - " " - (deadgrep--button "change" 'deadgrep-search-term) - "\n" - (propertize "Search type: " - 'face 'deadgrep-meta-face) - - (if (eq deadgrep--search-type 'string) - "string" - (deadgrep--button "string" 'deadgrep-type - 'search-type 'string)) - " " - (if (eq deadgrep--search-type 'words) - "words" - (deadgrep--button "words" 'deadgrep-type - 'search-type 'words)) - " " - (if (eq deadgrep--search-type 'regexp) - "regexp" - (deadgrep--button "regexp" 'deadgrep-type - 'search-type 'regexp)) - "\n" - (propertize "Case: " - 'face 'deadgrep-meta-face) - (if (eq deadgrep--search-case 'smart) - "smart" - (deadgrep--button "smart" 'deadgrep-case - 'case 'smart)) - " " - (if (eq deadgrep--search-case 'sensitive) - "sensitive" - (deadgrep--button "sensitive" 'deadgrep-case - 'case 'sensitive)) - " " - (if (eq deadgrep--search-case 'ignore) - "ignore" - (deadgrep--button "ignore" 'deadgrep-case - 'case 'ignore)) - "\n" - (propertize "Context: " - 'face 'deadgrep-meta-face) - (if deadgrep--context - (deadgrep--button "none" 'deadgrep-context - 'context nil) - "none") - " " - (deadgrep--button "before" 'deadgrep-context - 'context 'before) - (if deadgrep--context - (format ":%d" (car deadgrep--context)) - "") - " " - (deadgrep--button "after" 'deadgrep-context - 'context 'after) - (if deadgrep--context - (format ":%d" (cdr deadgrep--context)) - "") - - "\n\n" - (propertize "Directory: " - 'face 'deadgrep-meta-face) - (deadgrep--button - (abbreviate-file-name default-directory) - 'deadgrep-directory) - (if (get-text-property 0 'deadgrep-overridden default-directory) - (propertize " (from override)" 'face 'deadgrep-meta-face) - "") - "\n" - (propertize "Files: " - 'face 'deadgrep-meta-face) - (if (eq deadgrep--file-type 'all) - "all" - (deadgrep--button "all" 'deadgrep-file-type - 'file-type 'all)) - " " - (deadgrep--button "type" 'deadgrep-file-type - 'file-type 'type) - (if (eq (car-safe deadgrep--file-type) 'type) - (format ":%s" (cdr deadgrep--file-type)) - "") - " " - (deadgrep--button "glob" 'deadgrep-file-type - 'file-type 'glob) - (if (eq (car-safe deadgrep--file-type) 'glob) - (format ":%s" (cdr deadgrep--file-type)) - "") - "\n\n") - (put-text-property - start-pos (point) - 'read-only t) - (put-text-property - start-pos (point) - 'front-sticky t))) - -;; TODO: could we do this in the minibuffer too? -(defun deadgrep--propertize-regexp (regexp) - "Given a string REGEXP representing a search term with regular -expression syntax, highlight the metacharacters. -Returns a copy of REGEXP with properties set." - (setq regexp (copy-sequence regexp)) - - ;; See https://docs.rs/regex/1.0.0/regex/#syntax - (let ((metachars - ;; Characters that don't match themselves. - '(?\( ?\) ?\[ ?\] ?\{ ?\} ?| ?. ?+ ?* ?? ?^ ?$)) - ;; Characters that have special regexp meaning when preceded - ;; with a backslash. This includes things like \b but not - ;; things like \n. - (escape-metachars - '(?A ?b ?B ?d ?D ?p ?s ?S ?w ?W ?z)) - (prev-char nil)) - ;; Put the standard search term face on every character - ;; individually. - (dotimes (i (length regexp)) - (put-text-property - i (1+ i) - 'face 'deadgrep-search-term-face - regexp)) - ;; Put the metacharacter face on any character that isn't treated - ;; literally. - (--each-indexed (string-to-list regexp) - (cond - ;; Highlight everything between { and }. - ((and (eq it ?\{) (not (equal prev-char ?\\))) - (let ((closing-pos it-index)) - ;; TODO: we have loops like this in several places, factor - ;; out. - (while (and (< closing-pos (length regexp)) - (not (eq (elt regexp closing-pos) - ?\}))) - (cl-incf closing-pos)) - ;; Step over the closing }, if we found one. - (unless (= closing-pos (length regexp)) - (cl-incf closing-pos)) - (put-text-property - it-index closing-pos - 'face - 'deadgrep-regexp-metachar-face - regexp))) - ;; Highlight individual metachars. - ((and (memq it metachars) (not (equal prev-char ?\\))) - (put-text-property - it-index (1+ it-index) - 'face - 'deadgrep-regexp-metachar-face - regexp)) - ((and (memq it escape-metachars) (equal prev-char ?\\)) - (put-text-property - (1- it-index) (1+ it-index) - 'face 'deadgrep-regexp-metachar-face - regexp))) - - (setq prev-char it))) - regexp) - -(defun deadgrep--buffer-name (search-term directory) - ;; TODO: Handle buffers already existing with this name. - (format "*deadgrep %s %s*" - (s-truncate 30 search-term) - (abbreviate-file-name directory))) - -(defun deadgrep--buffers () - "All the current deadgrep results buffers. -Returns a list ordered by the most recently accessed." - (--filter (with-current-buffer it - (eq major-mode 'deadgrep-mode)) - ;; `buffer-list' seems to be ordered by most recently - ;; visited first. - (buffer-list))) - -(defun deadgrep--buffer (search-term directory initial-filename) - "Create and initialise a search results buffer." - (let* ((buf-name (deadgrep--buffer-name search-term directory)) - (buf (get-buffer buf-name))) - (if buf - ;; There was already a buffer with this name. Reset its search - ;; state. - (with-current-buffer buf - (deadgrep--stop-and-reset)) - ;; We need to create the buffer, ensure we don't exceed - ;; `deadgrep-max-buffers' by killing the least recently used. - (progn - (when (numberp deadgrep-max-buffers) - (let* ((excess-buffers (-drop (1- deadgrep-max-buffers) - (deadgrep--buffers)))) - ;; Kill buffers so we have one buffer less than the maximum - ;; before we create a new one. - (-each excess-buffers #'kill-buffer))) - - (setq buf (get-buffer-create buf-name)))) - - (with-current-buffer buf - (setq default-directory directory) - (let ((inhibit-read-only t)) - ;; This needs to happen first, as it clobbers all buffer-local - ;; variables. - (deadgrep-mode) - (erase-buffer) - - (setq deadgrep--search-term search-term) - (setq deadgrep--current-file nil) - (setq deadgrep--initial-filename initial-filename)) - (setq buffer-read-only t)) - buf)) - -(defun deadgrep-cycle-search-type () - (interactive) - (cond - ((eq deadgrep--search-type 'string) (setq deadgrep--search-type 'words)) - ((eq deadgrep--search-type 'words) (setq deadgrep--search-type 'regexp)) - ((eq deadgrep--search-type 'regexp) (setq deadgrep--search-type 'string))) - (deadgrep-restart)) - -(defun deadgrep-cycle-search-case () - (interactive) - (cond - ((eq deadgrep--search-case 'smart) (setq deadgrep--search-case 'sensitive)) - ((eq deadgrep--search-case 'sensitive) (setq deadgrep--search-case 'ignore)) - ((eq deadgrep--search-case 'ignore) (setq deadgrep--search-case 'smart))) - (deadgrep-restart)) - -(defvar deadgrep-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "RET") #'deadgrep-visit-result) - (define-key map (kbd "o") #'deadgrep-visit-result-other-window) - ;; TODO: we should still be able to click on buttons. - - (define-key map (kbd "S") #'deadgrep-search-term) - (define-key map (kbd "T") #'deadgrep-cycle-search-type) - (define-key map (kbd "C") #'deadgrep-cycle-search-case) - (define-key map (kbd "D") #'deadgrep-directory) - (define-key map (kbd "^") #'deadgrep-parent-directory) - (define-key map (kbd "g") #'deadgrep-restart) - (define-key map (kbd "I") #'deadgrep-incremental) - - ;; TODO: this should work when point is anywhere in the file, not - ;; just on its heading. - (define-key map (kbd "TAB") #'deadgrep-toggle-file-results) - - ;; Keybinding chosen to match `kill-compilation'. - (define-key map (kbd "C-c C-k") #'deadgrep-kill-process) - - (define-key map (kbd "n") #'deadgrep-forward) - (define-key map (kbd "p") #'deadgrep-backward) - (define-key map (kbd "N") #'deadgrep-forward-match) - (define-key map (kbd "P") #'deadgrep-backward-match) - (define-key map (kbd "M-n") #'deadgrep-forward-filename) - (define-key map (kbd "M-p") #'deadgrep-backward-filename) - - map) - "Keymap for `deadgrep-mode'.") - -(defvar deadgrep-edit-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "RET") #'deadgrep-visit-result) - map) - "Keymap for `deadgrep-edit-mode'.") - -(define-derived-mode deadgrep-mode special-mode - '("Deadgrep" (:eval (spinner-print deadgrep--spinner))) - "Major mode for deadgrep results buffers." - (remove-hook 'after-change-functions #'deadgrep--propagate-change t)) - -(defun deadgrep--find-file (path) - "Open PATH in a buffer, and return a cons cell -\(BUF . OPENED). OPENED is nil if there was already a buffer for -this path." - (let* ((initial-buffers (buffer-list)) - (opened nil) - ;; Skip running find-file-hook since it may prompt the user. - (find-file-hook nil) - ;; If we end up opening a buffer, don't bother with file - ;; variables. It prompts the user, and we discard the buffer - ;; afterwards anyway. - (enable-local-variables nil) - ;; Bind `auto-mode-alist' to nil, so we open the buffer in - ;; `fundamental-mode' if it isn't already open. - (auto-mode-alist nil) - ;; Use `find-file-noselect' so we still decode bytes from the - ;; underlying file. - (buf (find-file-noselect path))) - (unless (-contains-p initial-buffers buf) - (setq opened t)) - (cons buf opened))) - -(defun deadgrep--propagate-change (beg end length) - "Repeat the last modification to the results buffer in the -underlying file." - ;; We should never be called outside an edit buffer, but be - ;; defensive. Buggy functions in change hooks are painful. - (when (eq major-mode 'deadgrep-edit-mode) - (save-mark-and-excursion - (goto-char beg) - (-let* ((column (+ (deadgrep--current-column) length)) - (filename (deadgrep--filename)) - (line-number (deadgrep--line-number)) - ((buf . opened) (deadgrep--find-file filename)) - (inserted (buffer-substring beg end))) - (with-current-buffer buf - (save-mark-and-excursion - (save-restriction - (widen) - (goto-char - (deadgrep--buffer-position line-number column)) - (delete-char (- length)) - (insert inserted))) - ;; If we weren't visiting this file before, just save it and - ;; close it. - (when opened - (basic-save-buffer) - (kill-buffer buf))))))) - -(defcustom deadgrep-edit-mode-hook nil - "Called after `deadgrep-edit-mode' is turned on." - :type 'hook - :group 'deadgrep) - -(defun deadgrep-edit-mode () - "Major mode for editing the results files directly from a -deadgrep results buffer. - -\\{deadgrep-edit-mode-map}" - (interactive) - (unless (eq major-mode 'deadgrep-mode) - (user-error "deadgrep-edit-mode only works in deadgrep result buffers")) - (when deadgrep--running - (user-error "Can't edit a results buffer until the search is finished")) - ;; We deliberately don't use `define-derived-mode' here because we - ;; want to check the previous value of `major-mode'. Initialise the - ;; major mode manually. - (delay-mode-hooks - (kill-all-local-variables) - (setq major-mode 'deadgrep-edit-mode) - (setq mode-name - '(:propertize "Deadgrep:Edit" face mode-line-emphasis)) - (use-local-map deadgrep-edit-mode-map) - ;; Done major mode manual initialise (copied from `define-derived-mode'). - - ;; Allow editing, and propagate changes. - (setq buffer-read-only nil) - (add-hook 'after-change-functions #'deadgrep--propagate-change nil t) - - (message "Now editing, use `M-x deadgrep-mode' when finished")) - - (run-mode-hooks 'deadgrep-edit-mode-hook)) - -(defun deadgrep--current-column () - "Get the current column position in char terms. -This treats tabs as 1 and ignores the line numbers in the results -buffer." - (let* ((line-start (line-beginning-position)) - (line-number - (get-text-property line-start 'deadgrep-line-number)) - (line-number-width - (max deadgrep--position-column-width - (length (number-to-string line-number)))) - (char-count 0)) - (save-excursion - (while (not (equal (point) line-start)) - (cl-incf char-count) - (backward-char 1))) - (max - (- char-count line-number-width) - 0))) - -(defun deadgrep--flash-column-offsets (start end) - "Temporarily highlight column offset from START to END." - (let* ((line-start (line-beginning-position)) - (overlay (make-overlay - (+ line-start start) - (+ line-start end)))) - (overlay-put overlay 'face 'highlight) - (run-with-timer 1.0 nil 'delete-overlay overlay))) - -(defun deadgrep--match-face-p (pos) - "Is there a match face at POS?" - (eq (get-text-property pos 'face) 'deadgrep-match-face)) - -(defun deadgrep--match-positions () - "Return a list of indexes of the current line's matches." - (let (positions) - (save-excursion - (beginning-of-line) - - (let* ((line-number - (get-text-property (point) 'deadgrep-line-number)) - (line-number-width - (max deadgrep--position-column-width - (length (number-to-string line-number)))) - (i 0) - (start-pos 0) - (line-end-pos (line-end-position))) - - (forward-char line-number-width) - - (while (<= (point) line-end-pos) - ;; If we've just entered a match, record the start position. - (when (and (deadgrep--match-face-p (point)) - (not (deadgrep--match-face-p (1- (point))))) - (setq start-pos i)) - ;; If we've just left a match, record the match range. - (when (and (not (deadgrep--match-face-p (point))) - (deadgrep--match-face-p (1- (point)))) - (push (list start-pos i) positions)) - - (setq i (1+ i)) - (forward-char 1)))) - - (nreverse positions))) - -(defun deadgrep--buffer-position (line-number column-offset) - "Return the position equivalent to LINE-NUMBER at COLUMN-OFFSET -in the current buffer." - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (forward-line (1- line-number)) - (forward-char column-offset) - - (point)))) - -(defun deadgrep--filename (&optional pos) - "Get the filename of the result at point POS. -If POS is nil, use the beginning position of the current line." - (get-text-property (or pos (line-beginning-position)) 'deadgrep-filename)) - -(defun deadgrep--line-number () - "Get the filename of the result at point." - (get-text-property (line-beginning-position) 'deadgrep-line-number)) - -(defun deadgrep--visit-result (open-fn) - "Goto the search result at point." - (interactive) - (let* ((pos (line-beginning-position)) - (file-name (deadgrep--filename)) - (line-number (deadgrep--line-number)) - (column-offset (when line-number (deadgrep--current-column))) - (match-positions (when line-number (deadgrep--match-positions)))) - (when file-name - (when overlay-arrow-position - (set-marker overlay-arrow-position nil)) - ;; Show an arrow next to the last result viewed. This is - ;; consistent with `compilation-next-error-function' and also - ;; useful with `deadgrep-visit-result-other-window'. - (setq overlay-arrow-position (copy-marker pos)) - - (funcall open-fn file-name) - (goto-char (point-min)) - - (when line-number - (-let [destination-pos (deadgrep--buffer-position - line-number column-offset)] - ;; Put point on the position of the match, widening the - ;; buffer if necessary. - (when (or (< destination-pos (point-min)) - (> destination-pos (point-max))) - (widen)) - (goto-char destination-pos) - - ;; Temporarily highlight the parts of the line that matched - ;; the search term. - (-each match-positions - (-lambda ((start end)) - (deadgrep--flash-column-offsets start end)))))))) - -(defun deadgrep-visit-result-other-window () - "Goto the search result at point, opening in another window." - (interactive) - (deadgrep--visit-result #'find-file-other-window)) - -(defun deadgrep-visit-result () - "Goto the search result at point." - (interactive) - (deadgrep--visit-result #'find-file)) - -(defvar-local deadgrep--hidden-files nil - "An alist recording which files currently have their lines -hidden in this deadgrep results buffer. - -Keys are interned filenames, so they compare with `eq'.") - -(defun deadgrep-toggle-file-results () - "Show/hide the results of the file at point." - (interactive) - (let* ((file-name (deadgrep--filename)) - (line-number (deadgrep--line-number))) - (when (and file-name (not line-number)) - ;; We're on a file heading. - (if (alist-get (intern file-name) deadgrep--hidden-files) - (deadgrep--show) - (deadgrep--hide))))) - -(defun deadgrep--show () - (-let* ((file-name (deadgrep--filename)) - ((start-pos end-pos) (alist-get (intern file-name) deadgrep--hidden-files))) - (remove-overlays start-pos end-pos 'invisible t) - (setf (alist-get (intern file-name) deadgrep--hidden-files) - nil))) - -(defun deadgrep--hide () - "Hide the file results immediately after point." - (save-excursion - (let* ((file-name (deadgrep--filename)) - (start-pos - (progn - (forward-line) - (point))) - (end-pos - (progn - (while (and - (or (get-text-property (point) 'deadgrep-line-number) - (get-text-property (point) 'deadgrep-separator)) - (not (bobp))) - (forward-line)) - ;; Step over the newline. - (1+ (point)))) - (o (make-overlay start-pos end-pos))) - (overlay-put o 'invisible t) - (setf (alist-get (intern file-name) deadgrep--hidden-files) - (list start-pos end-pos))))) - -(defun deadgrep--interrupt-process () - "Gracefully stop the rg process, synchronously." - (-when-let (proc (get-buffer-process (current-buffer))) - ;; Ensure that our process filter is not called again. - (set-process-filter proc #'ignore) - - (interrupt-process proc) - ;; Wait for the process to terminate, so we know that - ;; `deadgrep--process-sentinel' has been called. - (while (process-live-p proc) - ;; `redisplay' can trigger process filters or sentinels. - (redisplay) - (sleep-for 0.1)))) - -(defun deadgrep-kill-process () - "Kill the deadgrep process associated with the current buffer." - (interactive) - (if (get-buffer-process (current-buffer)) - (deadgrep--interrupt-process) - (message "No process running."))) - -(defun deadgrep--item-p (pos) - "Is there something at POS that we can interact with?" - (or (button-at pos) - (deadgrep--filename pos))) - -(defun deadgrep--filename-p (pos) - "Is there a filename at POS that we can interact with?" - (eq (get-text-property pos 'face) 'deadgrep-filename-face)) - -(defun deadgrep--move (forward-p) - "Move to the next item. -This will either be a button, a filename, or a search result." - (interactive) - (let ((pos (point))) - ;; If point is initially on an item, move past it. - (while (and (deadgrep--item-p pos) - (if forward-p - (< pos (point-max)) - (> pos (point-min)))) - (if forward-p - (cl-incf pos) - (cl-decf pos))) - ;; Find the next item. - (while (and (not (deadgrep--item-p pos)) - (if forward-p - (< pos (point-max)) - (> pos (point-min)))) - (if forward-p - (cl-incf pos) - (cl-decf pos))) - ;; Regardless of direction, ensure point is at the beginning of - ;; the item. - (while (and (if forward-p - (< pos (point-max)) - (> pos (point-min))) - (deadgrep--item-p (1- pos))) - (cl-decf pos)) - ;; If we reached an item (we aren't at the first/last item), then - ;; go to it. - (when (deadgrep--item-p pos) - (goto-char pos)))) - -(defun deadgrep-forward () - "Move forward to the next item. -This will either be a button, a filename, or a search result. See -also `deadgrep-forward-match'." - (interactive) - (deadgrep--move t)) - -(defun deadgrep-backward () - "Move backward to the previous item. -This will either be a button, a filename, or a search result. See -also `deadgrep-backward-match'." - (interactive) - (deadgrep--move nil)) - -(defun deadgrep-forward-filename () - "Move forward to the next filename." - (interactive) - (deadgrep--move-match t 'deadgrep-filename-face)) - -(defun deadgrep-backward-filename () - "Move backward to the previous filename." - (interactive) - (deadgrep--move-match nil 'deadgrep-filename-face)) - -(defun deadgrep--move-match (forward-p face) - "Move point to the beginning of the next/previous match." - (interactive) - (let ((start-pos (point))) - ;; Move over the current match, if we were already on one. - (while (eq (get-text-property (point) 'face) - face) - (if forward-p (forward-char) (backward-char))) - (condition-case nil - (progn - ;; Move point to the next match, which may be on the same line. - (while (not (eq (get-text-property (point) 'face) - face)) - (if forward-p (forward-char) (backward-char))) - ;; Ensure point is at the beginning of the match. - (unless forward-p - (while (eq (get-text-property (point) 'face) - face) - (backward-char)) - (forward-char))) - ;; Don't move point beyond the last match. However, it's still - ;; useful to signal that we're at the end, so users can use this - ;; command with macros and terminate when it's done. - (beginning-of-buffer - (goto-char start-pos) - (signal 'beginning-of-buffer nil)) - (end-of-buffer - (goto-char start-pos) - (signal 'end-of-buffer nil))))) - -(defun deadgrep-forward-match () - "Move point forward to the beginning of next match. -Note that a result line may contain more than one match, or zero -matches (if the result line has been truncated)." - (interactive) - (deadgrep--move-match t 'deadgrep-match-face)) - -(defun deadgrep-backward-match () - "Move point backward to the beginning of previous match." - (interactive) - (deadgrep--move-match nil 'deadgrep-match-face)) - -(defun deadgrep--start (search-term search-type case) - "Start a ripgrep search." - (setq deadgrep--spinner (spinner-create 'progress-bar t)) - (setq deadgrep--running t) - (spinner-start deadgrep--spinner) - (let* ((args (deadgrep--arguments - search-term search-type case - deadgrep--context)) - (process - (apply #'start-file-process - (format "rg %s" search-term) - (current-buffer) - deadgrep-executable - args))) - (setq deadgrep--debug-command - (format "%s %s" deadgrep-executable (s-join " " args))) - (set-process-filter process #'deadgrep--process-filter) - (set-process-sentinel process #'deadgrep--process-sentinel))) - -(defun deadgrep--stop-and-reset () - "Terminate the current search and reset any search state." - ;; Stop the old search, so we don't carry on inserting results from - ;; the last thing we searched for. - (deadgrep--interrupt-process) - - (let ((inhibit-read-only t)) - ;; Reset UI: remove results, reset items hidden by TAB, and arrow - ;; position. - (erase-buffer) - (setq deadgrep--hidden-files nil) - (when overlay-arrow-position - (set-marker overlay-arrow-position nil)) - - ;; Reset intermediate search state. - (setq deadgrep--current-file nil) - (setq deadgrep--spinner nil) - (setq deadgrep--remaining-output nil) - (setq deadgrep--current-file nil) - (setq deadgrep--debug-first-output nil) - (setq deadgrep--imenu-alist nil))) - -(defun deadgrep-restart () - "Re-run ripgrep with the current search settings." - (interactive) - ;; If we haven't started yet, start the search if we've been called - ;; by the user. - (when (and deadgrep--postpone-start - (called-interactively-p 'interactive)) - (setq deadgrep--postpone-start nil)) - - (deadgrep--stop-and-reset) - - (let ((start-point (point)) - (inhibit-read-only t)) - (deadgrep--write-heading) - ;; If the point was in the heading, ensure that we restore its - ;; position. - (goto-char (min (point-max) start-point)) - - (if deadgrep--postpone-start - (deadgrep--write-postponed) - (deadgrep--start - deadgrep--search-term - deadgrep--search-type - deadgrep--search-case)))) - -(defun deadgrep--read-search-term () - "Read a search term from the minibuffer. -If region is active, return that immediately. Otherwise, prompt -for a string, offering the current word as a default." - (let (search-term) - (if (use-region-p) - (progn - (setq search-term - (buffer-substring-no-properties (region-beginning) (region-end))) - (deactivate-mark)) - (let* ((sym (symbol-at-point)) - (sym-name (when sym - (substring-no-properties (symbol-name sym)))) - ;; TODO: prompt should say search string or search regexp - ;; as appropriate. - (prompt - (deadgrep--search-prompt sym-name))) - (setq search-term - (read-from-minibuffer - prompt nil nil nil 'deadgrep-history sym-name)) - (when (equal search-term "") - (setq search-term sym-name)))) - (unless (equal (car deadgrep-history) search-term) - (push search-term deadgrep-history)) - search-term)) - -(defun deadgrep-incremental () - (interactive) - (catch 'break - (let ((deadgrep--incremental-active t) - (search-term (or deadgrep--search-term ""))) - (while t - (let ((next-char - (read-char - ;; TODO: Use the same prompt format as other search options. - (format "%s %s" - (apply #'propertize "Incremental Search (RET when done):" minibuffer-prompt-properties) - search-term)))) - (cond - ((eq next-char ?\C-m) - (throw 'break nil)) - ((eq next-char ?\C-?) - (setq search-term (s-left -1 search-term))) - (t - (setq search-term (concat search-term (list next-char)))))) - (when (> (length search-term) 2) - (setq deadgrep--search-term search-term) - (deadgrep-restart)))))) - - -(defun deadgrep--normalise-dirname (path) - "Expand PATH and ensure that it doesn't end with a slash. -If PATH is remote path, it is not expanded." - (directory-file-name (if (file-remote-p path) - path - (let (file-name-handler-alist) - (expand-file-name path))))) - -(defun deadgrep--lookup-override (path) - "If PATH is present in `deadgrep-project-root-overrides', -return the overridden value. -Otherwise, return PATH as is." - (let* ((normalised-path (deadgrep--normalise-dirname path)) - (override - (-first - (-lambda ((original . _)) - (equal (deadgrep--normalise-dirname original) normalised-path)) - deadgrep-project-root-overrides))) - (when override - (setq path (cdr override)) - (unless (stringp path) - (user-error "Bad override: expected a path string, but got: %S" path)) - (setq path (propertize path 'deadgrep-overridden t))) - path)) - -(defun deadgrep--project-root () - "Guess the project root of the given FILE-PATH." - (let ((root default-directory) - (project (project-current))) - (when project - (cond ((fboundp 'project-root) - ;; This function was defined in Emacs 28. - (setq root (project-root project))) - (t - ;; Older Emacsen. - (-when-let (roots (project-roots project)) - (setq root (car roots)))))) - (when root - (deadgrep--lookup-override root)))) - -(defun deadgrep--write-postponed () - "Write a message to the current buffer informing the user that -deadgrep is ready but not yet searching." - (let* ((inhibit-read-only t) - (restart-key - (where-is-internal #'deadgrep-restart deadgrep-mode-map t))) - (save-excursion - (goto-char (point-max)) - (insert - (format "Press %s to start the search." - (key-description restart-key)))))) - -(defun deadgrep--create-imenu-index () - "Create `imenu' index for matched files." - (when deadgrep--imenu-alist - (list (cons "Files" (reverse deadgrep--imenu-alist))))) - -;;;###autoload -(defun deadgrep (search-term &optional directory) - "Start a ripgrep search for SEARCH-TERM in DIRECTORY. - -If not provided, DIR defaults to the directory as determined by -`deadgrep-project-root-function'. - -See also `deadgrep-project-root-overrides'. - -If called with a prefix argument, create the results buffer but -don't actually start the search." - (interactive (list (deadgrep--read-search-term))) - (let* ((dir (or directory - (funcall deadgrep-project-root-function))) - (buf (deadgrep--buffer - search-term - dir - (or deadgrep--initial-filename - (buffer-file-name)))) - (last-results-buf (car-safe (deadgrep--buffers))) - prev-search-type - prev-search-case) - ;; Find out what search settings were used last time. - (when last-results-buf - (with-current-buffer last-results-buf - (setq prev-search-type deadgrep--search-type) - (setq prev-search-case deadgrep--search-case))) - - (funcall deadgrep-display-buffer-function buf) - - (with-current-buffer buf - (setq imenu-create-index-function #'deadgrep--create-imenu-index) - (setq next-error-function #'deadgrep-next-error) - - ;; If we have previous search settings, apply them to our new - ;; search results buffer. - (when last-results-buf - (setq deadgrep--search-type prev-search-type) - (setq deadgrep--search-case prev-search-case)) - - (deadgrep--write-heading) - - (if current-prefix-arg - ;; Don't start the search, just create the buffer and inform - ;; the user how to start when they're ready. - (progn - (setq deadgrep--postpone-start t) - (deadgrep--write-postponed)) - ;; Start the search immediately. - (deadgrep--start - search-term - deadgrep--search-type - deadgrep--search-case))))) - -(defun deadgrep-next-error (arg reset) - "Move to the next error. -If ARG is given, move by that many errors. - -This is intended for use with `next-error-function', which see." - (when reset - (goto-char (point-min))) - (beginning-of-line) - (let ((direction (> arg 0))) - (setq arg (abs arg)) - - (while (and - (not (zerop arg)) - (not (eobp))) - (if direction - (forward-line 1) - (forward-line -1)) - ;; If we are on a specific result (not a heading), we have a line - ;; number. - (when (get-text-property (point) 'deadgrep-line-number) - (cl-decf arg)))) - (deadgrep-visit-result-other-window)) - -(defun deadgrep-debug () - "Show a buffer with some debug information about the current search." - (interactive) - (unless (eq major-mode 'deadgrep-mode) - (user-error "deadgrep-debug should be run in a deadgrep results buffer")) - - (let ((command deadgrep--debug-command) - (output deadgrep--debug-first-output) - (buf (get-buffer-create "*deadgrep debug*")) - (inhibit-read-only t)) - (pop-to-buffer buf) - (erase-buffer) - (special-mode) - (setq buffer-read-only t) - - (insert - (propertize - "About your environment:\n" - 'face 'deadgrep-filename-face) - (format "Platform: %s\n" system-type) - (format "Emacs version: %s\n" emacs-version) - (format "Command: %s\n" command) - (format "default-directory: %S\n" default-directory) - (format "exec-path: %S\n" exec-path) - (if (boundp 'tramp-remote-path) - (format "tramp-remote-path: %S\n" tramp-remote-path) - "") - (propertize - "\nInitial output from ripgrep:\n" - 'face 'deadgrep-filename-face) - (format "%S" output) - (propertize - "\n\nPlease file bugs at https://github.com/Wilfred/deadgrep/issues/new" - 'face 'deadgrep-filename-face)))) - -(defun deadgrep-kill-all-buffers () - "Kill all open deadgrep buffers." - (interactive) - (dolist (buffer (deadgrep--buffers)) - (kill-buffer buffer))) - -(provide 'deadgrep) -;;; deadgrep.el ends here - -;; Local Variables: -;; byte-compile-warnings: (not obsolete) -;; End: blob - 1f7c4b790168032cdf15e155f3d3f3419b908ce0 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/.dir-locals.el +++ /dev/null @@ -1,4 +0,0 @@ -;;; Directory Local Variables -;;; For more information see (info "(emacs) Directory Variables") - -((emacs-lisp-mode . ((indent-tabs-mode . nil)))) blob - 619d1542450db444eb03d4af7ce133cd953fdd1b (mode 644) blob + /dev/null --- elpa/denote-2.3.5/CHANGELOG.org +++ /dev/null @@ -1,4558 +0,0 @@ -#+title: Change log of Denote -#+author: Protesilaos Stavrou -#+email: info@protesilaos.com -#+language: en -#+options: ':t toc:nil author:t email:t num:t -#+startup: content - -This document contains the release notes for each tagged commit on the -project's main git repository: . - -The newest release is at the top. For further details, please consult -the manual: . - -#+toc: headlines 1 insert TOC here, with one headline level - -* Version 2.3.0 on 2024-03-24 -:PROPERTIES: -:CUSTOM_ID: h:e9d3ebdb-8a69-47a9-a5a2-619abc44b7d2 -:END: - -This release brings a host of user-facing refinements to an already -stable base, as well as some impressive new features. There is a lot -to cover, so take your time reading these notes. - -Special thanks to Jean-Philippe Gagné Guay for the numerous -refinements to parts of the code base. Some of these are not directly -visible to users, but are critical regardless. In the interest of -brevity, I will not be covering the most technical parts here. I -mention Jean-Philippe's contributions at the outset for this reason. -Though the Git commit log is there for interested parties to study -things further. - -** Check out the ~denote-explore~ package by Peter Prevos -:PROPERTIES: -:CUSTOM_ID: h:3e49dd9d-59db-40e5-9116-ce678231b08d -:END: - -This package provides several neat extensions that help you make -better sense of your knowledge base, while keeping it in good order. -The ~denote-explore~ package has commands to summarise the usage of -keywords, visualise connections between notes, spot infrequently used -keywords, and jump to previous historical entries. - -- Git repository: . -- Documentation: . - -Now on to Denote version =2.3.0=! - -** Link to a heading inside a Denote Org file -:PROPERTIES: -:CUSTOM_ID: h:ca7baf4f-04af-4467-a1e6-20403357280f -:END: - -Denote creates links to files by using their unique identifier. As Org -provides the =CUSTOM_ID= property for per-heading identifiers, we now -leverage this infrastructure to compose links that point to a file and -then to a heading therein. This only works for Org, as no other plain -text major mode has a concept of heading identifiers (and it is not -Denote's job to create such a feature). - -I demonstrated the functionality in a video: - - -Technically, the =denote:= link type has the same implementation -details as Org's standard =file:= and has always had this potential to -jump to a section inside the given file. - -*** The ~denote-org-store-link-to-heading~ user option -:PROPERTIES: -:CUSTOM_ID: h:a7864660-5b4c-4467-a252-9140baedeb1a -:END: - -The user option ~denote-org-store-link-to-heading~ determines whether -~org-store-link~ links to the current Org heading (such links are -merely "stored" and need to be inserted afterwards with the command -~org-insert-link~). Note that the ~org-capture~ command uses the -~org-link~ internally if it has to store a link. - -When its value is non-nil, ~org-store-link~ stores a link to the -current Org heading inside the Denote Org file. If the heading does -not have a =CUSTOM_ID=, it creates it and includes it in the heading's -=PROPERTIES= drawer. If a =CUSTOM_ID= exists, ~org-store-link~ use it -as-is. - -This makes the resulting link a combination of the =denote:= link type, -pointing to the identifier of the current file, plus the value of the -heading's =CUSTOM_ID=, such as: - -- =[[denote:20240118T060608][Some test]]= -- =[[denote:20240118T060608::#h:eed0fb8e-4cc7-478f-acb6-f0aa1a8bffcd][Some test::Heading text]]= - -Both lead to the same Denote file, but the latter jumps to the heading -with the given =CUSTOM_ID=. Notice that the link to the heading also -has a different description, which includes the heading text. - -The value of the =CUSTOM_ID= is determined by the Org user option -~org-id-method~. The sample shown above uses the default UUID -infrastructure. - -If ~denote-org-store-link-to-heading~ is set to a nil value, the -command ~org-store-link~ only stores links to the Denote file (using -its identifier), but not to the given heading. This is what Denote was -doing in all versions prior to =2.3.0=. - -Thanks to Kristoffer Balintona for discussing with me how -~org-capture~ interfaces with ~org-store-link~. I updated the -documentation accordingly. This was done in issue 267: -. - -*** Insert link to an Org file with a further pointer to a heading -:PROPERTIES: -:CUSTOM_ID: h:dd054536-8d20-4251-b23d-77fec7d7d036 -:END: - -As part of the optional =denote-org-extras.el= extension that comes -with the ~denote~ package, the command ~denote-org-extras-link-to-heading~ -prompts for a link to an Org file and then asks for a heading therein, -using minibuffer completion. Once the user provides input at the two -prompts, the command inserts a link at point which has the following -pattern: =[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading text]]=. - -Because only Org files can have links to individual headings, the -command ~denote-org-extras-link-to-heading~ prompts only for Org files -(i.e. files which include the =.org= extension). Remember that Denote -works with many file types. - -This feature is similar to the concept of the aforementioned user -option ~denote-org-store-link-to-heading~. It is, however, interactive -and differs in the directionality of the action. With that user -option, the command ~org-store-link~ will generate a =CUSTOM_ID= for -the current heading (or capture the value of one as-is), giving the -user the option to then call ~org-insert-link~ wherever they see fit. -By contrast, the command ~denote-org-extras-link-to-heading~ prompts -for a file, then a heading, and inserts the link at point. - -** Refinements galore to minibuffer prompts -:PROPERTIES: -:CUSTOM_ID: h:e509402b-a58f-4a10-b364-b158b31d1ee5 -:END: - -*** All commands that affect file names conform with ~denote-prompts~ -:PROPERTIES: -:CUSTOM_ID: h:11f0fc1e-552b-4a02-bf01-9d8508ce68c8 -:END: - -The scope of the ~denote-prompts~ user option is broadened to make it -more useful. In the past, this variable would only affect the -behaviour of the ~denote~ command. For example, the user would make -the command prompt for a subdirectory, then keywords, then a title. -But all other commands were not following this setting, as they were -hardcoding the prompts for title and keywords. - -Take the ~denote-subdirectory~ command as an example. It would first -prompt for a subdirectory to place the new note in, then for a title, -and then for keywords. Whereas now, it prepends the =subdirectory= -prompt to the list of ~denote-prompts~. So if the user has configured -their ~denote-prompts~ to, for example, ask for a signature and a file -type, the ~denote-subdirectory~ will do just that with the addition of -the =subdirectory= prompt. - -Same idea for all commands that either create or modify file names, -wherever conformity with ~denote-prompts~ makes sense. For example, -the ~denote-rename-file~ will never ask for a =subdirectory= because -our renaming policy is to always rename in place (to avoid -mistakes---you can always move the file afterwards). - -This also means that the ~denote-rename-file~ and its multi-file -counterpart, ~denote-dired-rename-files~, will only prompt for a -signature if it is part of the ~denote-prompts~. Whereas in the -previous version this was unconditional, thus burdening users who do -not need the =SIGNATURE= file name component (more about renaming -further into the release notes). - -Lots of Git commits went into this redesign, per my initiave in issue -247: . Thanks to -Vedang Manerikar for the changes to the convenience wrappers of the -~denote~ command (like ~denote-subdirectory~), which were done in pull -request 248: . - -Vedang has assigned copyright to the Free Software Foundation. - -Also thanks to Max Brieiev for joining the technical discussion -therein. - -The renaming commands are more intuitive now, which addresses a -discussion point raised by user babusri in issue 204: -. - -*** A simple tweak for more informative minibuffer prompts -:PROPERTIES: -:CUSTOM_ID: h:a502217d-8eff-4a6f-b66a-33e5e7ecda9d -:END: - -The text of each prompt now has all capital letters for the word -referencing its scope of its application, like =TITLE=, =KEYWORDS=, -=SIGNATURE=. The idea is to make it easier to quickly scan the text, -especially while working through multiple prompts. For example, the -prompt for a title now reads: - -: New file TITLE: - -This paradigm is followed by all prompts. It is a small yet effective -tweak to get a better sense of context. - -*** The file prompt uses relative names once again -:PROPERTIES: -:CUSTOM_ID: h:8f182ad3-c97f-45dc-a451-c552f2a7957c -:END: - -In previous versions of Denote, the minibuffer prompt to pick a file -(such as a file to link to) would show relative file names: the name -without the full file system path. The functionality depended on the -built-in =project.el= library, which did not allow us to do everything -we wanted with our prompts, such as to have a dedicated minibuffer -history or to easily enable the workflow of commands like -~denote-open-or-create~. - -In the previous version, I made the decision to remove the -=project.el= dependency and the concomitant presentation of relative -names in order to add the functionality we want. I did it with the -intention to find a better solution down the line. Et voilá! Relative -file names are back. We now have all the functionality we need. Sorry -if in the meantime you had to deal with those longer names! It was a -necessary intermediate arrangement for the greater good. - -For the technicalities, refer to the source code of the function -~denote-title-prompt~. - -*** Completion using previous inputs is now optional -:PROPERTIES: -:CUSTOM_ID: h:bcf382e4-bd00-49f3-859a-3f86e9770b77 -:END: - -All our minibuffer prompts have their dedicated history (you can -persist histories with the built-in ~savehist-mode~). They store -previous values, giving the user easy access to their past input -values. Some of our commands not only record a history, but also -leverage it to provide completion. These commands are named in the -variable ~denote-prompts-with-history-as-completion~. As of this -writing, they are: - -- ~denote-title-prompt~ -- ~denote-signature-prompt~ -- ~denote-files-matching-regexp-prompt~ - -Users who do not want to use completion for those can set the new user -option ~denote-history-completion-in-prompts~ to a nil value. - -** Renaming files got better all-round -:PROPERTIES: -:CUSTOM_ID: h:747e126a-b966-4ac8-a8ec-cf900012e37e -:END: - -One of the pillars of the ~denote~ package is its ability to rename -any file to use the efficient Denote file-naming scheme (makes file -names predictable and easy to retrieve even with rudimentary tools). -To this end, we provide several commands that affect file names, -beside the commands that create new files. - -As noted above, the commands which rename files to follow the Denote -file-naming scheme now conform with the user option ~denote-prompts~, -but there is more! - -*** A broadened scope for the ~denote-rename-no-confirm~ option -:PROPERTIES: -:CUSTOM_ID: h:f93b8075-de2d-416e-9275-7225d03678ad -:END: - -The implementation of this user option is redone (i) to save the -underlying buffer outright if the user does not want to provide their -confirmation for a rename each time and (ii) to cover all relevant -commands that perform a rename operation. The assumption is that the -user who opts in to this feature is familiar with the Denote renaming -modalities and knows they are reliable. - -The default is still the same: Denote always asks for confirmation -before renaming a file, showing the difference between the old and new -names, as well as any changes to the file's contents. In this light, -buffers are not saved to give the user the chance to further inspect -the changes (such as by running ~diff-buffer-with-file~). - -Commands that will now skip all confirmation prompts to rename the file -and, where relevant, save the corresponding buffer outright: - -- ~denote-rename-file~ -- ~denote-dired-rename-files~ -- ~denote-dired-rename-marked-files-with-keywords~ -- ~denote-rename-file-using-front-matter~ -- ~denote-rename-add-keywords~ -- ~denote-rename-remove-keywords~ -- ~denote-rename-add-signature~ (new, more below) -- ~denote-rename-remove-signature~ (new, more below) - -*** Rename a file by adding or removing a =SIGNATURE= component -:PROPERTIES: -:CUSTOM_ID: h:01ab0277-b4d4-433e-bd25-b9a0357412f6 -:END: - -The =SIGNATURE= is an optional free-form field that is part of a -Denote file name. A common use-case is to write sequence notes with -it, though Denote does not enforce any particular convention (you may -prefer to have it as a special kind of keyword for certain files that -simply stands out more due to its placement). - -[ Besides, the ~denote-sort-dired~ command lets you filter and sort - files while putting them in a fully fledged Dired buffer, so - manually sequencing notes via their signature may not be needed. ] - -We now provide two commands to add or remove a signature from file -names: - -- The ~denote-rename-add-signature~ prompts for a file and a - signature. The default value for the file prompt is the file of the - currently open buffer or the file-at-point in a Dired buffer. The - signature is an ordinary string, defaulting to the selected file's - signature, if any. - -- The ~denote-rename-remove-signature~ uses the same file prompt as - above. It performs its action only if the selected file has a - signature. Otherwise, it does nothing. - -Files that do not have a Denote file name are renamed accordingly. -Though for such cases it is better to use ~denote-rename-file~ or -~denote-dired-rename-files~ as they are more general. - -*** Use the ~denote-after-rename-file-hook~ for optional post-rename operations -:PROPERTIES: -:CUSTOM_ID: h:57f4f60c-7873-4542-a7a5-5c997cdbd137 -:END: - -All renaming commands run the ~denote-after-rename-file-hook~ after a -successful operation. This is meant for users who want to do something -specific after the renaming is done. - -** More optional features of the =denote-org-extras.el= -:PROPERTIES: -:CUSTOM_ID: h:a0a2753e-5be9-4776-9f3f-e3b7556c13c1 -:END: - -I already covered the ~denote-org-extras-link-to-heading~, though the -file =denote-org-extras.el= has some more optional goodies for those -who work with Org files. - -*** Create a note from the current Org subtree -:PROPERTIES: -:CUSTOM_ID: h:fbf1e574-e9aa-4c67-8034-27341d7a5536 -:END: - -In Org parlance, an entry with all its subheadings and other contents -is a "subtree". Denote can operate on the subtree to extract it from -the current file and create a new file out of it. One such workflow is -to collect thoughts in a single document and produce longer standalone -notes out of them upon review. - -The command ~denote-org-extras-extract-org-subtree~ (part of the -optional =denote-org-extras.el= extension) is used for this purpose. -It creates a new Denote note using the current Org subtree. In doing -so, it removes the subtree from its current file and moves its -contents into a new file. - -The text of the subtree's heading becomes the =#+title= of the new -note. Everything else is inserted as-is. - -Read the documentation string of ~denote-org-extras-extract-org-subtree~ -or consult the manual for further details. - -*** Convert =denote:= links to =file:= links -:PROPERTIES: -:CUSTOM_ID: h:042e26e8-e3e0-4c57-9855-6b363671ae9a -:END: - -Sometimes the user needs to translate all =denote:= link types to -their =file:= equivalent. This may be because some other tool does not -recognise =denote:= links (or other custom links types---which are a -standard feature of Org, by the way). The user thus needs to (i) -either make a copy of their Denote note or edit the existing one, and -(ii) convert all links to the generic =file:= link type that -external/other programs understand. - -The optional extension =denote-org-extras.el= contains two commands -that are relevant for this use-case: - -+ Convert =denote:= links to =file:= links :: The command - ~denote-org-extras-convert-links-to-file-type~ goes through the - buffer to find all =denote:= links. It gets the identifier of the - link and resolves it to the actual file system path. It then - replaces the match so that the link is written with the =file:= type - and then the file system path. The optional search terms and/or link - description are preserved. - -+ Convert =file:= links to =denote:= links :: The command - ~denote-org-extras-convert-links-to-denote-type~ behaves like the - one above. The difference is that it finds the file system path and - converts it into its identifier. - -*** The Denote Org dynamic blocks are now in =denote-org-extras.el= -:PROPERTIES: -:CUSTOM_ID: h:51d72c47-d434-4954-98d6-2db7a7ea6812 -:END: - -As part of this version, all our dynamic blocks are defined in the -file =denote-org-extras.el=. The file which once contained these block -definitions, =denote-org-dblock.el=, now only has aliases for the new -function names and dipslays a warning about its deprecation. - -There is no need to ~require~ the ~denote-org-extras~ feature because -all of Denote's Org dynamic blocks are autoloaded (meaning that they -work as soon as they are used). For backward compatibility, all -dynamic blocks retain their original names as an alias for the newer -one. - -We will not remove =denote-org-dblock.el= anytime soon to avoid any -potential breakage with people's existing notes. Though if you are new -to this functionality, you better avoid the deprecated symbols. - -*** Org dynamic block to only insert missing links -:PROPERTIES: -:CUSTOM_ID: h:45176e63-c609-40f6-a11d-1cc0c28460dd -:END: - -The =denote-missing-links= block is available with the command -~denote-org-extras-dblock-insert-missing-links~. It is like the -=denote-links= block (documented at length in the manual), except it -only lists links to files that are not present in the current buffer. -The parameters are otherwise the same: - -: #+BEGIN: denote-missing-links :regexp "YOUR REGEXP HERE" :sort-by-component nil :reverse-sort nil :id-only nil -: -: #+END: - -Remember to type =C-c C-x C-u= (~org-dblock-update~) with point on the -=#+BEGIN= line to update the block. - -This brings back a feature that was deprecated in version 2.2.0, but -makes changes to it so that (i) it is more limited in scope and (ii) -available as a standalone Org dynamic block. - -Thanks to Stephen R. Kifer, Peter Prevos, and Elias Storms for the -discussion which made it clear to me that users do have a need for -such functionality. This was done in the now-defunct mailing list: -. - -Also thanks to Vedang Manerikar for fixing an edge case bug. This was -done in pull request 260: . - -Org dynamic blocks are a powerful feature which also showcases how far -we can go with Denote's efficient file-naming scheme. - -** Quality-of-life improvements -:PROPERTIES: -:CUSTOM_ID: h:08f27f36-0ed2-4a5e-b02b-f0075c6e904f -:END: - -Here I include other changes we made to existing functionality. - -*** BREAKING User-defined sluggification of file name components -:PROPERTIES: -:CUSTOM_ID: h:240b80e7-242c-46fb-83d2-1ba36bdcaf66 -:END: - -In the previous version, we introduced the user option -~denote-file-name-letter-casing~. This was used to control the letter -casing of file name components, but was ultimately not flexible enough -for our purposes. We are thus retiring it and replacing it with the -more powerful, but also more advanced, user option -~denote-file-name-slug-functions~. - -For existing users of the deprecated functionality, you can still -preserve the input of a prompt verbatim with something like this: - -#+begin_src emacs-lisp -(setq denote-file-name-slug-functions - '((title . denote-sluggify-title) - (keyword . identity) - (signature . denote-sluggify-signature))) -#+end_src - -The manual explains the details and shows ready-to-use code samples. - -Remember that deviating from the default file-naming scheme of Denote -will make things harder to use in the future, as files will have -permutations that create uncertainty. The sluggification scheme and -concomitant restrictions we impose by default are there for a very -good reason: they are the distillation of years of experience. Here we -give you what you wish, but bear in mind it may not be what you need. -You have been warned. - -Thanks to Jean-Philippe Gagné Guay for introducing this variable, -among other tweaks, in pull request 217: . -Jean-Philippe has assigned copyright to the Free Software Foundation. - -*** Option to automatically save the buffer of a new note -:PROPERTIES: -:CUSTOM_ID: h:3e1249f1-ac26-4187-9ddd-7391b4e5131f -:END: - -The user option ~denote-save-buffer-after-creation~ controls whether -commands that create new notes save their buffer right away. - -The default behaviour of commands such as ~denote~ (or related) is to -not save the buffer they create. This gives the user the chance to -review the text before writing it to a file. The user may choose to -delete the unsaved buffer, thus not creating a new file on disk. - -If ~denote-save-buffer-after-creation~ is set to a non-nil value, such -buffers are saved automatically and so the file is written to disk. - -*** The ~denote-menu-bar-mode~ and the placement of the Denote submenu -:PROPERTIES: -:CUSTOM_ID: h:c8336927-cf6b-4770-b041-123bf9186e57 -:END: - -The command ~denote-menu-bar-mode~ toggles the inclusion of the -submenu with the Denote entries in the Emacs menu bar (which is on -display when ~menu-bar-mode~ is enabled). - -This submenu is now shown after the =Tools= entry. - -Thanks to Joseph Turner for sending me the relevant patches. Joseph -has assigned copyright to the Free Software Foundation. - -*** The =C-c C-o= works in ~markdown-mode~ for Denote links -:PROPERTIES: -:CUSTOM_ID: h:1c884b19-7ab7-4eb5-a332-815d25f7373c -:END: - -In files whose major mode is ~markdown-mode~, the default key binding -=C-c C-o= (which calls the command ~markdown-follow-thing-at-point~) -correctly resolves =denote:= links. This method works in addition to -the =RET= key, which is made available by the buttonization that we -also provide. Interested users can refer to the function -~denote-link-markdown-follow~ for the implementation details. - -Thanks to user pmenair for noting a case where this was breaking -general Markdown linking functionality. This was done in issue 290: -. - -*** More fine-grained control of Denote faces for dates/identifiers -:PROPERTIES: -:CUSTOM_ID: h:c6f739ef-ea26-41b8-84e6-c87c4622cdba -:END: - -We now define more faces for fine-grained control of the identifier in -Dired. Thanks to mentalisttraceur for suggesting the idea in issue -276: . - -Before you ask, no, none of my themes will cover those faces because -extra colouration is something only the user can decide if they want -or not. In the above link I provide a sample with a screenshot (apart -from the ~modus-themes~, my ~ef-themes~ and ~standard-themes~ have -similar functionality): - -#+begin_src emacs-lisp -(defun my-modus-themes-denote-faces (&rest _) - (modus-themes-with-colors - (custom-set-faces - `(denote-faces-year ((,c :foreground ,cyan))) - `(denote-faces-month ((,c :foreground ,magenta-warmer))) - `(denote-faces-day ((,c :foreground ,cyan))) - `(denote-faces-time-delimiter ((,c :foreground ,fg-main))) - `(denote-faces-hour ((,c :foreground ,magenta-warmer))) - `(denote-faces-minute ((,c :foreground ,cyan))) - `(denote-faces-second ((,c :foreground ,magenta-warmer)))))) - -(add-hook 'modus-themes-post-load-hook #'my-modus-themes-denote-faces) -#+end_src - -*** New convenience command for users of the optional =denote-journal-extras.el= -:PROPERTIES: -:CUSTOM_ID: h:9e7bff88-a6ad-45e7-b802-0493153e0e20 -:END: - -The command ~denote-journal-extras-link-or-create-entry~ links to the -journal entry for today or creates it in the background, if missing, -and then links to it from the current file. If there are multiple -journal entries for the same day, it prompts to select one among them -and then links to it. When called with an optional prefix argument -(such as =C-u= with default key bindings), the command prompts for a -date and then performs the aforementioned. With a double prefix -argument (=C-u C-u=), it also produces a link whose description -includes just the file's identifier. - -Thanks to Alan Schmitt for contributing this command, based on -previous discussions. It was done in pull request 243: -. - -** For developers or advanced users -:PROPERTIES: -:CUSTOM_ID: h:03778c8c-60aa-449c-96df-7e41916668a6 -:END: - -These has new parameters or are new symbols altogether. Please read -their respective doc string for the details. - -+ Function ~denote-convert-file-name-keywords-to-crm~. -+ Function ~denote-valid-date-p~. -+ Function ~denote-parse-date~. -+ Function ~denote-retrieve-title-or-filename~. -+ Function ~denote-get-identifier~. -+ Function ~denote-signature-prompt~. -+ Function ~denote-file-prompt~. -+ Function ~denote-keywords-prompt~. -+ Function ~denote-title-prompt~. -+ Function ~denote-rewrite-front-matter~. -+ Function ~denote-rewrite-keywords~. -+ Function ~denote-update-dired-buffers~. -+ Function ~denote-format-string-for-org-front-matter~. -+ Function ~denote-format-string-for-md-front-matter~. -+ Variable ~denote-link-signature-format~. -+ Function ~denote-link-description-with-signature-and-title~. -+ Variable ~denote-link-description-function~. - -** Miscellaneous -:PROPERTIES: -:CUSTOM_ID: h:040f2678-674d-4e99-b428-659cd3a3b7c3 -:END: - -- The ~denote-sort-dired~ function no longer errors out when there is - no match for the given search terms. Thanks to Vedang Manerikar for - the initial patch! This was done in the now-defunct mailing list: - . Further - changes by me. - -- The ~denote-keywords-sort~ function no longer tries to sort keywords - that are not a list. Thanks to Ashton Wiersdorf for the patch. The - change is small. As such, Ashton does not need to assign copyright - to the Free Software Foundation. - -- Documented in the manual that custom convenience commands can be - accessed by the ~denote-command-prompt~. Thanks to Glenna D. for - clarifying the language. - -- The ~denote-user-enforced-denote-directory~ is obsolete. Those who - used it in their custom code can simply ~let~ bind the value of the - variable ~denote-directory~. Thanks to Jean-Philippe Gagné Guay for - making the relevant changes (the Git history is not direct here and - I cannot quickly find the pull request---the commit is =a48a1da=). - -- The ~denote-link-return-links~ no longer keeps buffers around. - Thanks to Matteo Cavada for the patch. This was done in pull request - 252: . The change is - small and so Matteo does not need to assign copyright to the Free - Software Foundation. - -- Thanks to user jarofromel (recorded in Git as "random" author) for - fixing a mismatched parenthesis in ~denote-parse-date~. This was - done in pull request 258: . - -- The ~denote-rename-buffer-mode~ now works as expected with - non-editable files, like PDFs. Thanks to Alan Schmitt for bringing - this matter to my attention and then refining the implementation - details in pull request 268: . - -- All the Denote linking functions can be used from any file outside - the ~denote-directory~ (links are still resolved to files inside the - ~denote-directory~). Thanks to Jean-Philippe Gagné Guay for the - contribution in pull request 236: . - -- We removed all glue code that integrated Denote with the built-in - ~ffap~, ~xref~, and ~project~ libraries. We may reconsider how best - to organise such features in the future. Thanks to Noboru Ota - (nobiot), who originally contributed those extensions, for - suggesting their removal from our code base. We did this by - evaluating all use-cases. The discussion with Noboru happened in - issue 264: . Also - thanks to Jean-Philippe Gagné Guay and Alan Schnmitt for checking - the impact of this on how we generate backlinks. The latest - iteration of this was done in pull request 294, by Jean-Philippe: - . - -- While renaming files, signatures no longer lose consecutive spaces. - Thanks to Wesley Harvey for the contribution in pull request 207: - . The change is - within the ~15 line limit and so Wesley does not need to assign - copyright to the Free Software Foundation. - -- All of the above and lots more are documented at length in the - manual. This is a big task in its own right (as are release notes, - by the way), though it ensures we keep a high standard for the - entire package and can communicate all our knowledge to the user. - -** No more SourceHut -:PROPERTIES: -:CUSTOM_ID: h:9a0d6afc-95e0-490e-a573-5a50fe7bdf28 -:END: - -Development continues on GitHub with GitLab as a mirror. I explained -my reasons here: . - -This is a change that affects all my Emacs packages. - -** Forward guidance for Denote version 3.0.0 -:PROPERTIES: -:CUSTOM_ID: h:61fb340e-5c7c-4a4b-927c-63faf4759a09 -:END: - -We will not any new features until mid-April or a bit later if -necessary. This gives users enough time to report any potential issues -with version =2.3.0=. If there are any bugs, they will be fixed right -away and new minor releases will be introduced (though without release -notes). - -Once we are done with this release cycle, we want to prepare for the -next major version of Denote. The plan is to make the placement of -file name components entirely customisable, among many other power -user features. Though the defaults will remain intact. - -For the immediate future, please prioritise bug reports/fixes. Then -see you around for another round of hacking. The Denote code base is a -pleasure to work with due to how composable everything is. I happy to -make it even better for developers and users alike. - -** Git commits -:PROPERTIES: -:CUSTOM_ID: h:a6fd8e16-ded9-49cf-afbb-6e1373c3c43d -:END: - -Just an overview of what we did. Thanks again to everyone involved. - -#+begin_src sh -~/Git/Projects/denote $ git shortlog 2.2.0..2.3.0 --summary --numbered - 246 Protesilaos Stavrou - 46 Jean-Philippe Gagné Guay - 6 Vedang Manerikar - 3 Joseph Turner - 2 Alan Schmitt - 2 Max - 2 Peter Prevos - 1 Ashton Wiersdorf - 1 Glenna D. - 1 Matteo Cavada - 1 mattyonweb - 1 random - 1 wlharvey4 -#+end_src - -** All contributions are valuable -:PROPERTIES: -:CUSTOM_ID: h:967372fa-933b-40d2-b1a8-546d1a50d35d -:END: - -I encourage you to provide feedback on any of the functionality of the -Denote package. You do not need to be a developer or indeed an expert -in Emacs. When you have an idea in mind on how you use Denote, or you -think something could be done differently, please speak your mind. I -do listen to feedback and am interested in further improving this -package. Everybody is welcome! - -* Version 2.2.0 on 2023-12-10 -:PROPERTIES: -:CUSTOM_ID: h:8efed390-cfa0-420d-b300-0cb76bf2c9f9 -:END: - -The present version covers four broad themes: - -1. Denote rename commands are more user-friendly and featureful. -2. An optional sorting facility makes it possible to produce a - filtered and sorted Dired buffer with Denote files. -3. The optional Denote Org dynamic blocks have received a lot of attention. -4. Bug fixes and internal refinements. - -[ Remember that you do not need to be a programmer to contribute to - Denote. Report a bug, make a suggestion, or just describe how you - want to use this package. Every idea counts and we may implement it - if we can. ] - -** The rename commands can remove a Denote file name component -:PROPERTIES: -:CUSTOM_ID: h:54d803d8-4863-4160-bb2f-3302fb8bff23 -:END: - -The commands we provide to rename files using the Denote file-naming -scheme---~denote-rename-file~, ~denote-dired-rename-files~, and -~denote-dired-rename-marked-files-with-keywords~---can now remove -Denote file name components. This is done by providing an empty string -at the relevant prompt. - -For example, to remove the =TITLE= component from a file called -=20231209T110322==sig--title__keywords.ext= we provide an empty string -at the title prompt. The end result will look something like this: -=20231209T110322==sig__keywords.ext=. - -All prompts now include a hint that leaving them empty will ignore the -given field if it does not exist or remove it if it does exist. - -Note that you must *check how to input an empty string* with your -minibuffer user interface of choice. For instance, with the ~vertico~ -package you can do that with the =M-RET= key binding or by selecting -the prompt line directly (notice the counter showing something like -=*/5= instead of =1/5=). Please make sure to consult the documentation -of the package you are using as this behaviour is not controlled by -Denote. Vertico, and others like it, selects the first candidate if -you type =RET= without any input, which is not the same as an empty -string---it is the first candidate. - -Also read the Denote manual on the matter of [[https://protesilaos.com/emacs/denote#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]. In short, -we use this facility to name all our files, regardless of file type, -in a consistent way that makes them easier to find (I do this with my -videos, for example, and I do it across my filesystem for all personal -files). - -** The file-to-be-renamed is easier to read in the minibuffer -:PROPERTIES: -:CUSTOM_ID: h:69d85d3b-0200-4cc1-baff-9d59aa0ff57b -:END: - -The commands ~denote-rename-file~ and ~denote-dired-rename-files~ -show the name of the file they are operating on in the minibuffer -prompt. This is now produced relative to the current directory, -meaning that instead of =/some/rather/long/path/to/file-name.txt= -Denote only displays =file-name.txt=. - -Our rename commands never move files to another directory, anyway, so -we do not need to remind the user of the entire file system path. - -To make things easier for users/themes, file names highlighted in -Denote prompts are fontified with either of following faces, -depending on the specifics of the case: - -- ~denote-faces-prompt-old-name~ -- ~denote-faces-prompt-new-name~ -- ~denote-faces-prompt-current-name~ - -These faces inherit the attributes of basic faces, so they should look -decent without further tweaks across all themes. - -** Prompts for title, keywords, and signature accept an empty string -:PROPERTIES: -:CUSTOM_ID: h:5897bcc1-4637-4268-8518-8404d939b4b9 -:END: - -The prompts defined by Denote that apply to file name components all -accept an empty string. This has the effect of skipping the given -component. For example, we can create a file without a title and -keywords, with the following sequence of actions (I assume you are -using ~vertico~ for the minibuffer user interface): - -- Type =M-x denote=. -- Type =M-RET= at the title prompt to input an empty string. -- Now type =M-RET= at the keywords prompt for another empty string. - -The resulting file name is something like =20231209T110950.org=. - -** Dired with sorting and filtering -:PROPERTIES: -:CUSTOM_ID: h:05aa437b-2fc8-4e01-ac38-ab77baad83af -:END: - -The new optional =denote-sort.el= library provides facilities to sort -Denote files by any of their file name components. Users can benefit -from this facility to produce a filtered and sorted listing of Denote -files with the command ~denote-sort-dired~. - -~denote-sort-dired~ produces a fully fledged Dired buffer. It asks for a -regular expression that matches file names in the ~denote-directory~. -It then prompts for a sort key and finally checks with the user -whether to reverse the order or not. - -[ Do not be discouraged by the term "regular expression". Ordinary - words work fine. Plus, with Denote's file-naming scheme we have - semantics such as =_keyword=, =-title=, ~=signature~, as explained - in the manual. This is the whole point of using a thoughtful naming - scheme. ] - -The resulting Dired listing is flat, meaning that files inside of -subdirectories are bundled together with those present at the root of -the ~denote-directory~. In this case, files inside of a subdirectory -include the directory component as a prefix. So we have something like -this: - -#+begin_example -test-subdir/20230320T105950--a-new-note__testing.txt -20231202T095629--rename-works-as-intended__one_test_two.org -#+end_example - -I think this is a killer feature, as the fully fledged Dired buffer -allows us to perform all supported operations on our Denote -sorted+filtered files (e.g. change file permissions, move files to -another directory, or open them in an external application). - -I recorded a video to show how this works: -. - -[ Remember that we can rename any file using the Denote file-naming - scheme, meaning that our files can include stuff like PDFs and - videos. Combine this with the concept of "silos", which is covered - in the Denote manual, to organise your long-term storage and - retrieve it efficiently. ] - -** Combine contents of files with an Org dynamic block -:PROPERTIES: -:CUSTOM_ID: h:d41009c1-9833-4b28-8240-9666bfd26559 -:END: - -The new =denote-files= Org dynamic block produces a continuous stream -of file contents. It joins together the contents of files inside the -~denote-directory~ whose name matches the given regular expression. -Optional parameters control whether to include links to those files, -omit their front matter, sort by a given file name component, or tweak -the separator between each file's contents. - -I produced a video to demonstrate the functionality: -. - -Use the command ~denote-org-dblock-insert-files~ to insert such a -block directly at point. Read the Denote manual for the -technicalities: [[https://protesilaos.com/emacs/denote#h:f15fa143-5036-416f-9bff-1bcabbb03456][Org dynamic block to insert file contents]]. - -[ Videos I do will eventually be out-of-date. The manual is the source - of truth. ] - -Bear in mind that this feature is not "transclusion". We are simply -printing a copy of the contents of the files in the current buffer. -Changes made to this copy are not reflected in the original files. - -The =denote-files= Org dynamic block is an excellent way to quickly -collect your thoughts on a given topic. Although dynamic blocks are a -feature of Org, the contents of the files do not need to be in Org -syntax (I write most of my notes in plain text (=.txt=)). - -Thanks to Claudiu Tănăselia for proposing this idea and discussing it -with me. This was done via a private channel and the information is -shared with permission. - -** Sort parameters are used in all Denote Org dynamic blocks -:PROPERTIES: -:CUSTOM_ID: h:7b51fe38-302e-488d-9816-7015a8071ddb -:END: - -All Denote Org dynamic blocks make use of =denote-sort.el= (described -further above). It powers the =:sort-by-component= and =:reverse-sort= -parameters. - -Thanks to Glenna D. for suggesting this feature and discussing it with -me. This was done via a private channel and the information is shared -with permission. It is what inspired me to start work on -=denote-sort.el=, which I then extended to cover Dired, as noted -above. - -** The =:missing-only= parameter is removed from Org dynamic blocks -:PROPERTIES: -:CUSTOM_ID: h:2bd26aef-70ad-4d83-a4ab-c75a893a733a -:END: - -I am removing it because the underlying functionality of -~denote-add-missing-links~ was not always reliable. - -** Files with signature are linked appropriately in Org dynamic blocks -:PROPERTIES: -:CUSTOM_ID: h:144436eb-e674-4052-ac0a-d582b6aa2f53 -:END: - -In general, we provide the command ~denote-link-with-signature~ to let -the user pick a file that has a signature and link to it. The -description of such a link contains the signature text as well as the -file title. The ~denote-link-with-signature~ is distinct from the -standard ~denote-link~, as it allows the user to express intent about -the inclusion of the signature. - -In Org dynamic blocks for links/backlinks, we make this happen -automatically since there can be no manual intervention to express -intent on a link-by-link basis. - -** Fontification in Dired can now extend to subdirectories -:PROPERTIES: -:CUSTOM_ID: h:46e08576-4c17-4b1e-a268-e0223250e7c1 -:END: - -The user option ~denote-dired-directories~ activates the -~denote-dired-mode~ in the specified list of directories when the user -sets this in their init file: - -#+begin_src emacs-lisp -(add-hook 'dired-mode-hook #'denote-dired-mode-in-directories) -#+end_src - -The new user option ~denote-dired-directories-include-subdirectories~ -extends the reach of this feature to all subdirectories thereof. - -Thanks to Henrik Hörmann for discussing this with me and contributing -a patch. This was originally done in pull request 191 on the GitHub -mirror: . Subsequent -refinements by me. - -** Signatures are sluggified as intended -:PROPERTIES: -:CUSTOM_ID: h:73e1efaa-2c22-48ee-be46-072b55177c99 -:END: - -The file name signature component is now sluggified properly. This -means that multiple words are separated by the equals sign, in -accordance with the Denote file-naming scheme where a word separator -is the same as the given field separator (this is the low-tech feature -that makes Denote files so easy to retrieve without fancy extras). - -Vedang Manerikar fixed two relevant bugs in the "rename" commands, -while I rewrote internal functions and tests in the interest of consistency. Vedang's patches: . - -[ The "signature" is a free form component of the file name. Users can - add anything they want there, such as to use it as a "category" that - is different from "tags/keywords", or to introduce sequences in - their notes, or to just have an extra marker for files they need to - spot quickly. ] - -** For developers -:PROPERTIES: -:CUSTOM_ID: h:79f2fd7e-d5a7-4c78-bce7-f8d21e86e32c -:END: - -There is a section in the manual titled "For developers or advanced -users". There we document functions or variables that are -public-facing, meaning that we test and document their behaviour and -encourage others to use them for code they write on top of Denote. -Refer to this section if you are looking to extend Denote. Though you -can also just check the source code, which is designed to be readable -and hackable. - -- The ~denote-directory-files~ function gains new functionality that - subsumes that of the now-deprecated functions - ~denote-directory-files-matching-regexp~, ~denote-all-files~, - ~denote-directory-text-only-files~. Thanks to Jean-Philippe Gagné - Guay for the contribution, which was done in pull request 195 on the - GitHub mirror: . - -- The font-lock keywords we define are consolidated into a single - variable: ~denote-faces-file-name-keywords~ instead of being split - into two variables. This means that we cover all our fontification - needs in the backlinks buffer as well as the ~denote-dired-mode~ - with this one point of entry. It also works for ~denote-sort-dired~, - which can include files with their subdirectory component in the - same flat listing. - -- Use the function ~denote-retrieve-filename-keywords~ to extract - keywords from the file name alone, without going into the file - contents. - -- The ~denote-retrieve-filename-title~ function now returns an empty - string if no title is present. Its behaviour is thus consistent with - ~denote-retrieve-filename-keywords~ and ~denote-retrieve-filename-signature~. - -- The ~denote-retrieve-filename-title~ will now use the - ~file-name-base~ function as a fallback subject to a non-nil - optional argument. This case come into effect when the file does not - have a title component. The new optional argument allows the caller - to handle such cases as they see fit. - -- The ~denote-signature-prompt~ and ~denote-title-prompt~ functions - accept an optional =DEFAULT-SIGNATURE= or =DEFAULT-TITLE= argument. - Internally, this is used as the =INITIAL-INPUT= of ~completing-read~ - instead of the =DEF= argument. This matters because we want the - prompt to return an empty string if there is no input, whereas the - presence of =DEF= means that =DEF= is returned when the prompt is - empty. - -- All our functions that interactively match file names with a regular - expression now use the ~denote-files-matching-regexp-prompt~ - function. When called from Lisp, it takes a =REGEXP= argument as - well as an optional =PROMPT-TEXT=. - -For the purposes of this release cycle, I am not documenting the -points of entry provided by =denote-sort.el=. It is a new feature that -I may eventually incorporate in =denote.el=. If you are interested in -the functionality (e.g. to have more elaborate sorting algorithms), -please take a look at the source code and then let us discuss the -implementation details. - -** Miscellaneous -:PROPERTIES: -:CUSTOM_ID: h:ce5c7865-9ec1-49ba-9388-5a251ab56735 -:END: - -- Rewrote the manual on the topic of Org dynamic blocks. Same idea for - practically the entirety of =denote-org-dblock.el=. - -- Marked the interactive specification of a few commands with the - major mode they belong to. This means that =M-X= (note the capital - X), which calls ~execute-extended-command-for-buffer~ by default, - will only show those commands in the relevant context. - -- Made internal refinements and simplified the implementation of a few - functions. This is important work to keep the code base clean and - easy to read/maintain. Thanks to Jean-Philippe Gagné Guay for the - contribution. It was done in pull request 193 on the GitHub mirror: - . - -- Improved the doc string of the ~denote-format-file-name~ function. - Also introduced a unit test for it to be sure it does what we expect - (I eventually want to have tests for everything we do, but this is a - long-term project). - -** Git commits -:PROPERTIES: -:CUSTOM_ID: h:6830d3f3-130c-4346-b3ca-a3d4b0e9f974 -:END: - -Just an overview of what we did. Thanks again to everyone involved. - -#+begin_src sh -~/Git/Projects/denote $ git shortlog 2.1.0..2.2.0 --summary --numbered - 125 Protesilaos Stavrou - 17 Jean-Philippe Gagné Guay - 2 Vedang Manerikar - 1 Henrik Hörmann -#+end_src - -** Policy for the next development cycle -:PROPERTIES: -:CUSTOM_ID: h:cb0cae4f-c9a1-40b3-98ae-781a57270d4e -:END: - -I will give a ~1 week pause on Denote development before making any -feature changes. This is to ensure that we catch possible bugs and -push fixes right away. If there are other changes in place, it is not -possible to make point updates of this sort, as we must first wait for -the new features to be tested in real-world scenaria. - -* Version 2.1.0 on 2023-11-12 -:PROPERTIES: -:CUSTOM_ID: h:167beb8f-14be-40de-a1f2-d13910924c00 -:END: - -The general theme of this release is improvements to the quality of -life with Denote. While these release notes and the overall -documentation are comprehensive, make no mistake: Denote can be used -with =M-x denote=, =M-x denote-link=, =M-x denote-backlinks=, =M-x -denote-rename-file=. These have been rock solid from the beginning. -Everything else is for more specialised workflows. - -I hope to produce a companion video to this changelog in the coming -days. Though I am still reeling from the injury to my left hand (I -wrote all this to not delay the package any longer). Please check back -in my website's coding blog section to find the follow-up video: -. - -[ Remember to consult the manual whenever you have a question about - Denote. It is comprehensive and, in my opinion, a paradigm of how - free software should be done for the benefit of users. I document - everything in detail and am eager to continue this way. If something - is unclear, contact me in person, use the mailing list, or open an - issue on the GitHub/GitLab mirror. I do not check other fora or - media and will thus not help you there. If you are writing custom - code, remember to read the doc strings. I write them for you too. ] - -** Deprecated the ~denote-allow-multi-word-keywords~ -:PROPERTIES: -:CUSTOM_ID: h:a086d1d2-adb3-4151-a7af-813d79b4b3dc -:END: - -This user option enabled the use of keywords that consisted of -multiple words. Those would be separated by hyphens. Such keywords do -not work as Org =#+filetags= and also mess up with the neat search -semantics of Denote's file-naming scheme where a hyphen prefix -anchors the query to the =TITLE= component of the name. - -Users who absolutely need multi-word keywords are encouraged to use -the new ~denote-file-name-letter-casing~ option. More below. - -** Control the letter casing of file name components -:PROPERTIES: -:CUSTOM_ID: h:29319b8a-698b-4a1c-bab4-7b106a623de8 -:END: - -By default, Denote downcases all components of the file name. The user -option ~denote-file-name-letter-casing~ provides granular control over -this behaviour. - -The value it accepts is an alist where each element is a cons cell of -the form =(COMPONENT . METHOD)=. The manual, or the variable's doc -string, cover the details. The gist is that we can now instruct Denote -to accept input verbatim, such as because we want to apply a -=camelCase= convention or variants thereof. - -Here is an example, where we downcase the title, but preserve the -letter casing of the signature and keyword components with this: - -#+begin_src emacs-lisp -(setq denote-file-name-letter-casing - '((title . downcase) - (signature . verbatim) - (keywords . verbatim) - (t . downcase))) -#+end_src - -Users of the now-deprecated ~denote-allow-multi-word-keywords~ are -encouraged to implement a letter casing convention with the help of -this new user option. - -Relevant sections in the manual: - -- The file-naming scheme: - . -- Contol the letter casing of file names: - - -** The ~denote-dired-mode~ should now work while toggling ~wdired~ -:PROPERTIES: -:CUSTOM_ID: h:18a3b515-9306-4911-ba2d-73e36efbdd32 -:END: - -The writable version of Dired would break the colouration applied by -~denote-dired-mode~. I have arranged for this to not happen anymore, -although it means that I had to add an advice to relevant wdired -symbols because no proper hook is on offer. - -** The "do or create" commands are more intuitive to use -:PROPERTIES: -:CUSTOM_ID: h:5bcdc4b8-ecba-44d7-accc-0b26657aa29b -:END: - -Denote provides several commands with a "do or create" logic. For -example, the ~denote-open-or-create~ prompts for a file to visit: if -something matches the user's input, it is visited in a buffer, -otherwise a new note is created with the given input. Same for -~denote-link-or-create~, mutatis mutandis. - -Before, the "... or create" step did not make it obvious how the -previous search terms could be reused. Whereas now those are set as -the default minibuffer value at the title prompt, meaning that typing -=RET= at the empty prompt will use that value, while =M-n= -(~next-history-element~ with default settings) will put the text into -the prompt for further editing. - -I will answer this because I get asked about it: we still refrain from -creating the new note outright because the search terms are not -necessarily suitable for a new title. Remember that Denote's file name -is optimised for searching: =-word= is specific to the title, =_word= -to the keywords, and ==word= to the signature. Combine this with the -~orderless~ package and you frequently type something like =_jou -he= -to match a file with the =journal= keyword and the word =hesitation= -in its title. - -*IMPORTANT NOTE:* some minibuffer completion User Interfaces preselect -the first completion candidate, which is not always the same as the -default value. Check with your UI of choice how to pass a default -value and/or provide an empty input. For example, with the ~vertico~ -package one can move up from the first candidate to select the prompt -itself (the counter switches from =1/N= to =*/N=). - -Relevant sections in the manual: - -- Open an existing note or create it if missing: - . -- Link to a note or create it if missing: - . - -*** New "... or create with command" features for more flexibility -:PROPERTIES: -:CUSTOM_ID: h:6f475151-9d64-4dfb-8c59-694c93d56ce8 -:END: - -As part of the wider "do or create" feature set, Denote provides the -option to run a specific note-creating command instead of just using -the standard ~denote~ one. For example, it is possible to call the -~denote-subdirectory~ command to pick a subdirectory of the -~denote-directory~ for the new note. Commands providing this facility -are ~denote-open-or-create-with-command~ and ~denote-link-after-creating-with-command~. - -Thanks to Vedang Manerikar for fixing a broken ~if~ clause during -development: . - -** The title and signature prompts use minibuffer completion -:PROPERTIES: -:CUSTOM_ID: h:429847c8-ebf4-4b23-a597-5276309ef61a -:END: - -All Denote minibuffer prompts come with their own history. This means -that =M-p= (~previous-history-element~) and =M-n= -(~next-history-element~) always return relevant input. - -The title and signature prompts now reuse their input history to -provide completion. This means that the user can quickly access -previous inputs, either to pass them directly or edit them further -before inputting them. - -[ Use the built-in ~savehist-mode~ to persist histories across sessions. ] - -Remember to check with your minibuffer UI on how to input empty -values at the prompt, should you ever need to do so. - -For posterity, I first implemented this in commit =0d855bb=. However, -it did not work with the default minibuffer because the =SPC= key -performs completion (popping up the Completions buffer). So users -could not easily input an arbitrary string for the title/signature. I -thus reverted that commit in =9f692cb=. - -[ The bug was reported by Suhail Singh on the mailing list: - . ] - -Stefan Monnier suggested the use of the ~minibuffer-with-setup-hook~, -which lets us disable =SPC= completion for the purposes of these -functions. This is most welcome as the functionality is nice to have. -Stefan's feedback was provided on the emacs-devel mailing list: -. - -** Create a note with the region's contents -:PROPERTIES: -:CUSTOM_ID: h:ae798d1f-6fa2-4d99-91c9-0d5eb18b1bb0 -:END: - -The command ~denote-region~ takes the contents of the active region -and then prompts for a title and keywords. Once a new note is -created, it inserts the contents of the region therein. This is -useful to quickly elaborate on some snippet of text or capture it for -future reference. - -It also provides the ~denote-region-after-new-note-functions~ abnormal -hook. Read the manual for more: -. - -** Comprehensive refinements to the ~denote-rename-buffer-mode~ -:PROPERTIES: -:CUSTOM_ID: h:91b3ba9f-8b10-4f1c-a08b-70f5e7140923 -:END: - -This is an opt-in feature that automatically renames the buffer of -newly visited Denote files according to the user's preferences. Not to -be confused with renaming files: buffers are internal to Emacs. Enable -it at startup by adding this to your configuration file: - -#+begin_src emacs-lisp -(denote-rename-buffer-mode 1) -#+end_src - -Relevant entries in the manual: - -- Automatically rename Denote buffers: - . -- The ~denote-rename-buffer-format~ option: - . - -*** The ~denote-rename-buffer-format~ option -:PROPERTIES: -:CUSTOM_ID: h:beeafe57-f110-4c11-87e7-10f682ca2386 -:END: - -The user option ~denote-rename-buffer-format~ controls how the -function ~denote-rename-buffer~ chooses the name of the -buffer-to-be-renamed. This function is the one used by the -~denote-rename-buffer-mode~. - -Users may want, for example, to include some text that makes Denote -buffers stand out, such as a =[D]= prefix. Examples: - -#+begin_src emacs-lisp -;; Use the title (default) -(setq denote-rename-buffer-format "%t") - -;; Use the title and keywords with some emoji in between. -(setq denote-rename-buffer-format "%t 🤨 %k") - -;; Use the title with a literal "[D]" before it -(setq denote-rename-buffer-format "[D] %t") -#+end_src - -Users who need yet more flexibility are best served by writing their -own function and assigning it to the ~denote-rename-buffer-function~ -(in such a case, please contact me as I am curious to know what the -underlying need is). - -The manual or doc string of ~denote-rename-buffer-format~ cover the -technicalities of the available format specifiers. - -Thanks to Jean-Philippe Gagné Guay for intermediately refining parts -of the code. This was done in pull request 177 on the GitHub mirror: -. - -Thanks to Vedang Manerikar for ensuring that the string of the buffer -is trimmed so that it never starts with an empty space (those buffers -count as "internal" to Emacs and are not shown to the user): -. - -*** The ~denote-rename-buffer-mode~ also works with unsaved buffers -:PROPERTIES: -:CUSTOM_ID: h:e65bb546-af22-45fb-a918-d0e621b0e415 -:END: - -Internal refinements to a Denote Lisp macro make this minor mode also -work with new and unsaved Denote buffers. Whereas before only the -buffers of existing files would be renamed. - -** Denote's renaming facilities are better than ever -:PROPERTIES: -:CUSTOM_ID: h:703b9021-f917-4b3f-9406-14992b2a4fe8 -:END: - -Denote's value proposition is its efficient file-naming scheme that -makes it easier to retrieve files even with rudimentary search tools. -We provide several commands to rename existing files according to this -scheme. The underlying file type does not matter (e.g. I use Denote to -name my video files). - -Relevant sections in the manual: - -- Renaming files: - . -- Front matter: - . - -*** Rename like an expert with ~denote-rename-no-confirm~ -:PROPERTIES: -:CUSTOM_ID: h:8798dd8c-819d-4fda-9865-77d9734da28c -:END: - -By default, the ~denote-rename-file~ command asks for a final -confirmation before carrying out its function. The new user option -~denote-rename-no-confirm~ can be bound to a non-nil value to skip -that step. - -This only applies to ~denote-rename-file~. Other commands that rename -files in bulk never prompt for such confirmation (it would make them -cumbersome to use, plus it is assumed that the user who performs a -batch operation understands the implications). - -*** The ~denote-rename-file~ command prompts for a signature -:PROPERTIES: -:CUSTOM_ID: h:e4e7e3d8-40e3-4f58-a19f-df34ccbfdbbd -:END: - -This command used to only ask for a title and keywords. Now it allows -to use a signature as well. An empty input means that the signature is -ignored. AGAIN, please check with your minibuffer completion UI on how -to input an empty value, otherwise you will not get what you expect. - -*** Rename mutliple files sequentially with ~denote-dired-rename-files~ -:PROPERTIES: -:CUSTOM_ID: h:dcb623aa-fc4d-4d84-80e4-a540b6dbb144 -:END: - -This provides the same interface as ~denote-rename-file~, only it -works over a list of marked Dired files. - -Internally, the prompts for title, keywords, and signature are -improved to display the underlying file that is affected by the -current operation. As the user renames files, the prompts reflect -which one is current. - -*** The name of ~denote-dired-rename-marked-files~ has changed -:PROPERTIES: -:CUSTOM_ID: h:f9a16fc1-840d-400f-a5ae-a7791fac441f -:END: - -It is now called ~denote-dired-rename-marked-files-with-keywords~ to -better communicate what it does. In short, this is a quick way to add -the given keywords to a list of files, converting them to the Denote -file-naming scheme in case they are not already using it. For the full -interactive power, use the aforementioned ~denote-dired-rename-files~. - -*** The ~denote-rename-file-using-front-matter~ can be used without saving its buffer -:PROPERTIES: -:CUSTOM_ID: h:bfc194c2-5980-482a-aa1c-feb4ced992d1 -:END: - -This is now possible because of changes to underlying functions (a -Denote Lisp macro---not to bother you with technicalities). - -Same principle for ~denote-rename-file-using-front-matter~. - -*** The name of ~denote-change-file-type~ has changed -:PROPERTIES: -:CUSTOM_ID: h:3bf4b6c4-8399-4d5d-8df1-6495f5bfc579 -:END: - -It is now called ~denote-change-file-type-and-front-matter~ to avoid -confusion as to whether Denote converts files from one format to -another (there are specialised tools for that). - -*** Renaming a file returns the new file path for programmatic use -:PROPERTIES: -:CUSTOM_ID: h:1d7bffd1-e422-420d-b453-9a36dd8508f7 -:END: - -Thanks to mentalisttraceur for requesting this feature in issue 183 on -the GitHub mirror: . - -** Link to a file with a signature -:PROPERTIES: -:CUSTOM_ID: h:b154ef64-c3b4-4e15-b533-c59d5b2ebf6b -:END: - -The ~denote-link-with-signature~ command prompts for a file that has a -=SIGNATURE= component and links to it. The link's description includes -the text of the signature as well as the title. - -Thanks to Mark Olson for mentioning this idea. It was done in issue -167 on the GitHub mirror: . - -I implemented it live, while also refactoring relevant parts of the -code to be more abstract/reusable: -. - -Thanks to Alan Schmitt for spotting and fixing a regression caused by -the above: -. - -** Renaming GPG or Age encrypted file works as expected -:PROPERTIES: -:CUSTOM_ID: h:9ceaf432-797c-46e5-aaf8-d7180ad66689 -:END: - -Emacs can seamlessly visit a =.gpg= or =.age= file. Denote has nothing -to do with encryption, though it takes care to recognise the -underlying file type and to perform its work accordingly. However, -prior versions of Denote contained a bug in how file extensions were -handled: it would keep the encryption extension but remove the file -type extension before it (so ".org.gpg" would wrongly become ".gpg"). - -Thanks to Jens Östlund for reporting a bug with ~denote-keywords-add~ -on an encrypted file, which prompted me to investigate this further -and fix the issue holistically. This was done in issue 172 on the -GitHub mirror: . - -Interested parties are advised to check the two new public functions, -~denote-get-file-extension~ and ~denote-get-file-extension-sans-encryption~, -for the implementation details. In short, we had a problem with all -operations that needed to retrieve the file extension when that -included an encryption component. - -** The optional ~denote-journal-extras~ -:PROPERTIES: -:CUSTOM_ID: h:54723661-31f8-4cab-9be5-4cab19e44dc7 -:END: - -The manual of Denote has long provided code samples to achieve -particularised results. Among those were snippets to streamline the -use of Denote for journaling. - -To make things even easier for users, we now have the -=denote-journal-extras.el=. It consolidates the rich corpus of -documented snippets into an easy-to-use and formally maintained -package. Thanks to Vedang Manerikar for providing the impetus for this -process. This was done on the mailing list: -. - -The new file is optional. It can be loaded thus: - -#+begin_src emacs-lisp -(require 'denote-journal-extras) -#+end_src - -The main idea is to quickly create journal entries. Check the manual -for the details, including the commands to use and the variables to -configure: . - -Thanks to Kostas Andreadis for working on a comment I had included in -a working state of the code about the inclusion of templates. Kostas -made it possible to use the Denote template prompt (per the -~denote-templates~ user option) as part of the creation of a new -journal entry. This was done in pull request 173 on the GitHub mirror: -. The change is less -than 15 lines and thus Kostas does not need to assign copyright to the -Free Software Foundation. - -Also thanks to TJ Stankus for reporting a case where -~denote-journal-extras-title-format~ did not accept a ~nil~ value (as -it should). This was done in issue 176 on the GitHub mirror: -. - -** The optional ~denote-silo-extras~ -:PROPERTIES: -:CUSTOM_ID: h:618495d2-0c5b-48b4-af88-56f3d969697c -:END: - -This is the same idea as with the =denote-journal-extras.el=: we had -the code in the manual and are now formally distributing it. Thanks -again to Vedang Manerikar for initiating this process. It was done on -the mailing list: -. - -Use this optional feature with: - -#+begin_src emacs-lisp -(require 'denote-silo-extras) -#+end_src - -Consult the manual for the details: -. - -** The infrastructure for unique identifiers is more robust -:PROPERTIES: -:CUSTOM_ID: h:1d538d7f-52e6-4653-b057-c62606752934 -:END: - -For Denote version =2.0.0= I introduced a general scheme intended to -avoid scenaria where duplicate identifiers could be created (thus -breaking a premise of Denote). Jean-Philippe Gagné Guay iterated over -the code to make it more robust and to fix some of the cases I had not -accounted for. This was done in pull request 159 on the GitHub mirror: -. Same idea in pull -request 187: . - -** For developers or advanced users -:PROPERTIES: -:CUSTOM_ID: h:9031dc82-ab75-438c-a2c8-a1250ae48671 -:END: - -Denote has a clean code base with small and composable functions. This -encourages hackability. Each definition in the source is documented, -while the manual provides an overview of every public symbol. - -- Added :: ~denote-get-file-extension~, - ~denote-get-file-extension-sans-encryption~, - ~denote-keywords-combine~, - ~denote-retrieve-keywords-value-as-string~, - ~denote-title-prompt-current-default~, ~denote-command-prompt~. - -- Refactored :: ~denote-all-files~, ~denote-signature-prompt~, - ~denote-file-prompt~, ~denote-title-prompt~, - ~denote-rewrite-front-matter~. - -Please read their documentation strings for the details. Or check the -manual: . - -** Check out the ~denote-explore~ package by Peter Prevos -:PROPERTIES: -:CUSTOM_ID: h:759d0276-17e8-4461-9ee4-b4d07840dd7a -:END: - -Peter posted this on the mailing list and I asked if it was okay to -mention it in the release notes of Denote. If you have a relevant -announcement to make, consider sending it to our mailing list. - -#+begin_quote -Hi folks, - -I have just updated the denote-explore package: -https://github.com/pprevos/denote-explore - -It does three things: - -1. Summary statistics: Count and visualise keywords and note types -2. Random walks: Generate new ideas using serendipity -3. Network visualisation: Visualise your Denote network of links - -It contains a rudimentary network visualisation function, relying -on the R language. I will need some D3.js expertise to improve the -visualisation. - -There should be a way to generate the basic network structure just -using Elisp and feeding a JSON to D3.js. - -Regards - -P:) -#+end_quote - -** Miscellaneous -:PROPERTIES: -:CUSTOM_ID: h:01dc6bb0-53ac-43e1-b12e-484c99a6c2a7 -:END: - -- During this release cycle, I made lots of changes that in one way or - another related to the ~denote-file-prompt~. It was relying on a - =project.el= mechanism that did not allow us to do everything we - needed. I have thus arranged for it to use the standard - ~completing-read~ mechanism. There are subtle differences in - behaviour, though the core idea is the same. This change fixes a few - not-so-obvious bugs. Interested parties are advised to refer to the - message in commit =50d1bbdf1e8ffe0f449f2f5da02f9b70322fff7d=. - -- All commands that use the ~denote~ function internally (i.e. - anything that creates a new note) call the - ~denote-after-new-note-hook~ as part of their work. This hook is - mostly intended for advanced users who want to do something after a - new note is produced. - -- The ~menu-bar-mode~ submenu of Denote is now positioned where it - should be after the "Tools". Thanks to Noboru Ota for the patch: - . - -- The ~menu-bar-mode~ entry of Denote includes the new commands. This - is a nice way to discover more of what Denote can do. - -- The commands ~denote-backlinks-prev~ and ~denote-backlinks-next~ are - only meant to be used inside the Denote backlinks buffer. As such, - they now produce an error when called elsewhere (I wish I could hide - them from =M-x= altogether). - -- The ~denote-extract-keywords-from-front-matter~ always returns a - list, thus avoiding an erroneous case. Thanks to Vedang Manerikar - for fixing the bug: . - -- The =T= in the Denote identifier component now has its own face: - ~denote-faces-time-delimiter~. This is used by the backlinks buffer - and the ~denote-dired-mode~. The idea is to introduce a subtle - distinction between the date and time constituents of the - identifier. Those who want the =T= to be the same colour as the rest - of the identifier, can make the ~denote-faces-time-delimiter~ - inherit the ~denote-faces-date~. For example: - - #+begin_src emacs-lisp - (set-face-attribute 'denote-faces-time-delimiter nil :inherit 'denote-faces-date) - #+end_src - - Thanks to Jean-Charles Bagneris for sending this patch: - . - -- Fixed a ~nil~ file expansion in the function - ~denote--extract-title-from-file-history~. Thanks to ezchi for - bringing this matter to my attention. It was done in issue 166 on - the GitHub mirror: - . - -- A link can be created from inside an ~org-capture~ buffer. This - means that we can call ~denote-link~ (and related) while capturing a - new note with ~org-capture~. Thanks to Peter Smith for reporting the - bug in issue 186 on the GitHub mirror: - . - -- We stopped using ~vc-rename-file~ to rename files. The reason is - that it requires the buffer to be saved, but we do not want that - after modifying the front matter because we want to give the user a - chance to confirm what happened. Thanks to Frédéric Willem for - reporting the problem in issue 185 on the GitHub mirror: - . - -- Thanks to Ivan Sokolov for removing a double negative logic in a - snippet. This was done in pull request 162 on the GitHub mirror: - . - -** Git commits -:PROPERTIES: -:CUSTOM_ID: h:d8f30943-70dd-45fe-8cf1-4c3918152aeb -:END: - -Just an overview of what we did. Every contribution matters. - -#+begin_src -~/Git/Projects/denote $ git shortlog 2.0.0..2.1.0 --summary --numbered - 153 Protesilaos Stavrou - 15 Jean-Philippe Gagné Guay - 5 Vedang Manerikar - 1 Alan Schmitt - 1 Ivan Sokolov - 1 Jean-Charles Bagneris - 1 Kostas Andreadis - 1 Noboru Ota - 1 Peter Prevos -#+end_src - -* Version 2.0.0 on 2023-07-21 -:PROPERTIES: -:CUSTOM_ID: h:3f17bf03-4c47-4410-abf8-1db4a0ac7775 -:END: - -This is the second major version of Denote, close to one year after -its initial release. The video demo I did back then remains relevant, -even though lots of details have changed. - -** Notes have a new optional SIGNATURE field -:PROPERTIES: -:CUSTOM_ID: h:a3a9e14d-4132-47c0-a23c-cb008a141668 -:END: - -It is now possible to create notes that include a =SIGNATURE= field in -their file name. Either use the convenience command ~denote-signature~ -or configure the user option ~denote-prompts~ to affect what the ~denote~ -command should prompt for. - -Signatures are arbitrary strings of characters that enable the user to -further qualify their documents. One possible workflow is to write -relational notes, such that =1a1= is the first extension of another -note with a =1a= signature. - -The design of the =SIGNATURE= field is consistent with the Denote -file-naming scheme. The field separator is the double equals sign -(~==~), while words that comprise the signature are joined together by -a single equals sign. As such, the user can prefix a search with an -equals sign to match words in the =SIGNATURE=, just as they would use -dashes for the =TITLE= and underscores for the =KEYWORDS=. - -[ Read the manual for the technicalities of the Denote file-naming - scheme. This is not limited to "notes": any file can be named - accordingly (I do it with my videos, for example). ] - -Signatures are not included in a file's front matter. This is a -strategic decision to preserve backward compatibility, while not -introducing a feature that has not enjoyed widespread usage. I want -to make signatures behave the same as the rest of the file name -fields, though I am interested to learn how users employ them in their -workflow. - -The signature extension was discussed at length on the GitHub mirror -in issue 115: . -Thanks to Stefan Thesing, Mirko Hernandez, Noboru Ota (nobiot), -Xiaoxing Hu, nbehrnd, Elias Storms, and 101scholar for helping me -reason about this feature, understand its scope, and prototype its -implementation. - -Also thanks to Alfredo Borrás and Jeremy Friesen for discussing with -me the field delimiter of signatures on the mailing list: -. -Thanks to Kai von Fintel for doing the same on the GitHub mirror in -issue 147: . - -Read the original announcement: -. - -As part of the development, I fixed a case where -~denote-rename-file-using-front-matter~ would fail if it could not -find a signature - -The idea is that we want the command to behave the way it always did -when the file has no signature and to preserve the signature when it -is present. - -Thanks to relict for reporting the issue on the mailing list: -. - -** The rename commands avoid creating duplicate identifiers -:PROPERTIES: -:CUSTOM_ID: h:d24645a3-ad02-450c-b3d7-af7802aa0b26 -:END: - -Denote provides commands to rename an existing file to one that -follows the Denote file-naming scheme (videos, PDFs, other text -documents, ...). Check, for example, the ~denote-rename-file~ and -~denote-dired-rename-marked-files~. The idea is to make everything -easier to search. - -In prior versions, these commands could produce duplicate identifiers -if the modification date of the underlying files was the same. Such a -scenario occurs when the files are modified programmatically, as with -the =touch= command or the various =git= operations. - -Denote will now take care to increment the identifier until it becomes -unique within the current scope. - -Thanks to Felipe Balbi for reporting this bug in issue 105 on the -GitHub mirror: . - -Thanks to Vedang Manerikar and Jean-Charles Bagneris for commenting on -this feature on the mailing list: -. - -Thanks to Ashton Wiersdorf for noticing a mistake I made that caused a -regression in ~denote-rename-file~: -. - -*** Optional arguments affect ~denote-dired-rename-marked-files~ -:PROPERTIES: -:CUSTOM_ID: h:6ea998be-83dd-4c67-945c-11011372818f -:END: - -The ~denote-dired-rename-marked-files~ now accepts two optional -arguments. When called interactively, these are interpreted as a -single or double universal prefix argument (=C-u= by default, though -do =M-x where-is= and search for ~universal-argument~). - -The first argument, named =SKIP-FRONT-MATTER-PROMPT=, skips the "yes -or no" prompt requested at the outset of the operation, passing to it -an affirmative response. Thanks to Jay Rajput for asking the question -that inspired me to implement this. It was done in issue 155 on the -GitHub mirror: . - -The second argument, named =NO-UNIQUE-ID-CHECK=, will not perform any -checks for potential duplicate identifiers. The default is to check -for duplicates and increment them such that they become unique. The -reason this optional argument exists is for those who want to speed up -the process, perhaps because they know ahead of time all identifiers -will be unique or do not care about them. - -Thanks to Bruno Boal for refining how the prefix argument is -processed. The patch was sent via a private channel. The change is -small and thus does not require copyright assignment to the Free -Software Foundation. - -** Menu entries help users discover Denote -:PROPERTIES: -:CUSTOM_ID: h:651e5561-f9ce-41f6-bad3-d54ce2dcff04 -:END: - -Users of ~menu-bar-mode~ and/or ~context-menu-mode~ will now find a -submenu with points of entry to Denote. Refer to the publication I -made on my website, as it includes a picture: -. I -will save the thousand words for the following sections. 🙃 - -There is a known issue where the ~menu-bar-mode~ entry is positioned -before the =File= submenu. Apparently, there exists an inelegant way -to place the menu elsewhere, but I am not willing to maintain hacks -for missing functionality. If someone knows a clear way to put the -submenu elsewhere, please contact me: I want it to be after =Tools=. - -Thanks to Kai von Fintel and Noboru Ota (nobiot) for discussing the -placement of the submenu: -. - -** "Link" commands have simpler names -:PROPERTIES: -:CUSTOM_ID: h:acf95a79-3c45-423d-a88f-d6eed7fa5387 -:END: - -Originally, Denote was organised as a collection of several files, -each of which had its own prefix like =denote-dired.el=, and -=denote-link.el=. This arrangement was deemed surplus to requirements -and all core code was consolidated in =denote.el=. An artefact of -that design was the presence of symbols that retained their admittedly -awkward names, like the command ~denote-link-backlinks~ or -~denote-link-add-missing-links~. - -All such commands are deprecated. They are replaced with more -discoverable names. The deprecation is done in such a way that the -old names are aliases for the new ones, but the user is warned not to -rely on them. - -The new names in detail: - -| Old name 🤨 | New name 🤩 | -|-------------------------------------+---------------------------------------------------------------| -| ~denote-link-add-links~ | ~denote-add-links~ | -| ~denote-link-add-missing-links~ | ~denote-add-missing-links~ | -| ~denote-link-backlinks~ | ~denote-backlinks~ | -| ~denote-link-find-file~ | ~denote-find-link~ | -| ~denote-link-insert-link~ | ~denote-insert-link~ (alias for ~denote-link~) | -| ~denote-link-show-backlinks-buffer~ | ~denote-show-backlinks-buffer~ (alias for ~denote-backlinks~) | - -** Denote buffers can have shorter names -:PROPERTIES: -:CUSTOM_ID: h:98f6b10a-ea29-49d1-8d3f-e2f0409f4c8f -:END: - -The Denote file-naming scheme is designed to be a low-tech way of -embedding information in files, making them easier to find. A -downside is that the names are longer than =blah.txt= and so the -default Emacs behaviour is to derive a buffer name from the file name. - -The new optional =denote-rename-buffer.el= provides a minor mode to -automatically rename the buffer of an existing file, such that it -reflects the file's =TITLE= field. Users must enable -~denote-rename-buffer-mode~. - -The renaming procedure is controlled by the user option -~denote-rename-buffer-function~. By default, it provides the means to -rename using (i) the title, (ii) the identifier, or (iii) a custom -function that returns a string. Experienced users can refer to -~denote-rename-buffer-with-title~ to draw inspiration on the design of -such a function. - -Thanks to Morgan Davidson for asking a question that inspired me to -implement this feature. The discussion took place in issue 151 on the -GitHub mirror . - -** Silos work as directory trees -:PROPERTIES: -:CUSTOM_ID: h:113820c4-7a6f-4126-9a44-92bfa59744e2 -:END: - -Denote provides a feature to isolate files in to their own silos, each -of which functions as its own ~denote-directory~ variable. The -technicalities are explained in the manual. Silos have proven to be a -valuable aspect of file management and I have thus expanded their -scope to work as fully fledged directory trees. This means that we no -longer assume a silo to be a flat directory listing, but instead -recognise any subdirectories inside of it. - -Thanks to relict007, Hilde Rhyne, Mirko Hernández, Noboru Ota -(nobiot), Alan Schmitt, hapst3r, and Hilde Rhyne for their -participation in the relevant discussions: - -- -- -- -- (GitHub mirror) -- . - -** Keywords do not accept multiple words by default -:PROPERTIES: -:CUSTOM_ID: h:08f23806-9570-4031-86e4-810b3e93be81 -:END: - -The idea is to have short keywords and then use more than one, if -necessary. We do not want to encourage the habit of long keywords -that become overly specific, while we want to avoid the use of -dashes as delimited in the file name's =KEYWORDS= field. - -Technically, this changes the default value of the user option -~denote-allow-multi-word-keywords~. Users who preferred the old -behaviour can simply toggle it on. - -** Pass arguments to Org capture -:PROPERTIES: -:CUSTOM_ID: h:58ff6dd3-693a-4437-9217-8e876d92c975 -:END: - -Denote is not an extension of Org mode, though it can integrate with -~org-capture~. I now make it possible to design a capture template -that uses specific prompts. Consult the section in the manual titled -"Create note with specific prompts using Org capture". - -Thanks to Aditya Yadav for asking about this in issue 132 on the -GitHub mirror: . - -** Change an existing note's file type -:PROPERTIES: -:CUSTOM_ID: h:e1e874e3-d8ad-4685-aa62-59ad07078db2 -:END: - -The command ~denote-change-file-type~ changes the file type of an -existing note. The available options are those among -~denote-file-type~. Thanks to Jean-Philippe Gagné Guay for the -contribution, which was done in pull request 137 on the GitHub mirror: -. - -** Denote dynamic blocks can now parse ~rx~ notation -:PROPERTIES: -:CUSTOM_ID: h:fe595ee7-8ba6-4ca3-aa66-35aa4e5ca0f5 -:END: - -Denote can leverage the Org feature of "dynamic blocks" to produce -lists of links/backlinks. This is especially useful for metanotes -(read the Denote manual---I document everything for a reason). - -Before, regular expressions were implemented only as strings while now -they can also be written using the ~rx~ notation. Thanks to Mirko -Hernandez for proposing this feature and discussing it with me in -issue 122 on the GitHub mirror: -. - -Thanks to Elias Storms, the author of =denote-org-dblock.el=, for -iterating on this functionality. This was done in pull request 130 on -the GitHub mirror: . - -** Made links to non-note files works as intended -:PROPERTIES: -:CUSTOM_ID: h:431a8952-0d71-4ba6-b6ae-85e5f7d520b9 -:END: - -The function ~denote-get-path-by-id~ is refactored to accept any file -with an identifier. This always was its intended purpose. The user -was always able to create =denote:= Org link types to, for example, -=jpg= files but ~denote-get-path-by-id~ was refusing to resolve the -otherwise valid path. Thanks to user relict007 for reporting the -problem and discussing it with me in issue 135 on the GitHub mirror: -. - -The change was not trivial. It was followed up by a patch from Noboru -Ota (nobiot) which elaborated on the conditionality. Quoting from -commit =9ce9a24=: - -#+begin_quote -fix(denote-get-path-by-id): #135 - -Reference: https://github.com/protesilaos/denote/issues/135 - -This patch change function 'denote-get-path-by-id' to allow for the following: - -- A single ID points to multiple files with different extensions -- Denote needs to find a single file out of the multiple files -- This is not necessarily a user error (export an Org file to an HTML) -- Denote should let user decide their "primary" file extension - -The case the patch is intended to fix goes something like this: - -- You have 20230216__mynotes--tag.org. -- You export it to 20230216__mynotes--tag.html. -- Both files are in denote-directory -- This means you have two files with the same ID with different - extensions denote-link-find-file, denote-link-find-backlink, and xref - integration might find the html file INSTEAD OF the .org file - -This is because html is earlier in the alphabetical order than -org. Because the function uses seq-find, it will find the .html file -first and returns it. -#+end_quote - -** The ~denote-rename-file-using-front-matter~ works with empty keywords -:PROPERTIES: -:CUSTOM_ID: h:b00f228d-7f84-4d84-8d5f-ac90ea6b1065 -:END: - -Keywords are an optional field in the Denote file-naming scheme. -However, an earlier version of the command mentioned in this heading -was considering them mandatory and would refuse to proceed if the -keywords were nil. Thanks to Eduardo Grajeda for fixing this: -. - -The change is within the ~15 line limit and does not require copyright -assignment to the Free Software Foundation. - -** The ~denote-title-prompt~ has its own history -:PROPERTIES: -:CUSTOM_ID: h:91f370f4-9fd1-461b-8ba4-fd9ba2d9c7a8 -:END: - -Denote implements minibuffer histories for all its relevant functions. -This makes it easier for users to retrieve their previous inputs and -to not get irrelevant ones. - -Before, the ~denote-title-prompt~ was not using its own history but -was instead relying on another one that was intended only for file -paths, thus mixing unrelated inputs. - -Thanks to Jonathan Sahar for bringing this matter to my attention. -This was done in issue 144 on the GitHub mirror: -. - -** For developers or advanced users -:PROPERTIES: -:CUSTOM_ID: h:dcc52671-2127-47e0-9167-003f40ca3a54 -:END: - -*** Made it possible to add predicates for recursive file listing -:PROPERTIES: -:CUSTOM_ID: h:62546ec1-6ec8-41c7-9a18-10a531b534ce -:END: - -The helper function ~denote--directory-all-files-recursively~ accepts -predicates to help speed up its work. - -Thanks to Wade Mealing for reporting the issue about the performance -of the built-in function ~directory-files-recursively~ in large, -nested directories. And thanks to Graham Marlow for the patch, which -was prepared as part of an extended discussion with me: - -- -- -- -- -- -- -- - -*** New public symbols -:PROPERTIES: -:CUSTOM_ID: h:ed723274-a78e-4cfd-9655-c3bfe0fb1e68 -:END: - -The following are now public symbols that we commit to support and -document henceforth: - -+ Function ~denote-file-type-extensions~ :: Return all file type - extensions in ~denote-file-types~. - -+ Variable ~denote-encryption-file-extensions~ :: List of strings - specifying file extensions for encryption. - -+ Function ~denote-file-type-extensions-with-encryption~ :: Derive - ~denote-file-type-extensions~ plus ~denote-encryption-file-extensions~. - -+ Function ~denote-link-return-links~ :: Return list of links in - current or optional =FILE=. Also see ~denote-link-return-backlinks~. - -+ Function ~denote-link-return-backlinks~ :: Return list of links in - current or optional =FILE=. Also see ~denote-link-return-links~. - -+ Function ~denote-rewrite-front-matter~ :: Rewrite front matter of - note after ~denote-rename-file~ (or related) The =FILE=, =TITLE=, - =KEYWORDS=, and =FILE-TYPE= arguments are given by the renaming - command and are used to construct new front matter values if - appropriate. - -+ Function ~denote-rewrite-keywords~ :: Rewrite =KEYWORDS= in =FILE= - outright according to =FILE-TYPE=. Do the same as - ~denote-rewrite-front-matter~ for keywords, but do not ask for - confirmation. This is for use in ~denote-keywords-add~, - ~denote-keywords-remove~, ~denote-dired-rename-marked-files~, or - related. - -I am publicising the ~denote-link-return-links~ and its counterpart in -response to the mailing list thread started by relict007: -. -relict007 is the developer of the ~denote-cache~ package (in -progress): . - -Similarly, the ~denote-rewrite-keywords~ is made public upon the -request of Alan Schmitt: -. - -** Miscellaneous -:PROPERTIES: -:CUSTOM_ID: h:918087e6-8cd5-4d4f-a11a-b465dcbd9fe3 -:END: - -- Revised ~denote-link-return-{links,backlinks}~ to not produce a - ~user-error~. The errors are reserved for the interactive - functions. The others are for developers. Thanks to Elias Storms for - bringing this matter to my attention: - . - -- Refrained from trying to find forward links in non-text-files. If a - file extension is not in ~denote-file-types~, we have no way of - parsing or finding outgoing links in it. This change checks for the - file extension early on in 'when-let*' block and avoids opening the - file which is a relatively costly operation (and would fail finding - links anyway). Thanks to relict007 for the patch. This was done on - the mailing list: - - The change is small and thus does not require copyright assignment - to the Free Software Foundation. - -- Explained how to troubleshoot Denote. Refer to the section in the - manual titled "Troubleshoot Denote in a pristine environment." - While this is about Denote, the skills apply to all Emacs packages. - -- Ensured backlinks get correct ~denote-directory~ path. The - backlinks buffer will now get the correct path when it is generated - inside a silo. This is related to issue 129 reported by hapst3r on - the GitHub mirror: . - The change is necessary because =.dir-locals.el= do not work for - buffers, so we must get the value from the file that calls - ~denote-link-backlinks~. - -- Added missing underscore from examples in exporting section. Thanks - to Peter Prevos for bringing this matter to my attention: - . - -- Made the command ~denote-open-or-create~ work with an empty - ~denote-directory~. The ~denote-file-prompt~ would throw an error - before. The correct behaviour is to proceed to the "Create" phase - if the ~denote-directory~ is empty. Thanks to user drcxd for - reporting the bug in issue 131 on the GitHub mirror and for testing - my sample code: . - -- Documented how to use tree-based file prompt on demand. This is my - solution to a request made by Mirko Hernandez on the possible use of - the old Denote file prompt. It is better not to introduce a user - option for this case, nor to keep multiple variants of the - ~denote-file-prompt~ in denote.el, as we want to keep things simple. - Mirko's feedback was provided in issue 121 on the GitHub mirror: - . - -- Added the variable ~denote-user-enforced-denote-directory~. This is - intended for users who write custom code to extend Denote. The - value of this variable should be ~let~ bound around calls to the - function ~denote-directory~, thus overriding its return value. This - was discussed on the mailing list and then introduced by Vedang - Manerikar in commit =977c757=, with further changes by me in - =20ddc97=: . - Vedang has assigned copyright to the Free Software Foundation. - -- Fixed ~my-denote-org-extract-subtree~ section of the documentation. - This is part of some sample code that is not part of =denote.el=, - but we provide as a convenience/inspiration for interested parties. - - The provided function did not work correctly. - - 1. Tags are extracted before deleting the region from the source file. - 2. The function ~org-end-of-subtree~ is called to calculate the - point we should delete up to. The previously used function - ~org-entry-end-position~ ends at the first sub-heading under the - tree, which is not what we want. Instead, we want to cut the - whole subtree. - 3. The date information available in the subtree is retained. We - look for three common places for this information: the =CREATED= - or =DATE= properties in the =PROPERTIES= drawer, and the =CLOSED= - cookie at the element level itself. - - Thanks to Vedang Manerikar for the contribution: - - -- Removed the dependency on the built-in ~xdg~ library and updated the - default value of the user option ~denote-directory~. The reason is - that XDG is a Linux standard that does not work on other operating - systems, according to private feedback I received. - -- Fixed a regression for =M-p= (~previous-history-element~) in "do or - create" commands. Read the doc string of the commands - ~denote-open-or-create~ or ~denote-link-or-create~ for how this is - supposed to work. In short: - - - Invoke the "do or create" command. - - Type something that does not match a file. - - In the following title prompt, hit =M-p= to bring back the last input. - - I realised there was a regression when I read issue 152 on the - GitHub mirror, which was created by user "ustcpxy": - . The issue is - about skipping the file title prompt. - -- Simplified the internal ~denote--buffer-file-names~. Thanks to Adam - Růžička for noting that my change was not compatible with older - Emacs versions, and for preparing the change. This was discussed in - pull request 158 on the GitHub mirror, with my suggestion to not use - ~seq-filter~ as it affected the return value: - . The change is - below the 15 line limit, meaning that Adam does have to assign - copyright to the Free Software Foundation. - -- Documented custom code in the manual on how to interactively select - a silo. I am providing this in response to a request from GitHub - user rbenit68. The discussion took place in issue 127 on the GitHub - mirror, with the participation of Mirko Hernandez: - . The custom code - I provide is the expanded version of an idea put forth by Mirko, to - whom I am thankful. - -- Fixed an outdated reference in the ~denote-file-types~ doc string. - Thanks to user doolio for spotting the error and reporting it in - issue 139 on the GitHub mirror: - . - -- Cited in the manual's section "Publications about Denote" an article - by Mohamed Suliman titled /Managing a bibliography of BiBTeX entries - with Denote/ (2022-12-20): - . - If you have published something related to Denote, please let me - know and I will add to the list. - -- Cited the essay by Summer Emacs titled /An explanation of how I use - Emacs/ (2023-05-04): - - -- Cited the video series by Stefan Thesing titled /Denote as a - Zettelkasten/: . - -- Added link to Karl Voit's work in the manual's section "Alternative - implementations and further reading." Thanks to Norwid Behrnd for - the contribution in pull request 123 on the GitHub mirror: - . - -- Fixed the broken link to jao's blog. Thanks to Tomasz Hołubowicz - for the contribution, which was done in pull request 145 on the - GitHub mirror: . - -- Authored lots of other ancillary changes/features to the code base - or the manual (yes, this change log is how I "cut the long story - short"). - -* Version 1.2.0 on 2022-12-12 -:PROPERTIES: -:CUSTOM_ID: h:92478a05-4a69-413c-8d95-1dacbcf6af2c -:END: - -** Denote now requires Emacs version 28.1 or higher -:PROPERTIES: -:CUSTOM_ID: h:bc0e173a-3b9f-427c-9fb0-d435a5ef127e -:END: - -With the help of Noboru Ota (nobiot), we realised that Denote was -broken on Emacs 27 for quite a while. The fact that we received no -feedback about it suggests that this change is the best course of -action going forward. Discussion: - - -Emacs 27 lacks certain Xref facilities that we need for the -backlinking facility. It was holding us back for no good reason, -while also adding to the maintenance burden. - -If you are using Denote on Emacs 27 and things are working for you, -there is no need to update the package. Do it when you also upgrade -Emacs to a newer version. - -** Display context in backlinks' buffer -:PROPERTIES: -:CUSTOM_ID: h:dafbdbae-36f1-487a-94c8-2762568a766e -:END: - -By default, the generic backlinks' buffer, which can be displayed with -the command ~denote-link-backlinks~ (alias ~denote-link-show-backlinks-buffer~), -only shows the file names of the linked notes. - -We have made it possible to produce a more informative view by showing -the context of the link and also listing all links per file. This is -done by setting the user option ~denote-backlinks-show-context~ to a -non-nil value. - -To illustrate the difference, this is the default backlinks' buffer: - -#+begin_example -Backlinks to "On being honest" (20220614T130812) ------------------------------------------------- - -20220614T145606--let-this-glance-become-a-stare__journal.txt -20220616T182958--feeling-butterflies-in-your-stomach__journal.txt -#+end_example - -And this is the one with ~denote-backlinks-show-context~ enabled: - -#+begin_example -Backlinks to "On being honest" (20220614T130812) ------------------------------------------------- - -20220614T145606--let-this-glance-become-a-stare__journal.txt -37: growing into it: [[denote:20220614T130812][On being honest]]. -64: As I said in [[denote:20220614T130812][On being honest]] I have never -20220616T182958--feeling-butterflies-in-your-stomach__journal.txt -62: indifference. In [[denote:20220614T130812][On being honest]] I alluded -#+end_example - -Granted, here we show plain text though in Emacs the results have the -appropriate colours of the active theme and are easier to read. - -Thanks to Noboru Ota (nobiot) for implementing this feature. We -discussed it at length on the mailing list: -. - -Noboru has assigned copyright to the Free Software Foundation. - -** Dynamic Org blocks for lists of Denote links -:PROPERTIES: -:CUSTOM_ID: h:f7904a57-22c0-446f-b7e3-7a736332002c -:END: - -Denote now includes the ~denote-org-dblock~ library. Activate it -thus: - -#+begin_src emacs-lisp -;; Register Denote's Org dynamic blocks -(require 'denote-org-dblock) -#+end_src - -A dynamic block gets its contents by evaluating a given function, -depending on the type of block. The type of block and its parameters -are stated in the opening =#+BEGIN= line of the block. Typing =C-c -C-c= with point on that line runs the function, with the given -arguments, and populates the block's contents accordingly. - -What Denote has is ways to write blocks that produce a list of links -matching a given regular expression while conforming with some other -parameters. The manual explains how to use this powerful feature -(which is necessarily specific to the Org file type): -. - -Thanks to Elias Storms for authoring ~denote-org-dblock~ and for -discussing this issue at length with me on the mailing list: -. - -Elias has assigned copyright to the Free Software Foundation. - -** Integration with the built-in project.el and xref.el libraries -:PROPERTIES: -:CUSTOM_ID: h:e8a7d08c-cdf0-4207-92c1-391415b8371f -:END: - -Denote was already using Xref internally but has now gained more -capabilities which help it find files more effectively. With the help -of Emacs' standard project library, all file-related prompts (e.g. to -add a link) search all items in the ~denote-directory~ regardless of -whether the user is in a subdirectory or not. - -All Denote commands benefit from this refactoring. One such request -to "Make ~denote-open-or-create~ work better across subfolders" was -made in issue 114 on the GitHub mirror: -. - -Thanks to Noboru Ota (nobiot) for introducing this feature together -with a new system of "modules" for incorporating additional built-in -functionality: - -- -- - -I will not document the new user option ~denote-modules~ right now as -my ongoing job search prevented me from exploring the full potential -of this feature. I promise to do it for the next version of Denote -and update the manual accordingly. Nevertheless, the doc string of -~denote-modules~ already provides all one needs to get started. - -** Re-use last input in "do or create" commands -:PROPERTIES: -:CUSTOM_ID: h:5a003d44-7ad0-4c92-b908-ec7cf016b2dd -:END: - -The commands ~denote-open-or-create~, ~denote-link-or-create~ first -prompt for an existing note. If they find it, they act on it, -otherwise they prompt for the creation of a new note to operate on. - -At the first prompt, it is common to use regular expressions and -out-of-order pattern matching (such as with the ~orderless~ package), -so the input can be something like =_test ^2022 some title=, which we -obviously don't want to automatically reuse as the new note's actual -title. - -To this end, and to accommodate all workflows, we leverage Emacs' -minibuffer history to make the last input accessible with =M-p= at the -minibuffer prompt (=M-x previous-history-element=). The text is -available for further editing before it is submitted as the new note's -title. Simple, effective, and flexible! - -Thanks to Guo Yong for starting the discussion that led me to this -improvement: -. - -** Add support for any file type -:PROPERTIES: -:CUSTOM_ID: h:e73a4e76-6c00-4691-8893-8f885c26f306 -:END: - -Denote provides the user option ~denote-file-type~ which specifies the -file type to use for new notes. Options include Org mode (the -default), Markdown+YAML, Markdown+TOML, and plain text. Furthermore, -there exists the convenience command ~denote-type~ (alias -~denote-create-note-using-type~) which prompts for a file type to use -when creating a new note (I normally write in plain text, but -sometimes switch to Org or Markdown). - -The variable ~denote-file-types~ (which is NOT a user option) -specifies all the parameters of what a "file type" means, such as how -to format its front matter, what style of date+time to use, which file -type extension to write, how to rename the file, what style of link to -apply, and so on. Advanced users can now edit this variable to either -register new file types or redefine the behaviour of existing ones. -Read this comprehensive guide on how to do it: -. - -I repeat: this is for advanced users or, anyhow, for those who are -prepared to maintain some custom code in their setup. The guide is -accessible though and I am always willing to help anyone in need of -assistance. - -A relevant request for such a feature can be found in issue 86 on the -GitHub mirror: . - -The ~denote-file-types~ were introduced by Jean-Philippe Gagné Guay in -pull request 89 at the GitHub mirror and were part of Denote version -0.6.0: . I have made -lots of changes since then to make all parts of Denote work with it -and to parameterise its various facets. - -** Exclude certain directories from all operations -:PROPERTIES: -:CUSTOM_ID: h:04f42aab-d8fe-4c4a-b865-3bb0655e2631 -:END: - -The user option ~denote-excluded-directories-regexp~ instructs all -Denote functions that read or check file/directory names to omit -directories that match the given regular expression. The regexp needs -to match only the name of the directory, not its full path. - -Affected operations include file prompts and functions that return the -available files in the ~denote-directory~. File prompts are used by -several commands, such as ~denote-link~ and ~denote-subdirectory~. -Functions that check for files include ~denote-directory-files~ and -~denote-directory-subdirectories~. - -Thanks to Graham Marlow for the contribution which was done in pull -request 112 on the GitHub mirror: -. - -The original contribution, with the subsequent tweaks I made to it, is -within the eligible line count and thus does not require copyright -assignment to the Free Software Foundation. - -** Exclude certain keywords from being inferred -:PROPERTIES: -:CUSTOM_ID: h:226ba85e-1f5e-45f5-956a-f5e8a95c397e -:END: - -The user option ~denote-excluded-keywords-regexp~ omits keywords that -match a regular expression from the list of inferred keywords. - -Keywords are inferred from file names and provided at relevant prompts -as completion candidates when the user option ~denote-infer-keywords~ -is non-nil. - -Thanks to Stefan Thesing for proposing this idea in issue 115 on the -GitHub mirror: . - -[ Other people participate in that thread and there may be something - more coming out of it. ] - -** Use the ~citar-denote~ package for bibliography notes -:PROPERTIES: -:CUSTOM_ID: h:ff16633f-5fb8-4935-9e2f-044ec998d3f7 -:END: - -Peter Prevos has produced the ~citar-denote~ package which makes it -possible to write notes on BibTeX entries with the help of the ~citar~ -package. These notes have the citation's unique key associated with -them in the file's front matter. They also get a configurable keyword -in their file name, making it easy to find them in Dired and/or -retrieve them with the various Denote methods. - -With ~citar-denote~, the user leverages standard minibuffer completion -mechanisms (e.g. with the help of the ~vertico~ and ~embark~ packages) -to manage bibliographic notes and access those notes with ease. The -package's documentation covers the details: . - -Thanks to Peter Prevos for developing this package and for mentioning -it on the Denote mailing list: -. - -** New functions and variables for developers -:PROPERTIES: -:CUSTOM_ID: h:5cc2076d-d4d2-45be-b28e-9ec67eca82b4 -:END: - -Developers or users who maintain custom code now have access to: - -+ Function ~denote-keywords-sort~ -+ Function ~denote-keywords-prompt~ - -Plus all the following which are related to the aforementioned ~denote-file-types~: - -+ Variable ~denote-org-link-format~ -+ Variable ~denote-md-link-format~ -+ Variable ~denote-id-only-link-format~ -+ Variable ~denote-org-link-in-context-regexp~ -+ Variable ~denote-md-link-in-context-regexp~ -+ Variable ~denote-id-only-link-in-context-regexp~ -+ Function ~denote-date-org-timestamp~ -+ Function ~denote-date-rfc3339~ -+ Function ~denote-date-iso-8601~ - -Again, users can implement support for ANY FILE TYPE and use it to -write notes in, either as their default choice or on-demand. If -anything, this highlights the flexibility of Denote. - -** Miscellaneous -:PROPERTIES: -:CUSTOM_ID: h:acbb0cf7-ad17-495e-85d2-821cbbfc3158 -:END: - -+ Added the ~denote-keywords-sort~ function. The intent is to - abstract the task of sorting the keywords. Before, it was handled - by the ~denote-keywords-prompt~, which meant that keywords were not - sorted when the ~denote~ function was called from Lisp. Thanks to - Florian for bringing this matter to my attention, providing relevant - feedback, and fixing an omission of mine in ~denote-rename-file~: - . - -+ Expanded the manual's entry on directory "silos" to include more - code examples. Thanks to Viktor Haag for asking a question on the - mailing list that inspired me to produce this entry: - . - -+ Included a section in the manual with a non-exhaustive list of - references to publications about Denote. As of this writing, it - includes entries from David Wilson (SystemCrafters), Jack Baty, - Jeremy Friesen, and Peter Prevos. If you have an article about - Denote, please contact me about it directly or on the Denote mailing - list and I will add it to the manual. - -+ Tweaked how Org's HTML export produces links in order to avoid - broken subdirectory paths. Thanks to Thibaut Benjamin for the - contribution, which was done in pull request 116 on the GitHub - mirror: . - - The change concerns a single line and thus Thibaut requires no - copyright assignment to the Free Software Foundation. - -+ Expanded the manual where necessary. - -* Version 1.1.0 on 2022-10-20 -:PROPERTIES: -:CUSTOM_ID: h:8e0f536a-ab3b-4cab-82f7-529bc0e40dbd -:END: - -** New commands or refinements to common use-cases -:PROPERTIES: -:CUSTOM_ID: h:5665e7ec-4f3a-4de3-8cb0-63d25a0db8c1 -:END: - -+ The ~denote-link-add-missing-links~ is a companion to what we - already provide to produce a list of links to Denote files matching - a regular expression (the ~denote-link-add-links~). This new - command adds links that are not already present in the current file. - So if you have a metanote that references, say, your journal entries - but have not updated it in a month, you can revisit the metanote, - invoke ~denote-link-add-missing-links~, and then type the search - terms (e.g. =_journal=) to include what remains. - - Thanks to Elias Storms for the initial contribution, which was done - in pull request 108 on the GitHub mirror: - . - - Elias has assigned copyright to the Free Software Foundation. It is - required for changes that exceed 15 lines in total. - -+ The ~denote-link-find-backlink~ provides a minibuffer interface that - shows all backlinks to the current note. It complements the - existing ~denote-link-backlinks~ command (which also has the alias - ~denote-link-show-backlinks-buffer~). Each command has its own - niche: the minibuffer lets the user leverage powerful pattern - matching styles, such as those provided by the =orderless= package, - while the bespoke buffer provides an easy overview of what links to - the current note. - - Thanks to Elias Storms for the original patch: - . - -+ The ~denote-keywords-add~ and ~denote-keywords-remove~ are two - commands that interactively operate on the current note's front - matter to add or remove keywords. They use the familiar keywords' - prompt which means, among others, that they can read more than one - keyword at a time. To specify multiple keywords, separate each - input with a comma (or whatever the value of ~crm-separator~ is, - which should be a comma unless something out-of-the-ordinary is in - force). - - Thanks to Elias Storms for the original patch, which was done as - part of a discussion on the mailing list and then iterated on: - . - -+ The ~denote-link~ command will now recognise an active region and - use its text as the description of the inserted link. The default - behaviour is to use the file's title from its front matter or file - name. Thanks to Charanjit Singh for the original contribution, - which was done as part of pull request 109 on the GitHub mirror: - . A subsequent - tweak was implemented in pull request 110, following a discussion - with me: . - - Charanjit's contribution is below the ~15 line threshold and thus - does not require copyright assignment to the Free Software - Foundation. - -+ The renaming operations are now aware of the underlying version - control system and will use the appropriate command when a VCS is - available. In practice, renaming a file under, say, Git will - register it as a "rename" instead of two separate actions of - deletion and addition. - - Thanks to Florian for the patch. It was discussed on the mailing - list and then underwent some changes: - . - -+ The ~denote-rename-file-using-front-matter~ no longer fails to carry - out its intended task when the front matter has no keywords. If no - keywords are available, this is interpreted as a request to remove - the KEYWORDS component of the file name. This was always - technically possible and could be achieved with various permutations - of the user option ~denote-prompts~ (as explained in its doc string - or the manual). Denote only needs an identifier in the file name to - establish unique links (although I strongly encourage you to stick - to the standard file-naming scheme as it is informative, reliable, - and can work even if you access your data without Emacs). - -** For more advanced use-cases -:PROPERTIES: -:CUSTOM_ID: h:505c84dd-2959-4bd4-8af4-78d75592a6d5 -:END: - -+ The variable ~denote-file-types~ has been tweaked to respond - directly to changes in its value done with ~setq~. Thanks to Noboru - Ota for the patch: . - - Noboru has assigned copyright to the Free Software Foundation. - -+ The =:front-matter= property of the ~denote-file-types~ now accepts - a nil value. Denote could always work without front matter, but - this was not implemented flexibly in the ~denote-file-types~. - Thanks to Noboru Ota (nobiot) for pointing this out on the mailing - list: . - -+ The ~denote-file-prompt~ function now reads an optional - =INITIAL-TEXT= argument. This is a string that prepopulates the - minibuffer. It is useful for custom commands the user may have - where, for example, there is a need to automatically filter to - entries matching =_journal=. Thanks to Alan Schmitt for suggesting - the idea: . - -+ The ~denote-rename-file-using-front-matter~ accepts an optional - =AUTO-CONFIRM= argument. It can either be passed interactively or - via Lisp. The doc string (or the manual) explains the details. - Thanks to Elias Storms for the initial patch: - . - -+ The ~denote-prompt-for-date-return-id~ function uses the familiar - ~denote-date-prompt~ and returns the appropriate identifier. It is - used internally by some of our function, but we also provide it for - anyone who wants to write their own custom code. - -+ The ~denote-retrieve-or-create-file-identifier~ function reads and - option =DATE= argument to its mandatory =FILE= argument. If =FILE= - does not have an identifier and optional =DATE= is non-nil, the - function invokes the ~denote-prompt-for-date-return-id~, as - mentioned above. - -+ The ~denote-rename-file~ command accepts an optional =DATE= - argument. It functionally does what is described right above, with - the exception that this is for an interactive function (a - "command"). Read the detailed doc string or the manual for - everything that pertains to this powerful command. - - Thanks to Florian for suggesting the idea on the mailing list: - . - -+ The ~denote-directory-text-only-files~ function filters the - ~denote-directory-files~ to only return a list of text files. This - leaves out, say, mp3 files. The function is used internally, though - it may also prove useful in custom user code. - -** Miscellaneous refinements -:PROPERTIES: -:CUSTOM_ID: h:0531047f-ef15-412e-b265-886c55526d57 -:END: - -+ Implemented a ~revert-buffer-function~ for the backlinks' buffer, - which is produced by the command ~denote-link-backlinks~. This - revert function is what the =g= key invokes with the default key - bindings (the command is ~revert-buffer~). It produces the buffer - anew, updating the list of backlinks accordingly. - -+ Documented how to speed up the creation of the backlinks' buffer. - As this depends on the built-in =xref= library, the change is done - by specifying the value of the user option ~xref-search-program~ in - Emacs 28 or higher. For example: - - #+begin_src emacs-lisp - (setq xref-search-program 'ripgrep) - #+end_src - - For something more elaborate: - - #+begin_src emacs-lisp - ;; Prefer ripgrep, then ugrep, and fall back to regular grep. - (setq xref-search-program - (cond - ((or (executable-find "ripgrep") - (executable-find "rg")) - 'ripgrep) - ((executable-find "ugrep") - 'ugrep) - (t - 'grep))) - #+end_src - -+ Removed some minor duplication of effort in how the buttonisation of - links is done (what makes them clickable). - -+ Made refinements to the definition of functions such as - ~denote-link-add-links~. There should be no noticeable change for - users, though this shows we care about code quality. - -+ With Eshel Yaron, we tried to remove the empty indices for functions - and variables from the HTML version of the manual. These indices - are useful in the Info version, which can be accessed directly from - Emacs when the =denote= package is installed (for example, evaluate - =(info "(denote) Top")=), but they do not work with HTML. Alas, - what we tried to do did not work. Maybe Org has a way to control - what is exported where. We shall see. At any rate, thanks to Eshel - for the effort: . - -+ All code that integrates the =denote:= custom hyperlink type with - Org's link facility is now assigned =autoload= cookies. These are - done to ensure that =denote= is loaded and is available in cases - where Org needs to access a =denote:= link at some early stage - (e.g. at startup before using Denote). Thanks to Sven Seebeck for - reporting the problem: . - Although Sven could not reproduce a bug reliably, I believe this - prevents such an eventuality. - -+ Expanded or otherwise updated the manual to account for all of the - above, where appropriate. - -* Version 1.0.0 on 2022-09-30 -:PROPERTIES: -:CUSTOM_ID: h:053975d7-3fe2-49e5-96a0-336483e5861c -:END: - -This is the first major release of Denote. A part of the changes -documented herein is for advanced users or developers who wish to -extend Denote with their custom code. Though we first cover what -applies to everyone. - -** Changes for all users -:PROPERTIES: -:CUSTOM_ID: h:25692d4f-08da-4938-a81e-54070d91f51a -:END: - -+ The custom Org hyperlink type of =denote:= can be visited from - outside the ~denote-directory~. We now provide the necessary glue - code that Org needs to store these =denote:= links. Storing them - can be done with an ~org-capture~ template or via the command - ~org-store-link~. Use this to, for example, capture a TODO that - references one of your notes. - - =denote:= links work for as long as the referenced file is somewhere - in the ~denote-directory~ or one of its subdirectories. - - Thanks to Marc Fargas for the contribution. Marc did not need to - assign copyright to the Free Software Foundation, as the patch was - within the ~15 line limit that is permissible. - - The contribution was discussed on the mailing list: - . A prior - exchange took place in issue 104 over at the GitHub mirror: - . - - Some further tweaks were made to the relevant function. Thanks to - Elias Storms for reporting on the mailing list a bug which revealed - a regression I introduced to the Org link storing mechanism: - . - -+ Following from above, the command ~denote-link-find-file~ finds - files reliably, regardless of where the link is stored. All it - needs is for the target file to be inside the ~denote-directory~. - - I discovered this while exchanging views with Marc Fargas regarding - the aforementioned patch: . - -+ The command ~denote-link-buttonize-buffer~, which "buttonizes" - =denote:= links in plain text and Markdown files, now performs its - task regardless of where the current file is stored. Those links - work for as long as the file they reference is somewhere inside the - ~denote-directory~. - -+ The commands ~denote-link-after-creating~, ~denote-link-or-create~ - provide a convenience for users who need to create link to notes - that may not exist yet. The idea is that one is expounding on a - given topic and wants to create a link to a relevant issue. They - are not sure if they have written anything about it yet, so they - invoke the relevant command. Consult their doc strings or read the - manual: . - - Thanks to user sienic for suggesting the idea and for testing the - prototypes. And thanks to Juanjo Presa for participating in the - discussion to share the view that this functionality should be part of - denote.el. This happened in issue 96 over at the GitHub mirror: - . - -+ The command ~denote-open-or-create~ offers the convenience of - visiting a file, if it exists, else prompting for its creation. - Thanks to Alan Schmitt for the contribution. The patch was sent on - the mailing list: . - It is within the limit of what is allowed without assigning - copyright to the Free Software Foundation, though Alan has done the - relevant paperwork. - -+ The manual expands on two sections: (1) Variants of - ~denote-open-or-create~, (2) Variants of ~denote-link-or-create~. - They show how one can use the above "do or create" commands with - different permutations of the Denote prompts for new note creation. - -+ The manual includes a section titled "Create a note with the - region's contents". Quote: - - #+begin_quote - Sometimes it makes sense to gather notes in a single file and later - review it to make multiple notes out of it. With the following - code, the user marks a region and then invokes the command - ~my-denote-create-new-note-from-region~: it prompts for a title and - keywords and then uses the region's contents to fill in the newly - created note. - #+end_quote - - This is not part of denote.el, though we provide it in the manual - for users that may need it. Thanks to sundar bp for suggesting the - idea. This was done via a private channel and the information is - shared with permission. - -+ The manual has another entry titled "Split an Org subtree into its - own note", which is similar to the above idea of using the region's - contents but has some extra niceties provided by Org. Quote: - - #+begin_quote - With Org files in particular, it is common to have nested headings which - could be split off into their own standalone notes. In Org parlance an - entry with all its subheadings is a "subtree". With the following code, - the user places the point inside the heading they want to split off and - invokes the command ~my-denote-split-org-subtree~. It will create a - note using the heading's text and tags for the new file. The contents - of the subtree become the contents of the new note and are removed from - the old one. - #+end_quote - - Thanks to Sven Seebeck for suggesting the idea and for testing my - prototypes. This information is shared with permission, as it was - provided via a private channel. - -+ The manual describes how a user can leverage the built-in - ~dired-virtual-mode~ to perform arbitrary sorting of their list of - notes. It also includes code for Eshell to quickly "export" a - command's output into a dedicated buffer (which can then be used to - derive a "virtual" Dired). Thanks to Yi Liu for asking the question - that inspired this entry: - . - -+ The ~denote-faces-broken-link~ has been removed. It was used for - Org links. The idea was to apply a different style if the link was - broken. However, the way fontification works means that there may - be a performance penalty as Org tries to check again and again if - the link is broken or note. As =denote:= links are robust (unless - the user tries to break them), this penalty is unacceptable. Thanks - to Peter Prevos for reporting the issue and discussing it with me on - the mailing list: - . - -+ The "denote" group in Custom UI buffers now provides a link to the - Info manual that is shipped with the package. To read the manual, - evaluate =(info "(denote) Top")=. Else visit the official web page: - . - -+ Fixed a case where an internal check for a note would throw an error - if the buffer was not visiting a file. Thanks to Hilde Rhyne was - the patch: it is below the ~15 line threshold and thus does not - require copyright assignment to the Free Software Foundation. The - issue was discussed on the mailing list and was pushed to users as - version =0.6.1=: - . - -+ When linking to a file that has no front matter, Denote tries to use - the TITLE component of the file name (per our file-naming scheme) as - the link's descriptive text. We now make this look a bit better, by - capitalising only the first letter while dehyphenating the text, - converting =this-is-a-test= to =This is a test=. Before, we would - capitalise all words. Thanks to Clemens Radermacher for the patch. - It was sent via a private channel. Clemens has assigned copyright - to the Free Software Foundation. - -** Changes for developers or advanced users -:PROPERTIES: -:CUSTOM_ID: h:165cd056-5e27-4536-b8ac-57f88c927a43 -:END: - -Lots of functions and variables which once were for "private" use (the -presence of double hyphens in the symbol) are now made public. -Concretely this means that they no longer have double hyphens in their -name and we pledge to support them henceforth. "Support" means that -we (i) consider them stable, (ii) document them properly, (iii) will -record any changes made to them such as in a change log, a blog post -on my website, and via ~make-obsolete~. - -The manual provides a complete reference of what is on offer. The -section is titled "For developers or advanced users": -. - -Normally, we do not support private forms and can delete/modify them -without notice. However, I decided to write obsoletion aliases for -all forms I made public or otherwise revised, in an effort not to -break any existing custom code. The following table covers all -obsolete symbols and their new counterparts. PLEASE UPDATE YOUR CODE -as those aliases will be removed in the near future. - -| Index | Old symbol | New symbol | -|-------+------------------------------------------------+---------------------------------------------------| -| 1 | denote--id-format | denote-id-format | -| 2 | denote--id-regexp | denote-id-regexp | -| 3 | denote--title-regexp | denote-title-regexp | -| 4 | denote--keywords-regexp | denote-keywords-regexp | -| 5 | denote--punctuation-regexp | denote-excluded-punctuation-regexp | -| 6 | denote-punctuation-excluded-extra-regexp | denote-excluded-punctuation-extra-regexp | -| 7 | denote--sluggify | denote-sluggify | -| 8 | denote--sluggify-and-join | denote-sluggify-and-join | -| 9 | denote--sluggify-keywords | denote-sluggify-keywords | -| 10 | denote--desluggify | denote-desluggify | -| 11 | denote--only-note-p | denote-file-is-note-p | -| 12 | denote--file-has-identifier-p | denote-file-has-identifier-p | -| 13 | denote--file-supported-extension-p | denote-file-has-supported-extension-p | -| 14 | denote--writable-and-supported-p | denote-file-is-writable-and-supported-p | -| 15 | denote--file-name-relative-to-denote-directory | denote-get-file-name-relative-to-denote-directory | -| 16 | denote-link--id-from-string | denote-extract-id-from-string | -| 17 | denote--directory-files | denote-directory-files | -| 18 | denote--subdirs | denote-directory-subdirectories | -| 19 | denote--get-note-path-by-id | denote-get-path-by-id | -| 20 | denote--directory-files-matching-regexp | denote-directory-files-matching-regexp | -| 21 | denote--retrieve-read-file-prompt | denote-file-prompt | -| 22 | denote--extract-keywords-from-path | denote-extract-keywords-from-path | -| 23 | denote--keywords-prompt | denote-keywords-prompt | -| 24 | denote--retrieve-filename-identifier | denote-retrieve-filename-identifier | -| 25 | denote--file-name-id | denote-retrieve-or-create-file-identifier | -| 26 | denote--retrieve-filename-title | denote-retrieve-filename-title | -| 27 | denote--retrieve-title-value | denote-retrieve-title-value | -| 28 | denote--retrieve-title-line | denote-retrieve-title-line | -| 29 | denote--retrieve-keywords-value | denote-retrieve-keywords-value | -| 30 | denote--retrieve-keywords-line | denote-retrieve-keywords-line | -| 31 | denote--format-file | denote-format-file-name | -| 32 | denote--barf-duplicate-id | denote-barf-duplicate-id | -| 33 | denote--title-prompt | denote-title-prompt | -| 34 | denote--file-type-prompt | denote-file-type-prompt | -| 35 | denote--date-prompt | denote-date-prompt | -| 36 | denote--subdirs-prompt | denote-subdirectory-prompt | -| 37 | denote--template-prompt | denote-template-prompt | -| 38 | denote--filetype-heuristics | denote-filetype-heuristics | -| 39 | denote--rename-file | denote-rename-file-and-buffer | -| 40 | denote--rename-file-prompt | denote-rename-file-prompt | - -If you are writing code that extends Denote and feel that something is -either missing or has remained private, please contact us on the -mailing list, the GitHub/GitLab mirror, or send me an email directly. -I always respond in a timely fashion. - -** Open to everyone -:PROPERTIES: -:CUSTOM_ID: h:27a391cf-8d5e-4d19-942f-46fc52dea80c -:END: - -The most common feedback I get about Denote is that its documentation -is good. As you can tell from these change logs, the plan is to -continue on this path. - -Please note that the communication channels for Denote (mailing list, -mirrors, my personal email) are open to users of all levels. Do not -hesitate to contact us/me. - -Thanks again to everyone for their contributions, direct or indirect, -either in the form of code or the discussion of ideas. Quoting from -the "Acknowledgements" section of the manual (all my packages have -such a section): - -#+begin_quote -Denote is meant to be a collective effort. Every bit of help matters. - -+ Author/maintainer :: Protesilaos Stavrou. - -+ Contributions to code or the manual :: Abin Simon, Alan Schmitt, - Benjamin Kästner, Clemens Radermacher, Colin McLear, Damien Cassou, - Eshel Yaron, Hilde Rhyne, Jack Baty, Jean-Philippe Gagné Guay, Jürgen - Hötzel, Kaushal Modi, Kyle Meyer, Marc Fargas, Peter Prevos, Philip - Kaludercic, Quiliro Ordóñez, Stefan Monnier. - -+ Ideas and/or user feedback :: Abin Simon, Alan Schmitt, Alfredo - Borrás, Benjamin Kästner, Colin McLear, Damien Cassou, Elias Storms, - Frank Ehmsen, Hanspeter Gisler, Jack Baty, Juanjo Presa, Kaushal - Modi, M. Hadi Timachi, Paul van Gelder, Peter Prevos, Shreyas - Ragavan, Summer Emacs, Sven Seebeck, Taoufik, Yi Liu, Ypot, atanasj, - hpgisler, pRot0ta1p, sienic, sundar bp. - -Special thanks to Peter Povinec who helped refine the file-naming -scheme, which is the cornerstone of this project. - -Special thanks to Jean-Philippe Gagné Guay for the numerous -contributions to the code base. -#+end_quote - -* Version 0.6.0 on 2022-08-31 -:PROPERTIES: -:CUSTOM_ID: h:50aba79a-d702-42b4-a2a5-7fa29033f904 -:END: - -Denote is in a stable state. I consider it feature-complete, without -prejudice to possible refinements to its existing feature set. The next -version shall be =1.0.0=. - -** User-facing changes -:PROPERTIES: -:CUSTOM_ID: h:566a770b-399e-47a6-9aa4-326fd6ade9a7 -:END: - -+ The Denote linking facility can now link to any file that has the - Denote file-naming scheme. Before, we limited this feature to what we - consider "note" files, else the supported plain text formats (per - ~denote-file-type~). Thanks to Peter Prevos for the discussion on the - mailing list: . - -+ Date prompts may optionally use the familiar Org date-selection - mechanism that leverages the calendar. This feature is subject to the - user option ~denote-date-prompt-use-org-read-date~. A date prompt is - used by the ~denote-date~ command or, optionally, by the ~denote~ - command when the user option ~denote-prompts~ is configured - accordingly. The manual elaborates on the specificities. Thanks to - Jean-Philippe Gagné Guay for the contribution in pull request 97 at - the GitHub mirror: . - -+ Leading empty spaces at the ~denote~ =TITLE= prompt no longer produce - hyphens: they are simply ignored to keep file names consistent. - Thanks to Peter Prevos for the contribution in pull request 99 at the - GitHub mirror: . - - [ Peter has started the process for copyright assignment to the Free - Software Foundation, though the total contributions are still within - the permitted boundaries. ] - -+ When linking to files that have no front matter, the link's anchor - text (the human-readable part) is derived from the file name =TITLE= - component. We apply a de-hyphenation and capitalisation of its - constituent words. This is not always perfect, but it is better than - something like =this-is-the-title=. Thanks to Peter Prevos for the - original idea in pull request 93 at the GitHub mirror: - . - -+ The active region is now used as the default value of the ~denote~ - command =TITLE= prompt. The idea behind this Do-What-I-Mean-flavoured - patch is to be able to take a note about a subject that appears in a - buffer by simply marking it before invoking the ~denote~ command. - - Thanks to Eshel Yaron for the patch: . - It is below the ~15 line threshold that thus requires no copyright - assignment to the Free Software Foundation. - -+ The ~denote-rename-file-using-front-matter~ command now offers to save - the buffer if appropriate. In the past, it would simply produce an - error asking the user to save the buffer. Thanks to Peter Prevos for - the contribution in pull request 103 at he GitHub mirror: - . - -+ Fixed the text of the confirmation prompt in the command - ~denote-migrate-old-markdown-yaml-tags~. Thanks to Abin Simon for the - patch: . - - This patchset also fixes (i) how a tag is identified for the purposes - of migrating old to new front matter, (ii) the regular expression for - Org front matter keywords - - [ The total changes are below the ~15 line threshold and thus do not - require copyright assignment to the Free Software Foundation. ] - -+ Fixed a bug that prevented the creation of new notes. Thanks to - Juergen Hoetzel for the contribution in pull request 84 at the GitHub - mirror: . This was - done immediately after the release of version =0.5.0= on 2022-08-10 - and was provided to users as version =0.5.1= - - [ The change is below the ~15 line threshold. ] - -** Internal refinements -:PROPERTIES: -:CUSTOM_ID: h:9374b533-faaa-4ab4-b668-f74b5eae7ab5 -:END: - -These make the code simpler and more predictable. As the individual -changes are not user-facing, I invite interested parties to consult the -Git log. Special thanks to Jean-Philippe Gagné Guay for the multiple -contributions (and relevant discussions) over at the GitHub mirror: - -- -- -- -- -- -- - -[ Jean-Philippe has assigned copyright to the Free Software Foundation. - It is required for non-trivial changes. ] - -** For advanced users -:PROPERTIES: -:CUSTOM_ID: h:c6fc05a2-ff31-4a0c-91a1-f64d2cfd6a16 -:END: - -The variable ~denote-file-types~ is an alist of plists which -substantiates the supported file types (per the user option -~denote-file-type~). Properties pertain to the formatting of front -matter and the retrieval of relevant values. The doc string of -~denote-file-types~ explains the details, while the default value uses -the ancillary functions we define. Thanks to Jean-Philippe Gagné Guay -for the relevant contributions in pull request 89 at the GitHub mirror: -. - - -* Version 0.5.0 on 2022-08-10 -:PROPERTIES: -:CUSTOM_ID: h:80b9daaa-c3c8-4457-b109-966bb6a99832 -:END: - -The general theme of this release is to refine what we already offer. -As I explained in some discussions, Denote is feature-complete. We can -always improve the code or add some ancillary function/command/variable, -though all the main ideas have already been implemented. Additional -functionality can be provided by other packages: I remain at the -disposal of anyone willing to write such a package. - -The present release covers more than 150 commits since version 0.4.0 on -2022-07-25. - -All release notes: . - -** Templates for new notes -:PROPERTIES: -:CUSTOM_ID: h:0878125f-8392-48e6-aeff-1469eb1e18fc -:END: - -We now provide the ~denote-templates~ user option. A "template" is -arbitrary text that Denote will add to a newly created note right below -the front matter. - -Templates are expressed as a =(KEY . STRING)= association. - -- The =KEY= is the name which identifies the template. It is an - arbitrary symbol, such as =report=, =memo=, =statement=. - -- The =STRING= is ordinary text that Denote will insert as-is. It can - contain newline characters to add spacing. The manual of Denote - contains examples on how to use the ~concat~ function, beside writing - a generic string: - . - -The user can choose a template either by invoking the new command -~denote-template~ or by changing the user option ~denote-prompts~ to -always prompt for a template when calling the ~denote~ command. - -Thanks to Jean-Philippe Gagné Guay for refinements to this facility. -Done in pull request 77 on the GitHub mirror: -. - -[ Jean-Philippe has assigned copyright to the Free Software Foundation. ] - -** Revised format for Org =#+filetags= entry -:PROPERTIES: -:CUSTOM_ID: h:17688b79-cb1b-4a59-831e-fbf2a81245d3 -:END: - -Denote used to format tags in Org files by separating them with two -spaces: - -#+begin_example -#+filetags: tag1 tag2 -#+end_example - -While this worked for some obvious use-cases, it is not supported by -Org. The Org documentation stipulates that tags be separated by the -colon sign. The above would then be written thus: - -#+begin_example -#+filetags: :tag1:tag2: -#+end_example - -Denote now conforms with Org's specifications. To help users update -their existing notes, we provide the ~denote-migrate-old-org-filetags~ -command. It will perform the conversion in all Org files that had the -old notation. As with all Denote operations that rewrite file contents, -it DOES NOT SAVE BUFFERS. The user is expected to review the changes, -such as by using ~diff-buffer-with-file~. Multiple buffers can be saved -with ~save-some-buffers~ (check its doc string). - -This command is provided for the convenience of the user. It shall be -deprecated and eventually removed from future versions of Denote. - -If you need help with any of this, please do not hesitate to contact me -either in private or in one of Denote's official channels (mailing list, -GitHub/GitLab mirror). - -Thanks to Alan Schmitt for bringing this matter to my attention: -. -Also thanks to Jean-Philippe Gagné Guay for commenting on it as it -helped me decide to include the command in =denote.el=: -. - -** Revised format for Markdown+YAML =tags:= entry -:PROPERTIES: -:CUSTOM_ID: h:205a09cf-0159-425e-a6b3-41700fa3ad31 -:END: - -This is the same idea as above. Before, we were making the mistake of -using incorrect YAML notation: - -#+begin_src yaml -tags: tag1 tag2 -#+end_src - -Now we do: - -#+begin_src yaml -tags: ["tag1", "tag2"] -#+end_src - -This is how the TOML variant always worked. - -For the user's convenience, we provide a command to migrate from the old -to the new syntax: ~denote-migrate-old-markdown-yaml-tags~. - -** Changes to file renaming and front matter rewriting -:PROPERTIES: -:CUSTOM_ID: h:15ecb4e8-d1ce-4e42-b74d-a3a046d93220 -:END: - -Denote adds "front matter" to newly created notes which includes data -such as the title and keywords/tags of the document. Strictly speaking, -the front matter is not required by Denote. It is provided for the -user's convenience, such as for readability or if they want to use the -note with other programs (e.g. Org export, a blog with Hugo/Jekyll, -...). - -Denote provides commands which help the user rename their notes, by -changing the file name's =TITLE= and/or =KEYWORDS= components (per -Denote's file-naming scheme). These commands also operate on the front -matter to keep the data between file name and file contents in sync -(again, for the user's convenience). - -For this release we have consolidated and refined our offerings in order -to improve their ergonomics. All changes are the result of fruitful -discussions on the mailing list and the issue tracker of the GitHub -mirror: - -- -- -- -- -- - -Thanks to (A-Z) Hanspeter Gisler, Jean-Philippe Gagné Guay, and Peter -Prevos for their participation. - -Also thanks to Jean-Philippe Gagné Guay for relevant code contributions -(please consult the Git log for the minutiae): - -- -- -- -- -- - -*** Renaming a single file -:PROPERTIES: -:CUSTOM_ID: h:1d695e54-1481-42dd-916b-c0542c48aa6f -:END: - -The commands ~denote-dired-rename-file-and-add-front-matter~ and -~denote-dired-rename-file~ are deprecated and superseded by the new -~denote-rename-file~. Please update any key bindings in your setup. - -The difference between the old commands and the new ~denote-rename-file~ -is that the latter will now insert front matter to supported file types -(per ~denote-file-type~) if they have none. This basically means that, -e.g., renaming a generic Org/Markdown/Plain text file with -~denote-rename-file~ will update its file name to comply with Denote's -file-naming scheme and also add the appropriate front matter (it -"converts" it to a Denote note). If front matter exists, this command -will rewrite it to reflect the changes to the file name's =TITLE= and/or -=KEYWORDS=. - -Consult the manual for the details: -. - -Or, if the new version of the GNU ELPA package is installed, evaluate: - -#+begin_src emacs-lisp -(info "(denote) Rename a single file") -#+end_src - -The user option ~denote-dired-rename-expert~ is obsolete. Denote always -asks for confirmation when renaming a single file. This is because the -user can rely on batch-renaming commands which ask for confirmation only -once per batch. - -*** Renaming multiple files at once -:PROPERTIES: -:CUSTOM_ID: h:82455fb4-576b-4753-af66-ac48fd158327 -:END: - -The command ~denote-dired-rename-marked-files-and-add-front-matter~ is -deprecated and its functionality is absorbed by the existing -~denote-dired-rename-marked-files~ command. The deprecated command was -used to insert front matter to supported file types (per -~denote-file-type~) that had none. We now handle this internally, thus -streamlining the experience for the user. - -Refer to the manual for the details: - - -Assuming the latest Info manual is installed, evaluate: - -#+begin_src emacs-lisp -(info "(denote) Rename multiple files at once") -#+end_src - -*** Renaming a single file based on its front matter -:PROPERTIES: -:CUSTOM_ID: h:d913e369-9325-46c4-985b-cf5b3e35372b -:END: - -Introduced the ~denote-rename-file-using-front-matter~ command. This is -new functionality we provide which uses the front matter as input to -perform a rename of the file. The aforementioned offerings prompt for -input via the minibuffer and propagate the changes firstly to the file -name and subsequently to the front matter. Whereas with the command -~denote-rename-file-using-front-matter~, the user can edit the front -matter manually and then invoke the command to pass the changes to the -file name, subject to a confirmation. Relevant entries are the title -and tags/filetags (depending on the file type). The date and the -identifier are not pertinent. Identifiers in file names are NEVER -rewritten by Denote. - -Consult the manual: -. - -With the latest package, evaluate: - -#+begin_src emacs-lisp -(info "(denote) Rename a single file based on its front matter") -#+end_src - -*** Renaming multiple files based on their front matter -:PROPERTIES: -:CUSTOM_ID: h:4efc6c14-fd71-4bd8-8bb1-e8e720b98eff -:END: - -The command ~denote-dired-rename-marked-files-using-front-matter~ -completes the set of features we provide for syncing between file name -and front matter. It applies to all marked files in a Dired buffer. - -Read the manual to understand how the command works and what it does -exactly: . - -Or evaluate: - -#+begin_src emacs-lisp -(info "(denote) Rename multiple files based on their front matter") -#+end_src - -*** Add missing front matter on demand -:PROPERTIES: -:CUSTOM_ID: h:32a103be-71a2-48e4-a18e-7727c04545ed -:END: - -Sometimes the user may have incomplete front matter, perhaps due to a -mistake that was saved on disk. The command ~denote-add-front-matter~ -appends a new front matter block to the current note. - -Read: - - -Or evaluate: - -#+begin_src emacs-lisp -(info "(denote) Regenerate front matter") -#+end_src - -** Faces for Denote links -:PROPERTIES: -:CUSTOM_ID: h:507fb46c-a2e9-48a7-8cd2-53c5fc73394d -:END: - -We provide the ~denote-faces-link~ and the ~denote-faces-broken-link~. -The latter is only relevant for Org, as Emacs' standard button mechanism -does not have a way to apply a face dynamically. - -This is a change for themes/tinkerers who need to differentiate -=denote:= links from other links. Otherwise, the presentation is the -same as before. - -Thanks to Peter Prevos for asking about it on the mailing list: -. - -** Use of XDG path in ~denote-directory~ -:PROPERTIES: -:CUSTOM_ID: h:efa3049e-f1fa-48ff-af7d-d16edc677704 -:END: - -The default value of the ~denote-directory~ user option used to be -=~/Documents/notes= (subject to some conversion via Elisp). Denote now -conforms with the freedesktop.org specifications by using the =XDG= -directory for =DOCUMENTS= instead of =~/Documents=: -. - -Users who already bind the ~denote-directory~ are not affected by this -change. Same for those who do not tinker with =XDG= environment -variables and/or do not use some exotic setup. - -Thanks to Philip Kaludercic for the patch: - - -** Bespoke major-mode for the backlinks' buffer -:PROPERTIES: -:CUSTOM_ID: h:feb9a0ed-ba15-486e-ae11-5b222b00bc31 -:END: - -The backlinks' buffer now uses the ~denote-backlink-mode~ instead of the -generic ~special-mode~. The former derives from the latter. It binds -keys to move between links with =n= (next) and =p= (previous). These -are stored in the ~denote-backlink-mode-map~ (use =M-x describe-mode= -(=C-h m=) in an unfamiliar buffer to learn more about it). - -Thanks to Philip Kaludercic for the patch: - - -** Changes to the manual -:PROPERTIES: -:CUSTOM_ID: h:80217a39-86b8-4310-b7c4-dcc14e0b98fd -:END: - -+ Documented all of the aforementioned. Improved how information is - presented and, generally, iterated on an already comprehensive - document. - -+ Introduced a node which explains how to tweak the front matter: - . - Or evaluate: - - #+begin_src emacs-lisp - (info "(denote) Change the front matter format") - #+end_src - -+ Updated the reference to =consult-notes=. This is a package that uses - the =consult= interface to provide access and search facilities for - notes. It can integrate with Denote. Thanks to Colin McLear for the - change in pull request 70 on the GitHub mirror: - . - - [ The change is below the ~15 line threshold and thus does not require - copyright assignment to the Free Software Foundation. ] - -** Internal restructuring -:PROPERTIES: -:CUSTOM_ID: h:5d09d0af-3c25-4419-8448-90b8e1adab0d -:END: - -+ All Denote code is consolidated in =denote.el=. We no longer maintain - separate files like =denote-link.el=, =denote-dired.el=, etc. Users - who had ~require~ calls to such libraries must remove them and only - keep: - - #+begin_src emacs-lisp - (require 'denote) - #+end_src - -+ User options that have an entry in the manual will now provide a link - to it via their Help buffer and/or the Custom UI. This is done by - adding the =:link= attribute to their declaration. - - Furthermore, user options and faces now specify the version of Denote - that last affected their value (e.g. ~denote-directory~, which was - mentioned above for the XDG spec, now informs the user that it changed - for version =0.5.0=). - - [ I learnt these by developing the =modus-themes=. ] - -+ The variables ~denote-last-title~, ~denote-last-keywords~, - ~denote-last-buffer~, and ~denote-last-front-matter~ are all obsolete. - These were used prior to version =0.1.0= to help with development but - are now deemed surplus to requirements. - -+ Lots of changes were made to private functions, variables, doc - strings, and comments, in the interest of simplifying the code and/or - ensuring consistency in how operations are carried out. Though - everything is the same for the end-user. - -Thanks to Jean-Philippe Gagné Guay for the numerous contributions on the -GitHub mirror. They are important for Denote, though the user does not -need to know what is happening internally (consult the Git log for the -details): - -- -- -- -- -- -- -- -- - -** Discussions -:PROPERTIES: -:CUSTOM_ID: h:79089c06-9e0c-49cc-9d53-a1a2fd72fb65 -:END: - -*** Encrypting Denote notes -:PROPERTIES: -:CUSTOM_ID: h:87e4556a-4864-4955-a98c-62b2e6a509c3 -:END: - -Paul van Gelder asked about this on the mailing list. I provided -guidelines on what can be done, though did not record anything in the -manual: I prefer to elicit more feedback from users. The gist is that -Emacs already has all the requisite functionality, though encryption per -se is outside the scope of Denote: -. - -Denote's relevant internal mechanisms will recognise files ending in -=.gpg= (e.g. for fontification in Dired). - -*** Visualise usage of Denote keywords -:PROPERTIES: -:CUSTOM_ID: h:d94ee5e3-0a54-404c-b44b-34edc3703fbc -:END: - -Peter Prevos shared a proof-of-concept way to visualise keywords in the -~denote-directory~ and show usage statistics: -. - -We do not include this information in the manual, as we wait for the -fully fledged code. Though do give it a try if you are interested and, -perhaps, share your thoughts for Peter's consideration. - -*** Conflict between ~denote-dired-mode~ and ~diredfl-mode~ -:PROPERTIES: -:CUSTOM_ID: h:0cbf504c-676c-436e-8ae8-e7115368e691 -:END: - -Hilde Rhyne shared a workaround they have to disable ~diredfl-mode~ in -the buffers where ~denote-dired-mode~ is enabled. The conflict between -the two is a known issue that is acknowledged in the manual: -. - -I think we need a proper solution in the code we provide, so this -workaround is not mentioned in the manual. - -*** Why doesn't Denote provide a search facility? -:PROPERTIES: -:CUSTOM_ID: h:068108f4-a4fa-4ff8-be49-f1f10a862451 -:END: - -There was a discussion started by Fourchaux, with the participation of -basaran and Andre0991 on the GitHub mirror: -. - -The gist of my answer is that Denote does not need to provide such a -facility because notes are ordinary files: whatever the user already has -for them should apply to Denote. If the user has nothing to search -through files, they anyhow need something that works outside the -confines of Denote: a =denote-SEARCH= command is not an adequate -solution. - -Emacs has numerous built-in commands, such as ~grep~ (~lgrep~ and -~rgrep~), ~project-find-regexp~, ~find-grep-dired~, ~ibuffer-do-occur~, -... Furthermore, there are lots of high quality packages that have -their own wrappers or extensions for searching file contents, such as -the =ivy= and =helm= completion frameworks, as well as =consult= (the -commands ~consult-grep~ and ~consult-ripgrep~), =consult-notes=, =rg=, -=deadgrep=, =deft=, and probably plenty more that do not come to mind -right now. - -I strongly encourage the user to find a universal search solution to the -problem of searching file contents. - -* Version 0.4.0 on 2022-07-25 -:PROPERTIES: -:CUSTOM_ID: h:1c8098ee-089c-4511-bc6a-4140aab01321 -:END: - -+ Defined the ~denote-link-dired-marked-notes~ command. It lets the - user produce a typographic list of links to the note files that are - marked in Dired. The list is written at point. If there are multiple - buffers which visit Denote notes, the command first prompts with - minibuffer completion for one among them. - - In terms of workflow, ~denote-link-dired-marked-notes~ complements the - ~denote-link-add-links~ command for those cases where it is easier to - select files than write an elegant regular expression. - -+ Implemented the ~denote-dired-rename-marked-files~ command. This - provides a much-requested facility to perform the familiar renaming - operation on a set of files. In particular: - - - the file's existing file name is retained and becomes the =TITLE= - field, per Denote's file-naming scheme; - - - the =TITLE= is sluggified and downcased, per our conventions; - - - an identifier is prepended to the =TITLE=; - - - the file's extension is retained; - - - a prompt is asked once for the =KEYWORDS= field and the input is - applied to all file names; - - - if the file is recognised as a Denote note, the command rewrites its - front matter to include the new keywords. A confirmation to carry - out this step is performed once at the outset. Note that the - affected buffers are not saved. The user can thus check them to - confirm that the new front matter does not cause any problems - (e.g. with the command ~diff-buffer-with-file~). Multiple buffers - can be saved with ~save-some-buffers~ (read its doc string). - - Parts of ~denote-dired-rename-marked-files~ were added or refined over - a series of commits. Consult the Git log for the minutia. Thanks to - Jean-Philippe Gagné Guay for the relevant additions in pull requests - 51 and 52 on the GitHub mirror: - - - - - - - Jean-Philippe has assigned copyright to the Free Software Foundation. - -+ Improved how the ~denote-dired-rename-file~ command rewrites front - matter. Before, it would perform a replacement of the whole block, - which had the adverse effect of overwriting custom front matter - entries. Now, it only targets the lines which hold the title and - keywords, leaving everything else intact. Thanks to Peter Prevos for - reporting the problem and testing the solution to it in issue 60 on - the GitHub mirror: . - -+ Introduced the ~denote-dired-rename-file-and-add-front-matter~ command - that always prepends front matter to a file whose extension is among - the supported ones (per the user option ~denote-file-type~). This - differs from the standard ~denote-dired-rename-file~ command which - only rewrites the front matter's title and keywords if they exist. - - In practice, ~denote-dired-rename-file-and-add-front-matter~ empowers - the user to convert a generic text file to a Denote note. - - This command was originally added by Jean-Philippe Gagné Guay in pull - request 49 on the GitHub mirror and refined in subsequent commits: - . Also read issue 48 - where this idea was originally discussed: - . - -+ Added the ~denote-dired-rename-marked-files-and-add-front-matters~ - command, which is like the ~denote-dired-rename-marked-files~ but adds - front matter instead of rewriting existing one, just how the command - ~denote-dired-rename-file-and-add-front-matter~ does it (both are - mentioned above). Thanks to Jean-Philippe Gagné Guay for the - refinements to it in pull request 53 on the GitHub mirror: - . - -+ Wrote an interactive spec for ~denote-link-buttonize-buffer~. It can - now be invoked with =M-x= or a key binding, should the need arise. - This function is normally called via a hook and takes effect in plain - text as well as Markdown files. - -+ Extended the fontification rules so that file names with non-ASCII - characters are styled properly. This issue was brought up on the - mailing list by Frank Ehmsen and was discussed with the participation - of Peter Prevos: - . - - The same topic was raised at the same time on the GitHub mirror by - user hpgisler in issue 61: - . - - After some discussion, we agreed on the right approach, which was - formalised by Peter Prevos as pull request 64 on the GitHub mirror: - . The change is below - the ~15 line threshold and thus does not require copyright assignment - to the Free Software Foundation. - -+ Made the registration of the =denote:= custom Org hyperlink type - conditional on the availability of the ~org~ feature. In other words, - those who do not use Org will not be loading this part of the code. - Thanks to Abin Simon for reporting the problem and for showing how - Elfeed handles this case. This was done in issue 47 on the GitHub - mirror: . - -+ Ensured that duplicate keywords are not produced by the relevant - prompt. Thanks to user Taoufik for the contribution in pull request - 50 on the GitHub mirror: . - The change is below the ~15 line threshold and thus does not require - copyright assignment to the Free Software Foundation. - -+ Fixed a typo in the reference to the ~crm-separator~ in the manual. - David Wilson (System Crafters channel) spotted the error in a recent - live stream whose main topic was about Denote (thanks, by the way!): - . - -+ Addressed an inconsistency in the command ~denote-link-find-file~ - where it would not recognise links without a title in their format - (those can be inserted by passing a prefix argument (=C-u= by default) - to the commands that insert links, such as ~denote-link~). - -+ Attached conditionality to the ~denote~ command's =SUBDIRECTORY= - argument, so that it does not create new file paths. This is only - relevant for those who call ~denote~ from Lisp. Interactive use is - the same as before. - -+ Clarified that the user option ~denote-org-capture-specifiers~ can - accept arbitrary text in addition to the formatting specifiers that - Org's capture mechanism introduces. - -+ Explained in the manual why ~denote-org-capture-specifiers~ is needed - instead of writing the capture template directly the way one normally - does. The gist is that because our file names are derived dynamically - based on user input, we need to account for the sequence in which the - value of arguments is reified by ~org-capture~. - -+ Refactored how notes are prepared internally. Thanks to Jean-Philippe - Gagné Guay for the contribution in pull request 55 on the GitHub - mirror: . - -+ Declared the ~denote-punctuation-excluded-extra-regexp~ variable which - is, for the time being, targeted at experienced users. Its purpose is - to extend what we consider "illegal" punctuation for the file name. - Thanks to pRot0ta1p for the feedback in issue 57 over at the GitHub - mirror: . Example - based on the input of pRot0ta1p: - - #+begin_src emacs-lisp - (setq denote-punctuation-excluded-extra-regexp - "[『』〖〗{}「」【】〔〕[]()《》〈〉«»!#¥%…&"'*,。;:、?—]*") - #+end_src - - The ideal is to make ~denote--punctuation-regexp~ work for all - scripts, but that may be unrealistic. - -+ Clarified what the manual means by "attachments" to notes. Those are - for Org, if the user resorts to the relevant Org mechanisms. Denote - does not do any of that. - -+ Revised the parsing of a date input as used in the ~denote-date~ - command or related. The idea is to turn =2020-01-15= into something - like =2020-01-15 16:19= by using the current time, so that the hour - and minute component is not left to =00:00= when the user does not - specify it explicitly. - - This reduces the burden on the user who would otherwise need to input - the time value in order to avoid the error of duplicate identifiers in - the scenario where the same date is used more than once. - - The change also addresses a difference between Emacs 28 and Emacs 29 - where the former does not read dates without a time component. - - Thanks to Peter Prevos for the feedback in issue 58 over at the GitHub - mirror: . - -+ Fixed compilation warnings in Emacs 29 about the format of doc strings - that need to output a literal single quote. Thanks to Kyle Meyer for - the patch, which was sent on the mailing list: - . - -+ Fixed typo in the user option ~denote-prompts~ about the - ~crm-separator~. Thanks to Kyle Meyer for the patch, which was sent - on the mailing list: - . - -+ Made the built-in =subr-x= library a runtime dependency, due to - complications with the ~when-let*~ form. The problem was made - manifest in a renaming operation, though it was not about renaming per - se. Thanks to hpgisler for reporting the problem in issue 62 and for - testing the proposed solution: - . - -+ Streamlined the use of the =seq= library instead of =cl-lib=, as we - were already using the former more heavily and there was no need for - the latter. Thanks to Philip Kaludercic for pointing this out on the - emacs-devel mailing list: - . - -+ Added a generic =README.md= file to placate the Git forges. Neither - SourceHut nor GitHub/GitLab are fully compliant with the Org markup we - use in =README.org= (we use Org because it is easy to generate the - Info manual and HTML pages out of it). SourceHut will not render the - file at all, while the others render it but do not parse it properly. - -+ Made several other internal tweaks and refinements in the interest of - robustness and/or clarity. - -+ Rewrote all relevant documentation. - -** Non-changes -:PROPERTIES: -:CUSTOM_ID: h:0ac79968-a575-4380-addc-d58cc2b5f627 -:END: - -The following are not part of any changes that were made during this -release cycle, though they provide potentially interesting insight into -the workings of the project. - -+ Identifiers with milliseconds :: Denote's identifier format extends up - to seconds. This is the product of years of experimentation and is, - in my opinion, the best compromise between usability/readability and - precision. If a user produces two notes within a fraction of a - second, then yes they will have duplicate identifiers. In principle, - there is no reason not to address this potential problem, provided we - do not compromise on Denote's file-naming scheme (making the - identifier less readable is a compromise). We shall see what the best - course of action is. Thanks to Felipe Balbi and Jean-Philippe Gagné - Guay for the discussion thus far in issue 54 on the GitHub mirror: - . - -+ Denote and evil-mode :: Users of evil-mode do not have to worry about - Denote, as we do not define any key bindings. The manual includes - sample configuration, which proposes some key bindings, but that is - the user's prerogative. Thanks to Saša Janiška and Alan Schmitt for - their participation on the mailing list: - . - -+ Denote and Citar :: Peter Prevos started developing a package that - connects Denote with Citar: . - The idea is to use notes as part of one's bibliography. Discussions - which include sample code on how to leverage ~denote~ from Lisp: - - - - - - - - -+ Denote and graph of connections :: Saša Janiška asked whether Denote - will provide some way to visualise links between notes. The answer is - negative. Denote's scope is clearly delineated and its feature set is - largely complete (notwithstanding refinements to what we already - provide). Peter Prevos is experimenting with some code that uses the - R language. Any such facility will have to be implemented as a - separate package. I remain at the disposal of anyone who needs help - with Denote's internals. Thanks to the aforementioned fellows for - their participation on the mailing list: - . - -+ Denote's scalability :: There was a discussion whether Denote will - work well with very large sets of files. The short answer is that it - will work the same way Emacs and/or standard Unix tools do: good - enough! If there are improvements to be made, which do not jeopardise - the principles of the project, we shall implement them without - hesitation. Thanks to Saša Janiška and Peter Prevos for their - participation on the mailing list: - . - -+ Denote's minimum requirement of Emacs 27.2 :: We cannot depend on - Emacs 27.1 due to this message from the byte compiler: - - : You should depend on (emacs "27.2") or the (org "9.3") package if you need `org-link-open-as-file'. - - Depending on Org is not an option because Denote optionally works - without Org, so Emacs 27.2 is what we have to opt for. If your - operating system does not provide this version in package format, - please petition its maintainers/providers to do so. Thanks to - Alexander for asking about it on the mailing list: - . - -Finally, a mildly interesting piece of trivia: we have exceeded 600 -commits since the first day of the project's Git history on 2022-06-04 -(the actual history is much longer). That averages to more than 10 per -day! I think things will slow down eventually. - -* Version 0.3.0 on 2022-07-11 -:PROPERTIES: -:CUSTOM_ID: h:6864cfd4-d0be-4c89-b313-39ba6e892a03 -:END: - -+ Fixed how references are analysed to produce the backlinks' buffer. - This should resolve the issue that some users faced where the - backlinks would not be produced. - - The previous implementation would not yield the appropriate results if - (i) the value of the user option ~denote-directory~ was a "project" - per the built-in project.el and (ii) the link to the given entry was - from a subdirectory. In short, the references were sometimes returned - as relative file paths, whereas they should always be absolute. - Thanks to Jean-Philippe Gagné Guay for the feedback in issue 42 over - at the GitHub mirror: . - - [ Jean-Philippe has assigned copyright to the Free Software - Foundation. It is a prerequisite for contributing to core Emacs - and/or any package distributed via the official GNU ELPA. ] - -+ Addressed a regression in the function ~denote-directory~ (this is the - function that normalises the variable of the same name) which - prevented it from returning an expanded file path. This too - contributed to problems with the backlinking facility. Thanks to - Jean-Philippe Gagné Guay for the contribution in pull request 44 over - at the GitHub mirror: . - - Also thanks to user pRot0ta1p for the relevant feedback in issue 43 - (also on the mirror): . - More thanks to Alfredo Borrás, Benjamin Kästner, and Sven Seebeck for - their comments in a related thread on the mailing list: - . - These discussions showed we had a problem, which we managed to - identify. - -+ Introduced the user option ~denote-prompts~ (read its doc string or - the relevant entry in the manual). It governs how the standard - ~denote~ command for creating new notes will behave in interactive - usage. By default, ~denote~ prompts for a title and keywords. With - ~denote-prompts~, the command can also ask for a file type (per - ~denote-file-type~), subdirectory of the ~denote-directory~, and a - specific date+time. Prompts occur in the order they are specified. - Furthermore, the ~denote-prompts~ can be set to values which do not - include the title and keywords. This means that the resulting file - names can be any of those permutations: - - : DATE.EXT - : DATE--TITLE.EXT - : DATE__KEYWORDS.EXT - - Recall that Denote's standard file-naming scheme is defined as follows - (read the manual for the details): - - : DATE--TITLE__KEYWORDS.EXT - - For our purposes, Denote will work perfectly fine for linking and - backlinking, even if file names do not include the =TITLE= and - =KEYWORDS= fields. However, the user is advised to consider the - implications on usability: notes without a descriptive title and/or - useful keywords may be hard to filter and practically impossible to - manage at scale. File names without such information should at least - be added to subdirectories which themselves have a descriptive name. - - At any rate, Denote does not have strong opinions about one's - workflow. The standard file name is the culmination of years of - experience. - - Consider the ~denote-prompts~ the affirmative answer to the question - "Can keywords be optional?" as posed by Jack Baty on the mailing list: - . - - Thanks to Jean-Philippe Gagné Guay for the original contribution in - commit 9b981a2. It was originally part of a pull request, but due to - some internal changes I had to merge it as a patch and technically the - web UI did not count the PR as "merged" (though it was in terms of - substance). - -+ Refactored the ~denote~ command to (i) accommodate the new user option - ~denote-prompts~ via its interactive specification and (ii) be more - flexible when called from Lisp. The latter scenario is for advanced - users or, generally, those who can maintain some custom code in their - configuration. A case in point is one of the examples we show in the - manual for a programmatic way to create notes that automatically get - the =journal= tag: - - #+begin_src emacs-lisp - (defun my-denote-journal () - "Create an entry tagged 'journal', while prompting for a title." - (interactive) - (denote - (denote--title-prompt) - '("journal"))) - #+end_src - - Notice that the ='("journal")= is a list of strings even for a single - keyword. Whereas before a single one was a plain string. This is a - breaking change. - - Please consult the doc string of the ~denote~ command for the - technicalities. - -+ Refashioned the interactive convenience functions of ~denote-type~, - ~denote-date~, ~denote-subdirectory~ to leverage the ~denote-prompts~ - user option while calling ~denote~ interactively. In practical terms, - they no longer accept any arguments when called from Lisp. Users who - need a programmatic approach are advised to either call ~denote~ - directly, or check how these commands ~let~ bind the ~denote-prompts~ - to carry out their operations. The doc string of each command - explains how it works. Or evaluate this to check the manual: - - #+begin_src emacs-lisp - (info "(denote) Convenience commands for note creation") - #+end_src - - Else visit: - - -+ Documented how the user option ~denote-directory~ can accept a local - value. This is pertinent to scenaria where the user needs to maintain - separate directories of notes. By "separate" we mean sets of notes - that do not communicate with each other, cannot create links between - them, etc. The manual delves into the technicalities. If you have - the Info entry installed, evaluate: - - #+begin_src emacs-lisp - (info "(denote) Maintain separate directories for notes") - #+end_src - - Else visit: - . - - Thanks to user "Summer Emacs" for starting the discussion on the - mailing list, and Benjamin Kästner for their participation: - . - -+ Added an entry to the manual's Frequently Asked Questions about a - failed search for backlinks. It includes sample code that users of - Windows can apply, if necessary. (The error is not Denote's fault.) - Thanks to Benjamin Kästner for the patch, which is below the ~15 line - threshold and thus does not require copyright assignment to the Free - Software Foundation: - . - -+ Removed duplicate entries from the list of file paths that the =xref= - library returns for the purposes of backlinking. Thanks to - Jean-Philippe Gagné Guay for the contribution in pull request 44 on - the GitHub mirror: . - -+ Applied an appropriate face to the backlinks' button to mitigate an - error. Thanks to Jean-Philippe Gagné Guay for the contribution in - pull request 45 on the GitHub mirror and for later testing a - subsequent tweak: . - -+ Simplfied all the faces we define to make them work with all themes. - The previous colours were consistent with the =modus-themes=: - . - -+ Refined how strings are sluggified under all circumstances. Before, a - nil value for the user option ~denote-allow-multi-word-keywords~ would - have the adverse effect of joining all the strings in the title field - of the file name. The intent always was to do that only for - multi-word keywords, not the title. This change was part of a hotfix, - formalised as version =0.2.1= a day after the release of =0.2.0=. - -+ Made the fontification rules more robust, while avoiding any false - positives. This was done over a series of commits as it had - implications for the file name permutations that were mentioned - earlier. Thanks to Jean-Philippe Gagné Guay for the patches and/or - discussion about the merits of each change and concomitant - considerations: - - - https://github.com/protesilaos/denote/pull/36 - - https://github.com/protesilaos/denote/pull/38 - - https://github.com/protesilaos/denote/pull/40 - - https://github.com/protesilaos/denote/pull/42 - -+ Rewrote all relevant entries in the manual to reflect all the - user-facing aspects of the aforementioned. - -+ Discussed a use-case of rewriting old journal entries as Denote-style - files. As of this writing, we do not support migration of files in - bulk. It might happen at some point, though it is no mean task. - Thanks to Summer Emacs and Alan Schmitt for their participation: - . - - An aside here as this topic was brought up: my packages are open to - users of all skill levels and is why I maintain a mailing list as well - as mirrors of the official git repository on SourceHut. Do not - hesitate to ask a question. If, for whatever reason, those - communication channels are not appropriate, you are welcome to contact - me in private: . - -Thanks again to Jean-Philippe Gagné Guay for the numerous contributions. -Please read the commit log for the minutia, as this change log entry -omitted some of the finer yet important details. - -* Version 0.2.0 on 2022-07-04 -:PROPERTIES: -:CUSTOM_ID: h:2002fee6-3f0c-48be-9727-6d4e20f34856 -:END: - -+ Version =0.1.0= (from 2022-06-27) was never built as a package. The - reason is that the GNU ELPA machinery reads the =Version:= header of - the main file, not the git tag. As the original commit in =denote.el= - included =Version: 0.1.0=, GNU ELPA rightly tries to build the package - using that reference. But because at that time I had not yet updated - the Copyright header to name the Free Software Foundation, the package - could not be prepared. As such, please consider this release to be - the "first formal stable version". My apologies for the delay, - contrary to what was promised in the last change log entry. - - - Prospective users are advised to read the manual: - . For a video demonstration: - . - - - Thanks to Benjamin Kästner for reporting the issue with the GNU ELPA - package on the mailing list: - . - -+ Originally, Denote was designed to only work with notes in a flat - directory. With code contributions from Jean-Philippe Gagné Guay, - support for subdirectories of the user option ~denote-directory~ is - now available. This covers the case of creating links between notes, - following them, and viewing the backlinks' buffer of the current - entry. - - - Thanks to Jean-Philippe for the contributions which took place on - the GitHub mirror: - - + - + - + - - - Jean-Philippe Gagné Guay has assigned copyright to the Free Software - Foundation. This is a prerequisite to contribute code to any - package on the official GNU ELPA archive (and to emacs.git for that - matter). - -+ The new ~denote-subdirectory~ command lets the user select a directory - to place the new note in. Available candidates are the value of the - ~denote-directory~ as well as all of its subdirectories, minus =.git=. - In future versions, we will consider how to provide a blocklist or a - regexp filter for the user to specify which subdirectories should be - omitted from minibuffer completion. Please consider providing your - feedback on the technicalities. - - - Thanks to Jean-Philippe Gagné Guay and Shreyas Ragavan for the - feedback in issue 31 on the GitHub mirror: - . - - - Thanks to Jean-Philippe Gagné Guay for fixing a potential problem in - how directories are represented when commands enter the directory - instead of selecting it (again, at the GitHub mirror): - . - -+ From 2022-06-24 to 2022-07-03, Denote provided support for links - between Org notes that leveraged the =id:= hyperlink type. - Discussions on the mailing list and the GitHub mirror revealed the - longer-term problems in our implementation. In the Annex below, I - provide my detailed opinion on the matter. The gist is that Denote - does not---and will not---create =id:= links between its notes, but - shall use the =denote:= hyperlink type instead (which works like the - standard =file:= type). As the Annex explains, Denote is not org-roam - lite and we try not to engender such false expectations. - - - Despite the fact that the relevant patches are no longer applicable, - I wish to thank Kaushal Modi and Jean-Philippe Gagné Guay for their - contributions over at the GitHub mirror: - - + - + - -+ The user option ~denote-date-format~ controls how the date and time is - recorded in the file's contents (what we call "front matter"). When - nil (the default value), we use a file-type-specific format (also - check the user option ~denote-file-type~): - - - For Org, an inactive timestamp is used, such as =[2022-06-30 Wed 15:31]=. - - - For Markdown, the RFC3339 standard is applied: =2022-06-30T15:48:00+03:00=. - - - For plain text, the format is that of ISO 8601: =2022-06-30=. - - If the value is a string, ignore the above and use it instead. The - string must include format specifiers for the date. These are described - in the doc string of ~format-time-string~. - - The ~denote-date-format~ supersedes the now obsolete - ~denote-front-matter-date-format~. - - Thanks to Peter Prevos and Kaushal Modi for their feedback in issue 27 - on the GitHub mirror: . - -+ All the faces we define are now declared in the =denote-faces.el= - file. The fontification rules are shared by ~denote-dired-mode~ and - the backlinks' buffer (invoked by ~denote-link-backlinks~ and - controlled by the user option ~denote-link-fontify-backlinks~). The - current list of faces: - - - ~denote-faces-date~ - - ~denote-faces-delimiter~ - - ~denote-faces-extension~ - - ~denote-faces-keywords~ - - ~denote-faces-subdirectory~ - - ~denote-faces-time~ - - ~denote-faces-title~ - -+ Named the mailing list address as the =Maintainer:= of Denote. - Together with the other package headers, they help the user find our - primary sources and/or communication channels. This change conforms - with work being done upstream in package.el by Philip Kaludercic. I - was informed about it here: - . - -+ Fixed how keywords are inferred and combined. The previous code did not - work properly when the user option =denote-infer-keywords= was nil. - It would return a list of symbols, with the parentheses, whereas the - file name needs a string where each keyword is delimited by an - underscore. - -+ Simplified how information in the front matter is retrieved. It fixes - cases where, for example, a special character at the end of the title - was ignored. Thanks to Jean-Philippe Gagné Guay for the patch over at - the GitHub mirror: . - -+ Rewrote parts of the manual in the interest of clarity. - -** Annex about discontinuing support for org-id -:PROPERTIES: -:CUSTOM_ID: h:647d6155-1ac3-4ecb-bd4c-06d09fecd3ba -:END: - -My thanks for their participation in the discussions go to Jean-Philippe -Gagné Guay, Kaushal Modi, and Shreyas Ragavan. - -#+begin_example -commit f35ef05cb451f265213c3aafc1e62c425b1ff043 -Author: Protesilaos Stavrou -Date: Sun Jul 3 17:34:38 2022 +0300 - - REMOVE support for 'id:' hyperlink types - - The original idea was to support the 'org-id' library on the premise - that it makes Denote a good Emacs citizen. However, discussions on the - mailing list[0] and the GitHub mirror[1] have made it clear to me that - 'org-id' is not consistent with Denote's emphasis on simplicity. - - To support the way 'org-id' works, we will eventually have to develop - some caching mechanism, just how the org-roam package does it. This is - because the variable 'org-id-extra-files' needs to be kept up-to-date - whenever an operation on a file is performed. At scale, this sort of - monitoring requires specialised software. Such a mechanism is outside - the scope of Denote---if you need a db, use org-roam which is already - great. - - [0] - - [1] - - Quote of what I wrote on the GitHub mirror issue 29: - - [ggjp] This is what I was implying. That we are, in fact, - providing an option that is not viable long-term, but keeping - the option for expert users who will be able to handle this. - And we should warn about this clearly in the doc of that option. - - [protesilaos] What you write here @ggjp and what @shrysr explained - tells me that those expert users will need to be real experts. To - put it concretely, I am an experienced Emacs user with no - programming background, who has written several Emacs - packages (including the modus-themes which are built into Emacs), - but I have zero knowledge of using a db or of handling things with - python and the like. So if I opt in to 'denote-link-use-org-id' I - will eventually run into problems that my non-existent skills will - prevent me from solving. At that point, I will just use org-roam - which already handles this use-case in a competent way (and has a - massive community to rely on in case I need further support). - - If each package needs to write its own optimisations and maintain - its own cache, to me this shows that 'org-id' is not good enough for - the time being: more work needs to be done in org.git to provide a - universal solution. - - I wanted to support 'org-id' by default on the premise that Denote - must be a good Emacs citizen which interoperates with the rest of - the wider ecosystem. But if 'org-id' leaves something to be - desired, then that goal is not worth pursuing: we add complexity to - our code, offer an option that we cannot genuinely/adequately - support, and make usage of it contingent on reading the docs and - having a high level of expertise. - - I think being a good Emacs citizen is a laudable principle. In this - case, the right thing to do is to recommend the use of org-roam - instead of trying to accommodate 'org-id'. As such, I have now - changed my mind and think we should remove what we previously added. - - For some context here: the reason I never used org-roam is - because (i) it is Org-specific whereas I write notes in different - file types and (ii) I did not want to ever rely on a db or - equivalent dependency. - - - - README.org | 226 ++++++++--------------------------------------------- - denote-link.el | 99 ++++++----------------- - denote-retrieve.el | 2 +- - denote.el | 14 +--- - 4 files changed, 63 insertions(+), 278 deletions(-) -#+end_example - -Followed up by my explanation: - -#+begin_src text -> can we not have denote style links to be default for (de)notes - and -> explicitly supported, while if they need to, users can still link -> denote org files via org-id to any other notes/files (and vice versa) -> -- in which case performance + testing for org-id driven linking is -> not within Denote's purview at all? - -The formal support for `id:` links was added shortly before the release -of version `0.1.0`. In the days prior, we supported what you describe -via the manual. The user could change the `denote-org-front-matter` -variable to include a `PROPERTIES` drawer. This possibility still -exists, though yesterday I removed the relevant entry from the manual. -This way only the real do-it-yourself experts will go down that path. - -My concern here is with managing expectations. If our Org notes are -superficially the same as org-roam's, an unsuspecting user may think -that Denote is an org-roam lite. We will thus get issues/requests, such -as those already mentioned in this GitHub repo, about migrating from -org-roam to Denote. While there are similarities, Denote is not a -minimalist org-roam and I would not like to encourage the idea of -treating the two as interchangeable. - -Doing things half-way-through is a way to create false expectations. A -package on GNU ELPA must be usable by users of all skill levels. If the -functionality we provide is incomplete and needs to be covered by -user-level tweaks, we are excluding a portion of the user base while -still assuming the maintenance burden. If someone trusts Denote to, -say, write a 1000 notes, we do not want to surprise them after the fact. -Imagine if the reported issues that triggered this change happened 6 -months into one's daily usage of Denote: it wouldn't be nice. - -Setting the right expectations is a matter of responsibility: we let the -user make a more informed choice and show respect for their time. It -also makes it easier for me to keep Denote's scope in check by not -supporting every little extra that Org implements. The premier Org -extension is org-roam: we do not need another one (or, if we do, I am -not the one to implement it). - -,* * * - -Some comments on the `denote:` hyperlink type for Org as they may be -relevant in this context: - -,* It is meant to work like the standard `file:` type. This means that - it links to a file, while it can also have additional search - parameters, as explained in the Org manual. Evaluate: - - (info "(org) Search Options") - -,* It does not read the front matter, but only the file name. You can - create a note as usual, delete all its contents, save it, and try to - link to it from another note. It works. - -,* Exporting now works like the `file:` type for HTML, LaTeX, Texinfo, - and Markdown. Technically, it also supports the ASCII backend but the - format of the output could be tweaked further. - -There may be refinements to be made, which is okay as that is part of a -maintainer's duties. -#+end_src - -* Version 0.1.0 on 2022-06-27 -:PROPERTIES: -:CUSTOM_ID: h:33939747-ad60-4913-a170-4b2f48f139cc -:END: - -The present entry is intended for early adopters of Denote who may have -not caught up with the latest developments. Prospective users are -advised to read the manual: . For -a video demonstration: . - -+ The =denote= package on GNU ELPA will be available a few hours after - this release. GNU ELPA provides the latest stable release. To use a - development snapshot, read: - . - -+ Remember that any significant contribution (above ~15 lines) requires - copyright assignment to the Free Software Foundation. A form with - instructions is included in the manual's "Contributing" section: - . - -+ The front matter of notes in Org has changed to be compliant with the - standard =org-id= infrastructure. A =PROPERTIES= drawer is added to - the top of the file, which includes an =ID= property with the value of - the Denote identifier. Sample: - - #+begin_src org - :PROPERTIES: - :ID: 20220610T202537 - :END: - ,#+title: Sample Org front matter - ,#+date: 2022-06-10 - ,#+filetags: denote testing - #+end_src - -+ The front matter of Markdown (YAML or TOML) and plain text files - remains constant. For completeness, this is how they look: - - #+begin_src md - --- - title: "Sample with Markdown and YAML" - date: 2022-06-10 - tags: denote testing - identifier: "20220610T202021" - --- - #+end_src - - #+begin_src md - +++ - title = "Sample with Markdown and TOML" - date = 2022-06-10 - tags = ["denote", "testing"] - identifier = "20220610T201510" - +++ - #+end_src - - #+begin_example - title: Sample plain text - date: 2022-06-10 - tags: denote testing - identifier: 20220610T202232 - --------------------------- - #+end_example - -+ The integration with =org-id= extends to how linking works. By - default, Denote uses its own custom hyperlink type which starts with - the =denote:= prefix. In Org, it works like the =file:= type. When - the user option ~denote-link-use-org-id~ is non-nil, links from Org - notes to other Org notes will use the standard =id:= type instead. As - this is an Org-specific feature, Denote takes care to use the - major-mode-agnostic =denote:= type when the link targets a non-Org - note. - -+ In Org files the links created by Denote are buttonized automatically. - For Markdown and plain text, we use our own methods. When a link is - inserted it is buttonized outright. To buttonize links in existing - notes while visiting them in a buffer, add/evaluate this (it excludes - Org on its own): - - #+begin_src emacs-lisp - (add-hook 'find-file-hook #'denote-link-buttonize-buffer) - #+end_src - -+ The generation of the backlinks' buffer now uses the built-in =xref= - library instead of relying on a hardcoded call to the =find= - executable. This means that the ~denote-link-backlinks~ command will, - in principle, work properly with all Emacs builds. - -+ Users of Emacs 28 or higher can configure ~xref-search-program~ to - change from the default =grep= to =ripgrep=, =ugrep=, or a - user-defined alternative. - -+ This is the first stable release of Denote. It covers close to 400 - commits starting from 2022-06-04. Denote is the successor to a toy - package of mine, USLS, whose first public version was made available - in early November 2020: . - -+ Thanks to everyone involved in the development of Denote. Code - contributions, bug reports, discussion of ideas, are all valuable. - From A-Z the names mentioned in the manual's "Acknowledgements" - section: Colin McLear, Damien Cassou, Frank Ehmsen, Jack Baty, Kaushal - Modi, Peter Povinec, Sven Seebeck, Ypot. - -+ Sources of Denote: - - + Package name (GNU ELPA): =denote= - + Official manual: - + Change log: - + Git repo on SourceHut: - - Mirrors: - + GitHub: - + GitLab: - + Mailing list: blob - 5b040d3ee212f49e6d097d3d83df82e81933b798 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/README-elpa +++ /dev/null @@ -1,5813 +0,0 @@ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - DENOTE: SIMPLE NOTES WITH AN EFFICIENT - FILE-NAMING SCHEME - - Protesilaos Stavrou - info@protesilaos.com - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - -This manual, written by Protesilaos Stavrou, describes the customization -options for the Emacs package called `denote' (or `denote.el'), and -provides every other piece of information pertinent to it. - -The documentation furnished herein corresponds to stable version 2.3.0, -released on 2024-03-24. Any reference to a newer feature which does not -yet form part of the latest tagged commit, is explicitly marked as such. - -Current development target is 3.0.0-dev. - -⁃ Package name (GNU ELPA): `denote' -⁃ Official manual: -⁃ Change log: -⁃ Git repositories: - ⁃ GitHub: - ⁃ GitLab: -⁃ Video demo: -⁃ Backronyms: Denote Everything Neatly; Omit The Excesses. Don’t Ever - Note Only The Epiphenomenal. - -If you are viewing the README.org version of this file, please note that -the GNU ELPA machinery automatically generates an Info manual out of it. - -Table of Contents -───────────────── - -1. COPYING -2. Overview -3. Points of entry -.. 1. Standard note creation -..... 1. The `denote-prompts' option -..... 2. The `denote-history-completion-in-prompts' option -..... 3. The `denote-templates' option -..... 4. Convenience commands for note creation -..... 5. The `denote-save-buffer-after-creation' option -..... 6. The `denote-date-prompt-use-org-read-date' option -.. 2. Create a note from the current Org subtree -.. 3. Create note using Org capture -.. 4. Create note with specific prompts using Org capture -.. 5. Create a note with the region’s contents -.. 6. Open an existing note or create it if missing -.. 7. Maintain separate directory silos for notes -..... 1. Use custom commands to select a silo -..... 2. The `denote-silo-extras.el' -.. 8. Exclude certain directories from all operations -.. 9. Exclude certain keywords from being inferred -.. 10. Use Denote commands from the menu bar or context menu -4. Renaming files -.. 1. Rename a single file -..... 1. The `denote-rename-no-confirm' option -.. 2. Rename a single file based on its front matter -.. 3. Rename multiple files interactively -.. 4. Rename multiple files at once by asking only for keywords -.. 5. Rename multiple files based on their front matter -.. 6. Rename a file by changing only its file type -.. 7. Rename a file by adding or removing keywords interactively -.. 8. Rename a file by adding or removing a signature interactively -.. 9. Faces used by rename commands -5. The file-naming scheme -.. 1. Sluggification of file name components -.. 2. User-defined sluggification of file name components -.. 3. Features of the file-naming scheme for searching or filtering -6. Front matter -.. 1. Change the front matter format -.. 2. Regenerate front matter -7. Linking notes -.. 1. Adding a single link -.. 2. The `denote-org-store-link-to-heading' user option -.. 3. Insert link to an Org file with a further pointer to a heading -.. 4. Insert links matching a regexp -.. 5. Insert link to file with signature -.. 6. Insert links from marked files in Dired -.. 7. Link to an existing note or create a new one -.. 8. The backlinks’ buffer -.. 9. Writing metanotes -.. 10. Visiting linked files via the minibuffer -.. 11. Convert `denote:' links to `file:' links -.. 12. Miscellaneous information about links -..... 1. Aliases for the linking commands -..... 2. The `denote-link-description-function' to format links -8. Choose which commands to prompt for -9. Fontification in Dired -10. Automatically rename Denote buffers -.. 1. The `denote-rename-buffer-format' option -11. Use Org dynamic blocks -.. 1. Org dynamic blocks to insert links or backlinks -.. 2. Org dynamic block to insert file contents -12. Sort files by component -13. Keep a journal or diary -.. 1. Journaling with a timer -14. Minibuffer histories -15. Extending Denote -.. 1. Create a new note in any directory -.. 2. Narrow the list of files in Dired -.. 3. Use `dired-virtual-mode' for arbitrary file listings -.. 4. Use Embark to collect minibuffer candidates -.. 5. Search file contents -.. 6. Bookmark the directory with the notes -.. 7. Use the `denote-explore' package to explore your notes -.. 8. Use the `citar-denote' package for bibliography notes -.. 9. Use the `consult-notes' package -.. 10. Use the `denote-menu' package -.. 11. Treat your notes as a project -.. 12. Use the tree-based file prompt for select commands -.. 13. Rename files with Denote in the Image Dired thumbnails buffer -.. 14. Rename files with Denote using `dired-preview' -.. 15. Avoid duplicate identifiers when exporting Denote notes -..... 1. Export Denote notes with Org Mode -..... 2. Export Denote notes with Markdown -16. Installation -.. 1. GNU ELPA package -.. 2. Manual installation -17. Sample configuration -18. For developers or advanced users -19. Troubleshoot Denote in a pristine environment -20. Contributing -.. 1. Wishlist of what we can do to extend Denote -21. Publications about Denote -22. Alternatives to Denote -.. 1. Alternative implementations and further reading -23. Frequently Asked Questions -.. 1. Why develop Denote when PACKAGE already exists? -.. 2. Why not rely exclusively on Org? -.. 3. Why care about Unix tools when you use Emacs? -.. 4. Why many small files instead of few large ones? -.. 5. Does Denote perform well at scale? -.. 6. I add TODOs to my notes; will many files slow down the Org agenda? -.. 7. I want to sort by last modified in Dired, why won’t Denote let me? -.. 8. How do you handle the last modified case? -.. 9. Speed up backlinks’ buffer creation? -.. 10. Why do I get “Search failed with status 1” when I search for backlinks? -.. 11. Why do I get a double `#+title' in Doom Emacs? -24. Acknowledgements -25. GNU Free Documentation License -26. Indices -.. 1. Function index -.. 2. Variable index -.. 3. Concept index - - -1 COPYING -═════════ - - Copyright (C) 2022-2024 Free Software Foundation, Inc. - - Permission is granted to copy, distribute and/or modify - this document under the terms of the GNU Free - Documentation License, Version 1.3 or any later version - published by the Free Software Foundation; with no - Invariant Sections, with the Front-Cover Texts being “A - GNU Manual,” and with the Back-Cover Texts as in (a) - below. A copy of the license is included in the section - entitled “GNU Free Documentation License.” - - (a) The FSF’s Back-Cover Text is: “You have the freedom to - copy and modify this GNU manual.” - - -2 Overview -══════════ - - Denote aims to be a simple-to-use, focused-in-scope, and effective - note-taking and file-naming tool for Emacs. - - Denote is based on the idea that files should follow a predictable and - descriptive file-naming scheme. The file name must offer a clear - indication of what the contents are about, without reference to any - other metadata. Denote basically streamlines the creation of such - files or file names while providing facilities to link between them - (where those files are editable). - - Denote’s file-naming scheme is not limited to “notes”. It can be used - for all types of file, including those that are not editable in Emacs, - such as videos. Naming files in a consistent way makes their - filtering and retrieval considerably easier. Denote provides relevant - facilities to rename files, regardless of file type. - - Denote is based on the following core design principles: - - Predictability - File names must follow a consistent and descriptive naming - convention ([The file-naming scheme]). The file name alone - should offer a clear indication of what the contents are, - without reference to any other metadatum. This convention is - not specific to note-taking, as it is pertinent to any form of - file that is part of the user’s long-term storage ([Renaming - files]). - - Composability - Be a good Emacs citizen, by integrating with other packages or - built-in functionality instead of re-inventing functions such as - for filtering or greping. The author of Denote (Protesilaos, - aka “Prot”) writes ordinary notes in plain text (`.txt'), - switching on demand to an Org file only when its expanded set of - functionality is required for the task at hand ([Points of - entry]). - - Portability - Notes are plain text and should remain portable. The way Denote - writes file names, the front matter it includes in the note’s - header, and the links it establishes must all be adequately - usable with standard Unix tools. No need for a database or some - specialised software. As Denote develops and this manual is - fully fleshed out, there will be concrete examples on how to do - the Denote-equivalent on the command-line. - - Flexibility - Do not assume the user’s preference for a note-taking - methodology. Denote is conceptually similar to the Zettelkasten - Method, which you can learn more about in this detailed - introduction: . Notes - are atomic (one file per note) and have a unique identifier. - However, Denote does not enforce a particular methodology for - knowledge management, such as a restricted vocabulary or - mutually exclusive sets of keywords. Denote also does not check - if the user writes thematically atomic notes. It is up to the - user to apply the requisite rigor and/or creativity in pursuit - of their preferred workflow ([Writing metanotes]). - - Hackability - Denote’s code base consists of small and reusable functions. - They all have documentation strings. The idea is to make it - easier for users of varying levels of expertise to understand - what is going on and make surgical interventions where necessary - (e.g. to tweak some formatting). In this manual, we provide - concrete examples on such user-level configurations ([Keep a - journal or diary]). - - Now the important part… “Denote” is the familiar word, though it also - is a play on the “note” concept. Plus, we can come up with acronyms, - recursive or otherwise, of increasingly dubious utility like: - - ⁃ Don’t Ever Note Only The Epiphenomenal - ⁃ Denote Everything Neatly; Omit The Excesses - - But we’ll let you get back to work. Don’t Eschew or Neglect your - Obligations, Tasks, and Engagements. - - -[The file-naming scheme] See section 5 - -[Renaming files] See section 4 - -[Points of entry] See section 3 - -[Writing metanotes] See section 7.9 - -[Keep a journal or diary] See section 13 - - -3 Points of entry -═════════════════ - - There are five main ways to write a note with Denote: invoke the - `denote', `denote-type', `denote-date', `denote-subdirectory', - `denote-template', `denote-signature' commands, or leverage the - `org-capture-templates' by setting up a template which calls the - function `denote-org-capture'. We explain all of those in the - subsequent sections. Other more specialised commands exist as well, - which one shall learn about as they read through this manual. We do - not want to overwhelm the user with options at this stage. - - -3.1 Standard note creation -────────────────────────── - - The `denote' command will prompt for a title. If a region is active, - the text of the region becomes the default at the minibuffer prompt - (meaning that typing `RET' without any input will use the default - value). Once the title is supplied, the `denote' command will then - ask for keywords. The resulting note will have a file name as already - explained: [The file naming scheme] - - The `denote' command runs the hook `denote-after-new-note-hook' after - creating the new note. - - The file type of the new note is determined by the user option - `denote-file-type' ([Front matter]). - - The keywords’ prompt supports minibuffer completion. Available - candidates are those defined in the user option - `denote-known-keywords'. More candidates can be inferred from the - names of existing notes, by setting `denote-infer-keywords' to non-nil - (which is the case by default). - - Multiple keywords can be inserted by separating them with a comma (or - whatever the value of the `crm-separator' is—which should be a comma). - When the user option `denote-sort-keywords' is non-nil (the default), - keywords are sorted alphabetically (technically, the sorting is done - with `string-lessp'). - - The interactive behaviour of the `denote' command is influenced by the - user option `denote-prompts' ([The denote-prompts option]). - - The `denote' command can also be called from Lisp. Read its doc - string for the technicalities. - - In the interest of discoverability, `denote' is also available under - the alias `denote-create-note'. - - -[The file naming scheme] See section 5 - -[Front matter] See section 6 - -[The denote-prompts option] See section 3.1.1 - -3.1.1 The `denote-prompts' option -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - The user option `denote-prompts' determines how the `denote' command - will behave interactively ([Standard note creation]). - - Commands that prompt for user input to construct a Denote file name - include, but are not limited to: `denote', `denote-signature', - `denote-type', `denote-date', `denote-subdirectory', - `denote-rename-file', `denote-dired-rename-files'. - - • [Convenience commands for note creation]. - • [Renaming files]. - - The value of this user option is a list of symbols, which includes any - of the following: - - • `title': Prompt for the title of the new note ([The - `denote-history-completion-in-prompts' option]). - - • `keywords': Prompts with completion for the keywords of the new - note. Available candidates are those specified in the user option - `denote-known-keywords'. If the user option `denote-infer-keywords' - is non-nil, keywords in existing note file names are included in the - list of candidates. The `keywords' prompt uses - `completing-read-multiple', meaning that it can accept multiple - keywords separated by a comma (or whatever the value of - `crm-separator' is). - - • `file-type': Prompts with completion for the file type of the new - note. Available candidates are those specified in the user option - `denote-file-type'. Without this prompt, `denote' uses the value of - `denote-file-type'. - - • `subdirectory': Prompts with completion for a subdirectory in which - to create the note. Available candidates are the value of the user - option `denote-directory' and all of its subdirectories. Any - subdirectory must already exist: Denote will not create it. - - • `date': Prompts for the date of the new note. It will expect an - input like 2022-06-16 or a date plus time: 2022-06-16 14:30. - Without the `date' prompt, the `denote' command uses the - `current-time'. - - [The denote-date-prompt-use-org-read-date option]. - - • `template': Prompts for a KEY among the `denote-templates'. The - value of that KEY is used to populate the new note with content, - which is added after the front matter ([The denote-templates - option]). - - • `signature': - Prompts for an arbitrary string that can be used to - establish a sequential relationship between files (e.g. 1, 1a, 1b, - 1b1, 1b2, …). Signatures have no strictly defined function and are - up to the user to apply as they see fit. One use-case is to - implement Niklas Luhmann’s Zettelkasten system for a sequence of - notes (Folgezettel). Signatures are not included in a file’s front - matter. They are reserved solely for creating a sequence in a file - listing, at least for the time being. To insert a link that - includes the signature, use the command `denote-link-with-signature' - ([Insert link to file with signature]). - - The prompts occur in the given order. - - If the value of this user option is nil, no prompts are used. The - resulting file name will consist of an identifier (i.e. the date and - time) and a supported file type extension (per `denote-file-type'). - - Recall that Denote’s standard file-naming scheme is defined as follows - ([The file-naming scheme]): - - ┌──── - │ DATE--TITLE__KEYWORDS.EXT - └──── - - - If either or both of the `title' and `keywords' prompts are not - included in the value of this variable, file names will be any of - those permutations: - - ┌──── - │ DATE.EXT - │ DATE--TITLE.EXT - │ DATE__KEYWORDS.EXT - └──── - - - When in doubt, always include the `title' and `keywords' prompts. - - Finally, this user option only affects the interactive use of the - `denote' or other relevant commands (advanced users can call it from - Lisp). In Lisp usage, the behaviour is always what the caller - specifies, based on the supplied arguments. - - -[Standard note creation] See section 3.1 - -[Convenience commands for note creation] See section 3.1.4 - -[Renaming files] See section 4 - -[The `denote-history-completion-in-prompts' option] See section 3.1.2 - -[The denote-date-prompt-use-org-read-date option] See section 3.1.6 - -[The denote-templates option] See section 3.1.3 - -[Insert link to file with signature] See section 7.5 - -[The file-naming scheme] See section 5 - - -3.1.2 The `denote-history-completion-in-prompts' option -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - The user option `denote-history-completion-in-prompts' toggles history - completion in all `denote-prompts-with-history-as-completion'. - - When this user option is set to a non-nil value, Denote will use - minibuffer history entries as completion candidates in all of the - `denote-prompts-with-history-as-completion'. Those will show previous - inputs from their respective history as possible values to select, - either to (i) re-insert them verbatim or (ii) with the intent to edit - them further (depending on the minibuffer user interface, one can - select a candidate with `TAB' without exiting the minibuffer, as - opposed to what `RET' normally does by selecting and exiting). - - When this user option is set to a nil value, all of the - `denote-prompts-with-history-as-completion' will not use minibuffer - completion: they will just prompt for a string of characters. Their - history is still available through all the standard ways of retrieving - minibuffer history, such as with the command - `previous-history-element'. - - History completion still allows arbitrary values to be provided as - input: they do not have to match the available minibuffer completion - candidates. - - Note that some prompts, like `denote-keywords-prompt', always use - minibuffer completion, due to the specifics of their data. - - [ Consider enabling the built-in `savehist-mode' to persist minibuffer - histories between sessions.] - - -3.1.3 The `denote-templates' option -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - The user option `denote-templates' is an alist of content templates - for new notes. A template is arbitrary text that Denote will add to a - newly created note right below the front matter. - - Templates are expressed as a `(KEY . STRING)' association. - - • The `KEY' is the name which identifies the template. It is an - arbitrary symbol, such as `report', `memo', `statement'. - - • The `STRING' is ordinary text that Denote will insert as-is. It can - contain newline characters to add spacing. Below we show some - concrete examples. - - The user can choose a template either by invoking the command - `denote-template' or by changing the user option `denote-prompts' to - always prompt for a template when calling the `denote' command. - - [The denote-prompts option]. - - [Convenience commands for note creation]. - - Templates can be written directly as one large string. For example - (the `\n' character is read as a newline): - - ┌──── - │ (setq denote-templates - │ '((report . "* Some heading\n\n* Another heading") - │ (memo . "* Some heading - │ - │ * Another heading - │ - │ "))) - └──── - - Long strings may be easier to type but interpret indentation - literally. Also, they do not scale well. A better way is to use some - Elisp code to construct the string. This would typically be the - `concat' function, which joins multiple strings into one. The - following is the same as the previous example: - - ┌──── - │ (setq denote-templates - │ `((report . "* Some heading\n\n* Another heading") - │ (memo . ,(concat "* Some heading" - │ "\n\n" - │ "* Another heading" - │ "\n\n")))) - └──── - - Notice that to evaluate a function inside of an alist we use the - backtick to quote the alist (NOT the straight quote) and then prepend - a comma to the expression that should be evaluated. The `concat' form - here is not sensitive to indentation, so it is easier to adjust for - legibility. - - DEV NOTE: We do not provide more examples at this point, though feel - welcome to ask for help if the information provided herein is not - sufficient. We shall expand the manual accordingly. - - -[The denote-prompts option] See section 3.1.1 - -[Convenience commands for note creation] See section 3.1.4 - - -3.1.4 Convenience commands for note creation -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - Sometimes the user needs to create a note that has different - requirements from those of `denote' ([Standard note creation]). While - this can be achieved globally by changing the `denote-prompts' user - option, there are cases where an ad-hoc method is the appropriate one - ([The denote-prompts option]). - - To this end, Denote provides the following interactive convenience - commands for note creation. They all work by appending a new prompt to - the existing `denote-prompts'. - - Create note by specifying file type - The `denote-type' command creates a note while prompting for a - file type. - - This is the equivalent of calling `denote' when `denote-prompts' - has the `file-type' prompt appended to its existing prompts. In - practical terms, this lets you produce, say, a note in Markdown - even though you normally write in Org ([Standard note - creation]). - - The `denote-create-note-using-type' is an alias of - `denote-type'. - - Create note using a date - Normally, Denote reads the current date and time to construct - the unique identifier of a newly created note ([Standard note - creation]). Sometimes, however, the user needs to set an - explicit date+time value. - - This is where the `denote-date' command comes in. It creates a - note while prompting for a date. The date can be in - YEAR-MONTH-DAY notation like `2022-06-30' or that plus the time: - `2022-06-16 14:30'. - - [The denote-date-prompt-use-org-read-date option]. - - This is the equivalent of calling `denote' when `denote-prompts' - has the `date' prompt appended to its existing prompts. - - The `denote-create-note-using-date' is an alias of - `denote-date'. - - Create note in a specific directory - The `denote-subdirectory' command creates a note while prompting - for a subdirectory. Available candidates include the value of - the variable `denote-directory' and any subdirectory thereof - (Denote does not create subdirectories). - - This is the equivalent of calling `denote' when `denote-prompts' - has the `subdirectory' prompt appended to its existing prompts. - - The `denote-create-note-in-subdirectory' is a more descriptive - alias of `denote-subdirectory'. - - Create note and add a template - The `denote-template' command creates a new note and inserts the - specified template below the front matter ([The denote-templates - option]). Available candidates for templates are specified in - the user option `denote-templates'. - - This is the equivalent of calling `denote' when `denote-prompts' - has the `template' prompt appended to its existing prompts. - - The `denote-create-note-with-template' is an alias of the - command `denote-template', meant to help with discoverability. - - Create note with a signature - The `denote-signature' command first prompts for an arbitrary - string to use in the optional `SIGNATURE' field of the file name - and then asks for a title and keywords. Signatures are - arbitrary strings of alphanumeric characters which can be used - to establish sequential relations between file at the level of - their file name (e.g. 1, 1a, 1b, 1b1, 1b2, …). - - This is the equivalent of calling `denote' when `denote-prompts' - has the `signature' prompt appended to its existing prompts. - - The `denote-create-note-using-signature' is an alias of the - command `denote-signature' intended to make the functionality - more discoverable. - - -[Standard note creation] See section 3.1 - -[The denote-prompts option] See section 3.1.1 - -[The denote-date-prompt-use-org-read-date option] See section 3.1.6 - -[The denote-templates option] See section 3.1.3 - -◊ 3.1.4.1 Write your own convenience commands - - The convenience commands we provide only cover some basic use-cases - ([Convenience commands for note creation]). The user may require - combinations that are not covered, such as to prompt for a template - and for a subdirectory, instead of only one of the two. To this end, - we show how to follow the code we use in Denote to write your own - variants of those commands. - - First let’s take a look at the definition of one of those commands. - They all look the same, but we use `denote-subdirectory' for this - example: - - ┌──── - │ (defun denote-subdirectory () - │ "Create note while prompting for a subdirectory. - │ - │ Available candidates include the value of the variable - │ `denote-directory' and any subdirectory thereof. - │ - │ This is equivalent to calling `denote' when `denote-prompts' is - │ set to '(subdirectory title keywords)." - │ (declare (interactive-only t)) - │ (interactive) - │ (let ((denote-prompts '(subdirectory title keywords))) - │ (call-interactively #'denote))) - └──── - - The hyphenated word after `defun' is the name of the function. It has - to be unique. Then we have the documentation string (or “doc string”) - which is for the user’s convenience. - - This function is `interactive', meaning that it can be called via - `M-x' or be assigned to a key binding. Then we have the local binding - of the `denote-prompts' to the desired combination (“local” means - specific to this function without affecting other contexts). Lastly, - it calls the standard `denote' command interactively, so it uses all - the prompts in their specified order. - - Now let’s say we want to have a command that (i) asks for a template - and (ii) for a subdirectory ([The denote-templates option]). All we - need to do is tweak the `let' bound value of `denote-prompts' and give - our command a unique name: - - ┌──── - │ ;; Like `denote-subdirectory' but also ask for a template - │ (defun denote-subdirectory-with-template () - │ "Create note while also prompting for a template and subdirectory. - │ - │ This is equivalent to calling `denote' when `denote-prompts' is - │ set to '(template subdirectory title keywords)." - │ (declare (interactive-only t)) - │ (interactive) - │ (let ((denote-prompts '(template subdirectory title keywords))) - │ (call-interactively #'denote))) - └──── - - The tweaks to `denote-prompts' determine how the command will behave - ([The denote-prompts option]). Use this paradigm to write your own - variants which you can then assign to keys, invoke with `M-x', or add - to the list of commands available at the `denote-command-prompt' - ([Choose which commands to prompt for]). - - - [Convenience commands for note creation] See section 3.1.4 - - [The denote-templates option] See section 3.1.3 - - [The denote-prompts option] See section 3.1.1 - - [Choose which commands to prompt for] See section 8 - - -3.1.5 The `denote-save-buffer-after-creation' option -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - The user option `denote-save-buffer-after-creation' controls whether - commands that creeate new notes save their buffer outright. - - The default behaviour of commands such as `denote' (or related) is to - not save the buffer they create ([Points of entry]). This gives the - user the chance to review the text before writing it to a file. The - user may choose to delete the unsaved buffer, thus not creating a new - note ([The `denote-save-buffer-after-creation' option]). - - If `denote-save-buffer-after-creation' is set to a non-nil value, such - buffers are saved automatically. - - -[Points of entry] See section 3 - -[The `denote-save-buffer-after-creation' option] See section 3.1.5 - - -3.1.6 The `denote-date-prompt-use-org-read-date' option -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - By default, Denote uses its own simple prompt for date or date+time - input ([The denote-prompts option]). This is done when the - `denote-prompts' option includes a `date' symbol and/or when the user - invokes the `denote-date' command. - - Users who want to benefit from the more advanced date selection method - that is common in interactions with Org mode, can set the user option - `denote-date-prompt-use-org-read-date' to a non-nil value. - - -[The denote-prompts option] See section 3.1.1 - - -3.2 Create a note from the current Org subtree -────────────────────────────────────────────── - - In Org parlance, an entry with all its subheadings and other contents - is a “subtree”. Denote can operate on the subtree to extract it from - the current file and create a new file out of it. One such workflow is - to collect thoughts in a single document and produce longer standalone - notes out of them upon review. - - The command `denote-org-extras-extract-org-subtree' (part of the - optional `denote-org-extras.el' extension) is used for this purpose. - It creates a new Denote note using the current Org subtree. In doing - so, it removes the subtree from its current file and moves its - contents into a new file. - - The text of the subtree’s heading becomes the `#+title' of the new - note. Everything else is inserted as-is. - - If the heading has any tags, they are used as the keywords of the new - note. If the Org file has any `#+filetags' they are taken as well - (Org’s `#+filetags' are inherited by the headings). If none of these - are true and the user option `denote-prompts' includes an entry for - keywords, then `denote-org-extras-extract-org-subtree' prompts for - keywords. Else the new note has no keywords ([Add or remove keywords - interactively]). - - If the heading has a `PROPERTIES' drawer, it is retained for further - review. - - If the heading’s `PROPERTIES' drawer includes a `DATE' or `CREATED' - property, or there exists a `CLOSED' statement with a timestamp value, - use that to derive the date (or date and time) of the new note (if - there is only a date, the time is taken as 00:00). If more than one of - these is present, the order of preference is `DATE', then `CREATED', - then `CLOSED'. If none of these is present, the current time is used. - If the `denote-prompts' includes an entry for a date, then the command - prompts for a date at this stage (also see - `denote-date-prompt-use-org-read-date'). - - For the rest, it consults the value of the user option - `denote-prompts' in the following scenaria: - - • To optionally prompt for a subdirectory, otherwise it produces the - new note in the `denote-directory'. - • To optionally prompt for a file signature, otherwise to not use any. - - The new note is an Org file regardless of the user option - `denote-file-type'. - - -[Add or remove keywords interactively] See section 4.7 - - -3.3 Create note using Org capture -───────────────────────────────── - - For integration with `org-capture', the user must first add the - relevant template. Such as: - - ┌──── - │ (with-eval-after-load 'org-capture - │ (add-to-list 'org-capture-templates - │ '("n" "New note (with Denote)" plain - │ (file denote-last-path) - │ #'denote-org-capture - │ :no-save t - │ :immediate-finish nil - │ :kill-buffer t - │ :jump-to-captured t))) - └──── - - [ In the future, we might develop Denote in ways which do not require - such manual intervention. More user feedback is required to - identify the relevant workflows. ] - - Once the template is added, it is accessed from the specified key. - If, for instance, `org-capture' is bound to `C-c c', then the note - creation is initiated with `C-c c n', per the above snippet. After - that, the process is the same as with invoking `denote' directly, - namely: a prompt for a title followed by a prompt for keywords - ([Standard note creation]). - - Users may prefer to leverage `org-capture' in order to extend file - creation with the specifiers described in the `org-capture-templates' - documentation (such as to capture the active region and/or create a - hyperlink pointing to the given context). - - IMPORTANT. Due to the particular file-naming scheme of Denote, which - is derived dynamically, such specifiers or other arbitrary text cannot - be written directly in the template. Instead, they have to be - assigned to the user option `denote-org-capture-specifiers', which is - interpreted by the function `denote-org-capture'. Example with our - default value: - - ┌──── - │ (setq denote-org-capture-specifiers "%l\n%i\n%?") - └──── - - Note that `denote-org-capture' ignores the `denote-file-type': it - always sets the Org file extension for the created note to ensure that - the capture process works as intended, especially for the desired - output of the `denote-org-capture-specifiers'. - - -[Standard note creation] See section 3.1 - - -3.4 Create note with specific prompts using Org capture -─────────────────────────────────────────────────────── - - This section assumes knowledge of how Denote+org-capture work, as - explained in the previous section ([Create note using Org capture]). - - The previous section shows how to define an Org capture template that - always prompts for a title and keywords. There are, however, cases - where the user wants more control over what kind of input Denote will - prompt for. To this end, we provide the function - `denote-org-capture-with-prompts'. Below we explain it and then show - some examples of how to use it. - - The `denote-org-capture-with-prompts' is like `denote-org-capture' but - with optional prompt parameters. - - When called without arguments, it does not prompt for anything. It - just returns the front matter with title and keyword fields empty and - the date and identifier fields specified. It also makes the file name - consist of only the identifier plus the Org file name extension. - - [The file-naming scheme]. - - Otherwise, it produces a minibuffer prompt for every non-nil value - that corresponds to the `TITLE', `KEYWORDS', `SUBDIRECTORY', `DATE', - and `TEMPLATE' arguments. The prompts are those used by the standard - `denote' command and all of its utility commands. - - [Points of entry]. - - When returning the contents that fill in the Org capture template, the - sequence is as follows: front matter, `TEMPLATE', and then the value - of the user option `denote-org-capture-specifiers'. - - Important note: in the case of `SUBDIRECTORY' actual subdirectories - must exist—Denote does not create them. Same principle for `TEMPLATE' - as templates must exist and are specified in the user option - `denote-templates'. - - This is how one can incorporate `denote-org-capture-with-prompts' in - their Org capture templates. Instead of passing a generic `t' which - makes it hard to remember what the argument means, we use semantic - keywords like `:title' for our convenience (internally this does not - matter as the value still counts as non-nil, so `:foo' for `TITLE' is - treated the same as `:title' or `t'). - - ┌──── - │ ;; This prompts for TITLE, KEYWORDS, and SUBDIRECTORY - │ (add-to-list 'org-capture-templates - │ '("N" "New note with prompts (with denote.el)" plain - │ (file denote-last-path) - │ (function - │ (lambda () - │ (denote-org-capture-with-prompts :title :keywords :subdirectory))) - │ :no-save t - │ :immediate-finish nil - │ :kill-buffer t - │ :jump-to-captured t)) - │ - │ ;; This prompts only for SUBDIRECTORY - │ (add-to-list 'org-capture-templates - │ '("N" "New note with prompts (with denote.el)" plain - │ (file denote-last-path) - │ (function - │ (lambda () - │ (denote-org-capture-with-prompts nil nil :subdirectory))) - │ :no-save t - │ :immediate-finish nil - │ :kill-buffer t - │ :jump-to-captured t)) - │ - │ ;; This prompts for TITLE and SUBDIRECTORY - │ (add-to-list 'org-capture-templates - │ '("N" "New note with prompts (with denote.el)" plain - │ (file denote-last-path) - │ (function - │ (lambda () - │ (denote-org-capture-with-prompts :title nil :subdirectory))) - │ :no-save t - │ :immediate-finish nil - │ :kill-buffer t - │ :jump-to-captured t)) - └──── - - -[Create note using Org capture] See section 3.3 - -[The file-naming scheme] See section 5 - -[Points of entry] See section 3 - - -3.5 Create a note with the region’s contents -──────────────────────────────────────────── - - The command `denote-region' takes the contents of the active region - and then calls the `denote' command. Once a new note is created, it - inserts the contents of the region therein. This is useful to quickly - elaborate on some snippet of text or capture it for future reference. - - When the `denote-region' command is called with an active region, it - finalises its work by calling - `denote-region-after-new-note-functions'. This is an abnormal hook, - meaning that the functions added to it are called with arguments. The - arguments are two, representing the beginning and end positions of the - newly inserted text. - - A common use-case for Org mode users is to call the command - `org-insert-structure-template' after a region is inserted. Emacs - will thus prompt for a structure template, such as the one - corresponding to a source block. In this case the function added to - `denote-region-after-new-note-functions' does not actually need - aforementioned arguments: it can simply declare those as ignored by - prefixing the argument names with an underscore (an underscore is - enough, but it is better to include a name for clarity). For example, - the following will prompt for a structure template as soon as - `denote-region' is done: - - ┌──── - │ (defun my-denote-region-org-structure-template (_beg _end) - │ (when (derived-mode-p 'org-mode) - │ (activate-mark) - │ (call-interactively 'org-insert-structure-template))) - │ - │ (add-hook 'denote-region-after-new-note-functions #'my-denote-region-org-structure-template) - └──── - - Remember that `denote-region-after-new-note-functions' are not called - if `denote-region' is used without an active region. - - -3.6 Open an existing note or create it if missing -───────────────────────────────────────────────── - - Sometimes it is necessary to briefly interrupt the ongoing writing - session to open an existing note or, if that is missing, to create it. - This happens when a new tangential thought occurs and the user wants - to confirm that an entry for it is in place. To this end, Denote - provides the command `denote-open-or-create' as well as its more - flexible counterpart `denote-open-or-create-with-command'. - - The `denote-open-or-create' prompts to visit a file in the - `denote-directory'. At this point, the user must type in search terms - that match a file name. If the input does not return any matches and - the user confirms their choice to proceed (usually by typing RET - twice, depending on the minibuffer settings), `denote-open-or-create' - will call the `denote' command interactively to create a new note. It - will then use whatever prompts `denote' normally has, per the user - option `denote-prompts' ([Standard note creation]). If the title - prompt is involved (the default behaviour), the - `denote-open-or-create' sets up this prompt to have the previous input - as the default title of the note to-be-created. This means that the - user can type RET at the empty prompt to re-use what they typed in - previously. Commands to use previous inputs from the history are also - available (`M-p' or `M-n' in the minibuffer, which call - `previous-history-element' and `next-history-element' by default). - Accessing the history is helpful to, for example, make further edits - to the available text. - - The `denote-open-or-create-with-command' is like the above, except - when it is about to create the new note it first prompts for the - specific file-creating command to use ([Points of entry]). For - example, the user may want to specify a signature for this new file, - so they can select the `denote-signature' command. - - Denote provides similar functionality for linking to an existing note - or creating a new one ([Link to a note or create it if missing]). - - -[Standard note creation] See section 3.1 - -[Points of entry] See section 3 - -[Link to a note or create it if missing] See section 7.7 - - -3.7 Maintain separate directory silos for notes -─────────────────────────────────────────────── - - The user option `denote-directory' accepts a value that represents the - path to a directory, such as `~/Documents/notes'. Normally, the user - will have one place where they store all their notes, in which case - this arrangement shall suffice. - - There is, however, the possibility to maintain separate directories of - notes. By “separate”, we mean that they do not communicate with each - other: no linking between them, no common keywords, nothing. Think of - the scenario where one set of notes is for private use and another is - for an employer. We call these separate directories “silos”. - - To create silos, the user must specify a local variable at the root of - the desired directory. This is done by creating a `.dir-locals.el' - file, with the following contents: - - ┌──── - │ ;;; Directory Local Variables. For more information evaluate: - │ ;;; - │ ;;; (info "(emacs) Directory Variables") - │ - │ ((nil . ((denote-directory . "/path/to/silo/")))) - └──── - - When inside the directory that contains this `.dir-locals.el' file, - all Denote commands/functions for note creation, linking, the - inference of available keywords, et cetera will use the silo as their - point of reference. They will not read the global value of - `denote-directory'. The global value of `denote-directory' is read - everywhere else except the silos. - - [Use custom commands to select a silo]. - - In concrete terms, this is a representation of the directory - structures (notice the `.dir-locals.el' file is needed only for the - silos): - - ┌──── - │ ;; This is the global value of 'denote-directory' (no need for a .dir-locals.el) - │ ~/Documents/notes - │ |-- 20210303T120534--this-is-a-test__journal_philosophy.txt - │ |-- 20220303T120534--another-sample__journal_testing.md - │ `-- 20220620T181255--the-third-test__keyword.org - │ - │ ;; A silo with notes for the employer - │ ~/different/path/to/notes-for-employer - │ |-- .dir-locals.el - │ |-- 20210303T120534--this-is-a-test__conference.txt - │ |-- 20220303T120534--another-sample__meeting.md - │ `-- 20220620T181255--the-third-test__keyword.org - │ - │ ;; Another silo with notes for my volunteering - │ ~/different/path/to/notes-for-volunteering - │ |-- .dir-locals.el - │ |-- 20210303T120534--this-is-a-test__activism.txt - │ |-- 20220303T120534--another-sample__teambuilding.md - │ `-- 20220620T181255--the-third-test__keyword.org - └──── - - It is possible to configure other user options of Denote to have a - silo-specific value. For example, this one changes the - `denote-known-keywords' only for this particular silo: - - ┌──── - │ ;;; Directory Local Variables. For more information evaluate: - │ ;;; - │ ;;; (info "(emacs) Directory Variables") - │ - │ ((nil . ((denote-directory . "/path/to/silo/") - │ (denote-known-keywords . ("food" "drink"))))) - └──── - - This one is like the above, but also disables `denote-infer-keywords': - - ┌──── - │ ;;; Directory Local Variables. For more information evaluate: - │ ;;; - │ ;;; (info "(emacs) Directory Variables") - │ - │ ((nil . ((denote-directory . "/path/to/silo/") - │ (denote-known-keywords . ("food" "drink")) - │ (denote-infer-keywords . nil)))) - └──── - - To expand the list of local variables to, say, cover specific major - modes, we can do something like this: - - ┌──── - │ ;;; Directory Local Variables. For more information evaluate: - │ ;;; - │ ;;; (info "(emacs) Directory Variables") - │ - │ ((nil . ((denote-directory . "/path/to/silo/") - │ (denote-known-keywords . ("food" "drink")) - │ (denote-infer-keywords . nil))) - │ (org-mode . ((org-hide-emphasis-markers . t) - │ (org-hide-macro-markers . t) - │ (org-hide-leading-stars . t)))) - └──── - - As not all user options have a “safe” local value, Emacs will ask the - user to confirm their choice and to store it in the Custom code - snippet that is normally appended to init file (or added to the file - specified by the user option `custom-file'). - - Finally, it is possible to have a `.dir-locals.el' for subdirectories - of any `denote-directory'. Perhaps to specify a different set of - known keywords, while not making the subdirectory a silo in its own - right. We shall not expand on such an example, as we trust the user - to experiment with the best setup for their workflow. - - Feel welcome to ask for help if the information provided herein is not - sufficient. The manual shall be expanded accordingly. - - -[Use custom commands to select a silo] See section 3.7.1 - -3.7.1 Use custom commands to select a silo -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - [ As part of version 2.1.0, the contents of this section are formally - provided in the file `denote-silo-extras.el'. We keep this here for - existing users. Otherwise consult the new entry in the manual ([The - `denote-silo-extras.el']). ] - - We implement silos as directory-local values of the user option - `denote-directory'. This means that all Denote commands read from the - local value if they are invoked from that context. For example, if - `~/Videos/recordings' is a silo and `~/Documents/notes' is the - default/global value of `denote-directory' all Denote commands will - read the video’s path when called from there (e.g. by using Emacs’ - `dired'); any other context reads the global value. - - [Maintain separate directory silos for notes]. - - There are cases where the user (i) wants to maintain multiple silos - and (ii) prefers an interactive way to switch between them without - going through Dired. Since this is specific to the user’s workflow, - it is easier to have some custom code for it. The following should be - added to the user’s Denote configuration: - - ┌──── - │ (defvar my-denote-silo-directories - │ `("/home/prot/Videos/recordings" - │ "/home/prot/Documents/books" - │ ;; You don't actually need to include the `denote-directory' here - │ ;; if you use the regular commands in their global context. I am - │ ;; including it for completeness. - │ ,denote-directory) - │ "List of file paths pointing to my Denote silos. - │ This is a list of strings.") - │ - │ (defvar my-denote-commands-for-silos - │ '(denote - │ denote-date - │ denote-subdirectory - │ denote-template - │ denote-type) - │ "List of Denote commands to call after selecting a silo. - │ This is a list of symbols that specify the note-creating - │ interactive functions that Denote provides.") - │ - │ (defun my-denote-pick-silo-then-command (silo command) - │ "Select SILO and run Denote COMMAND in it. - │ SILO is a file path from `my-denote-silo-directories', while - │ COMMAND is one among `my-denote-commands-for-silos'." - │ (interactive - │ (list (completing-read "Select a silo: " my-denote-silo-directories nil t) - │ (intern (completing-read - │ "Run command in silo: " - │ my-denote-commands-for-silos nil t)))) - │ (let ((denote-directory silo)) - │ (call-interactively command))) - └──── - - With this in place, `M-x my-denote-pick-silo-then-command' will use - minibuffer completion to select a silo among the predefined options - and then ask for the command to run in that context. - - Note that `let' binding `denote-directory' can be used in custom - commands and other wrapper functions to override the global default - value of `denote-directory' to select silos. - - To see another example of a wrapper function that `let' binds - `denote-directory', see: - - [Extending Denote: Split an Org subtree into its own note]. - - -[The `denote-silo-extras.el'] See section 3.7.2 - -[Maintain separate directory silos for notes] See section 3.7 - -[Extending Denote: Split an Org subtree into its own note] See section -3.2 - - -3.7.2 The `denote-silo-extras.el' -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - The `denote-silo-extras.el' provides optional convenience functions - for working with silos ([Maintain separate directory silos for - notes]). Start by loading the relevant library: - - ┌──── - │ (require 'denote-silo-extras) - └──── - - The user option `denote-silo-extras-directories' specifies a list of - directories that the user has set up as `denote-directory' silos. - - The command `denote-silo-extras-create-note' prompts for a directory - among `denote-silo-extras-directories' and runs the `denote' command - from there. - - Similar to the above, the command `denote-silo-extras-open-or-create' - prompts for a directory among `denote-silo-extras-directories' and - runs the `denote-open-or-create' command from there. - - The command `denote-silo-extras-select-silo-then-command' prompts with - minibuffer completion for a directory among - `denote-silo-extras-directories'. Once the user selects a silo, a - second prompt asks for a Denote note-creation command to call from - inside that silo ([Points of entry]). - - -[Maintain separate directory silos for notes] See section 3.7 - -[Points of entry] See section 3 - - -3.8 Exclude certain directories from all operations -─────────────────────────────────────────────────── - - The user option `denote-excluded-directories-regexp' instructs all - Denote functions that read or check file/directory names to omit - directories that match the given regular expression. The regexp needs - to match only the name of the directory, not its full path. - - Affected operations include file prompts and functions that return the - available files in the value of the user option `denote-directory' - ([Maintain separate directory silos for notes]). - - File prompts are used by several commands, such as `denote-link' and - `denote-subdirectory'. - - Functions that check for files include `denote-directory-files' and - `denote-directory-subdirectories'. - - The match is performed with `string-match-p'. - - [For developers or advanced users]. - - -[Maintain separate directory silos for notes] See section 3.7 - -[For developers or advanced users] See section 18 - - -3.9 Exclude certain keywords from being inferred -──────────────────────────────────────────────── - - The user option `denote-excluded-keywords-regexp' omits keywords that - match a regular expression from the list of inferred keywords. - - Keywords are inferred from file names and provided at relevant prompts - as completion candidates when the user option `denote-infer-keywords' - is non-nil. - - The match is performed with `string-match-p'. - - -3.10 Use Denote commands from the menu bar or context menu -────────────────────────────────────────────────────────── - - Denote registers a submenu for the `menu-bar-mode'. Users will find - the entry called “Denote”. From there they can use their pointer to - select a command. For a sample of how this looks, read the - development log: - . - - The command `denote-menu-bar-mode' toggles the presentation of the - menu. It is enabled by default. - - Emacs also provides support for operations through a context menu. - This is typically the set of actions that are made available via a - right mouse click. Users who enable `context-menu-mode' can register - the Denote entry for it by adding the following to their configuration - file: - - ┌──── - │ (add-hook 'context-menu-functions #'denote-context-menu) - └──── - - -4 Renaming files -════════════════ - - Denote provides commands to rename files and update their front matter - where relevant. For Denote to work, only the file name needs to be in - order, by following our naming conventions ([The file-naming scheme]). - The linking mechanism, in particular, needs just the identifier in the - file name ([Linking notes]). - - We write front matter in notes for the user’s convenience and for - other tools to make use of that information (e.g. Org’s export - mechanism). The renaming mechanism takes care to keep this data in - sync with the file name, when the user performs a change. - - Renaming is useful for managing existing files created with Denote, - but also for converting older text files to Denote notes. Denote’s - file-naming scheme is not specific to notes or text files: it is - relevant for all sorts of items, such as multimedia and PDFs that form - part of the user’s longer-term storage. While Denote does not manage - such files (e.g. doesn’t create links to them), it already has all the - mechanisms to facilitate the task of renaming them. - - All renaming commands run the `denote-after-rename-file-hook' after a - succesful operation. - - Apart from renaming files, Denote can also rename only the buffer. - The idea is that the underlying file name is correct but it can be - easier to use shorter buffer names when displaying them on the mode - line or switching between then with commands like `switch-to-buffer'. - - [Automatically rename Denote buffers]. - - -[The file-naming scheme] See section 5 - -[Linking notes] See section 7 - -[Automatically rename Denote buffers] See section 10 - -4.1 Rename a single file -──────────────────────── - - The `denote-rename-file' command renames a file and updates existing - front matter if appropriate. It is possible to do the same with - multiple files ([Rename multiple files interactively]). - - It always renames the file where it is located in the file system: it - never moves it to another directory. - - If in Dired, it considers `FILE' to be the one at point, else it - prompts with minibuffer completion for one. When called from Lisp, - `FILE' is a file system path represented as a string. - - If `FILE' has a Denote-compliant identifier, it retains it while - updating components of the file name referenced by the user option - `denote-prompts' ([The `denote-prompts' option]). By default, these - are the `TITLE' and `KEYWORDS'. The `SIGNATURE' is another one. When - called from Lisp, `TITLE' and `SIGNATURE' are strings, while - `KEYWORDS' is a list of strings. - - If there is no identifier, `denote-rename-file' creates an identifier - based on the following conditions: - - 1. If the `denote-prompts' includes an entry for date prompts, then it - prompts for `DATE' and takes its input to produce a new - identifier. For use in Lisp, `DATE' must conform with - `denote-valid-date-p'. - - 2. If `DATE' is nil (e.g. when `denote-prompts' does not include a - date entry), it uses the file attributes to determine the last - modified date of `FILE' and formats it as an identifier. - - 3. As a fallback, it derives an identifier from the current date and - time. - - 4. At any rate, if the resulting identifier is not unique among the - files in the variable `denote-directory', it increments it such - that it becomes unique. - - In interactive use, and assuming `denote-prompts' includes a title - entry, the `denote-rename-file' makes the `TITLE' prompt have - prefilled text in the minibuffer that consists of the current title of - `FILE'. The current title is either retrieved from the front matter - (such as the `#+title' in Org) or from the file name. - - The command does the same for the `SIGNATURE' prompt, subject to - `denote-prompts', by prefilling the minibuffer with the current - signature of `FILE', if any. - - Same principle for the `KEYWORDS' prompt: it converts the keywords in - the file name into a comma-separated string and prefills the - minibuffer with it (the `KEYWORDS' prompt accepts more than one - keywords, each separated by a comma, else the `crm-separator'). - - For all prompts, the `denote-rename-file' interprets an empty input as - an instruction to remove that file name component. For example, if a - `TITLE' prompt is available and `FILE' is - `20240211T093531--some-title__keyword1.org' then it renames `FILE' to - `20240211T093531__keyword1.org'. If a file name component is present, - but there is no entry for it in `denote-prompts', keep it as-is. - - [ NOTE: Please check with your minibuffer user interface how to - provide an empty input. The Emacs default setup accepts the empty - minibuffer contents as they are, though popular packages like - `vertico' use the first available completion candidate instead. For - `vertico', the user must either move one up to select the prompt and - then type `RET' there with empty contents, or use the command - `vertico-exit-input' with empty contents. That Vertico command is - bound to `M-RET' as of this writing on 2024-02-13 08:08 +0200. ] - - When renaming `FILE', the command reads its file type extension (like - `.org') and preserves it through the renaming process. Files that have - no extension are left without one. - - As a final step, it asks for confirmation, showing the difference - between old and new file names. It does not ask for confirmation if - the user option `denote-rename-no-confirm' is set to a non-nil value - ([The `denote-rename-no-confirm' option]). - - If `FILE' has front matter for `TITLE' and `KEYWORDS', - `denote-rename-file' asks to rewrite their values in order to reflect - the new input, unless `denote-rename-no-confirm' is non-nil. When the - `denote-rename-no-confirm' is nil (the default), the underlying buffer - is not saved, giving the user the change to invoking - `diff-buffer-with-file' to double-check the effect. The rewrite of the - `TITLE' and `KEYWORDS' in the front matter should not affect the rest - of the front matter. - - If the file does not have front matter but is among the supported file - types (per `denote-file-type'), the `denote-rename-file' adds front - matter to the top of it and leaves the buffer unsaved for further - inspection. It actually saves the buffer if `denote-rename-no-confirm' - is non-nil ([Front matter]). - - This command is intended to (i) rename Denote files, (ii) convert - existing supported file types to Denote notes, and (ii) rename - non-note files (e.g. `PDF') that can benefit from Denote’s file-naming - scheme. - - For a version of this command that works with multiple files - one-by-one, use `denote-dired-rename-files' ([Rename multiple files - interactively]). - - -[Rename multiple files interactively] See section 4.3 - -[The `denote-prompts' option] See section 3.1.1 - -[The `denote-rename-no-confirm' option] See section 4.1.1 - -[Front matter] See section 6 - -4.1.1 The `denote-rename-no-confirm' option -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - The user option `denote-rename-no-confirm' makes all commands that - rename files not prompt for confirmation and save buffers outright - ([Renaming files]). - - This affects the behaviour of the commands `denote-rename-file', - `denote-dired-rename-files', `denote-rename-file-using-front-matter', - `denote-dired-rename-marked-files-with-keywords', - `denote-dired-rename-marked-files-using-front-matter', - `denote-keywords-add', `denote-keywords-remove', and any other command - that builds on top of them. - - The default behaviour of the `denote-rename-file' command (and others - like it) is to ask for an affirmative answer as a final step before - changing the file name and, where relevant, inserting or updating the - corresponding front matter. It also does not save the affected file’s - buffer to let the user inspect and confirm the changes (such as by - invoking the command `diff-buffer-with-file'). - - With this user option bound to a non-nil value, buffers are saved as - well. The assumption is that the user who opts in to this feature is - familiar with the `denote-rename-file' (or related) operation and - knows it is reliable. - - Specialised commands that build on top of `denote-rename-file' (or - related) may internally bind this user option to a non-nil value in - order to perform their operation (e.g. `denote-dired-rename-files' - goes through each marked Dired file, prompting for the information to - use, but carries out the renaming without asking for confirmation - ([Rename multiple files interactively])). - - -[Renaming files] See section 4 - -[Rename multiple files interactively] See section 4.3 - - -4.2 Rename a single file based on its front matter -────────────────────────────────────────────────── - - In the previous section, we covered the more general mechanism of the - command `denote-rename-file' ([Rename a single file]). There is also - a way to have the same outcome by making Denote read the data in the - current file’s front matter and use it to construct/update the file - name. The command for this is - `denote-rename-file-using-front-matter'. It is only relevant for - files that (i) are among the supported file types, per - `denote-file-type', and (ii) have the requisite front matter in place. - - Suppose you have an `.org' file with this front matter ([Front - matter]): - - ┌──── - │ #+title: My sample note file - │ #+date: [2022-08-05 Fri 13:10] - │ #+filetags: :testing: - │ #+identifier: 20220805T131044 - └──── - - Its file name reflects this information: - - ┌──── - │ 20220805T131044--my-sample-note-file__testing.org - └──── - - - You want to change its title and keywords manually, so you modify it - thus: - - ┌──── - │ #+title: My modified sample note file - │ #+date: [2022-08-05 Fri 13:10] - │ #+filetags: :testing:denote:emacs: - │ #+identifier: 20220805T131044 - └──── - - At this stage, the file name still shows the old title and keywords. - You now invoke `denote-rename-file-using-front-matter' and it updates - the file name to: - - ┌──── - │ 20220805T131044--my-modified-sample-note-file__testing_denote_emacs.org - └──── - - - The renaming is subject to a “yes or no” prompt that shows the old and - new names, just so the user is certain about the change. - - If called interactively with a prefix argument (`C-u' by default) or - from Lisp with a non-nil `NO-CONFIRM' argument, this “yes or no” - prompt is skipped and the renaming is done outright. - - If called interactively with a double prefix argument (`C-u C-u' by - default) or from Lisp with a non-nil `SAVE-BUFFER' argument, the - buffer is saved after the front matter is updated and the file is - renamed. - - If the user option `denote-rename-no-confirm' is non-nil, it is - interpreted the same way as a combination of `NO-CONFIRM' and - `SAVE-BUFFER' ([The `denote-rename-no-confirm' option]). - - The identifier of the file, if any, is never modified even if it is - edited in the front matter: Denote considers the file name to be the - source of truth in this case, to avoid potential breakage with typos - and the like. - - -[Rename a single file] See section 4.1 - -[Front matter] See section 6 - -[The `denote-rename-no-confirm' option] See section 4.1.1 - - -4.3 Rename multiple files interactively -─────────────────────────────────────── - - The command `denote-dired-rename-files' (alias - `denote-dired-rename-marked-files') renames the files that are marked - in a Dired buffer. Its behaviour is similar to the - `denote-rename-file' in that it prompts for a title, keywords, and - signature ([Rename a single file]). It does so over each marked file, - renaming one after the other. - - Unlike `denote-rename-file', the command `denote-dired-rename-files' - does not ask to confirm the changes made to the files: it performs - them outright. This is done to make it easier to rename multiple files - without having to confirm each step. For an even more direct approach, - check the command `denote-dired-rename-marked-files-with-keywords'. - - • [Rename by writing only keywords] - • [Rename multiple files based on their front matter] - - -[Rename a single file] See section 4.1 - -[Rename by writing only keywords] See section 4.4 - -[Rename multiple files based on their front matter] See section 4.5 - - -4.4 Rename multiple files at once by asking only for keywords -───────────────────────────────────────────────────────────── - - The `denote-dired-rename-marked-files-with-keywords' command renames - marked files in Dired to conform with our file-naming scheme. It does - so by writing keywords to them. Specifically, it does the following: - - • retains the file’s existing name and makes it the `TITLE' field, per - Denote’s file-naming scheme; - - • sluggifies the `TITLE' and adjusts its letter casing, according to - our conventions; - - • prepends an identifier to the `TITLE', if one is missing; - - • preserves the file’s extension, if any; - - • prompts once for `KEYWORDS' and applies the user’s input to the - corresponding field in the file name, rewriting any keywords that - may exist while removing keywords that do exist if `KEYWORDS' is - empty; - - • adds or rewrites existing front matter to the underlying file, if it - is recognized as a Denote note (per the `denote-file-type' user - option), such that it includes the new keywords. - - [ Note that the affected buffers are not saved, unless the user option - `denote-rename-no-confirm' is non-nil. Users can thus check them to - confirm that the new front matter does not cause any problems (e.g. - with the `diff-buffer-with-file' command). Multiple buffers can be - saved in one go with the command `save-some-buffers' (read its doc - string). ] - - -4.5 Rename multiple files based on their front matter -───────────────────────────────────────────────────── - - As already noted, Denote can rename a file based on the data in its - front matter ([Rename a single file based on its front matter]). The - command `denote-dired-rename-marked-files-using-front-matter' extends - this principle to a batch operation which applies to all marked files - in Dired. - - Marked files must count as notes for the purposes of Denote, which - means that they at least have an identifier in their file name and use - a supported file type, per `denote-file-type'. Files that do not meet - this criterion are ignored, because Denote cannot know if they have - front matter and what that may be. For such files, it is still - possible to rename them interactively ([Rename multiple files - interactively]). - - -[Rename a single file based on its front matter] See section 4.2 - -[Rename multiple files interactively] See section 4.3 - - -4.6 Rename a file by changing only its file type -──────────────────────────────────────────────── - - The command `denote-change-file-type-and-front-matter' provides the - convenience of converting a note taken in one file type, say, `.txt' - into another like `.org'. It presents a choice among the - `denote-file-type' options. - - The conversion does NOT modify the existing front matter. Instead, it - prepends new front matter to the top of the file. We do this as a - safety precaution since the user can, in principle, add arbitrary - extras to their front matter that we would not want to touch. - - If in Dired, `denote-change-file-type-and-front-matter' operates on - the file at point, else it prompts with minibuffer completion for one. - - The title of the file is retrieved from a line starting with a title - field in the file’s front matter, depending on the previous file type - (e.g. `#+title' for Org). The same process applies for keywords. - - As a final step, the command asks for confirmation, showing the - difference between old and new file names. - - -4.7 Rename a file by adding or removing keywords interactively -────────────────────────────────────────────────────────────── - - The commands `denote-keywords-add' and `denote-keywords-remove' - streamline the process of interactively updating a file’s keywords in - the front matter and renaming it accordingly. - - The `denote-keywords-add' asks for keywords using the familiar - minibuffer prompt ([Standard note creation]). It then renames the - file ([Rename a single file based on its front matter]). - - Similarly, the `denote-keywords-remove' removes one or more keywords - from the list of existing keywords and then renames the file - accordingly. - - Both commands accept an optional prefix argument to automatically save - the buffer. Similarly, they both interpret a non-nil value for the - user option `denote-rename-no-confirm' the same as the prefix argument - ([The `denote-rename-no-confirm' option]). - - Furthermore, both commands call the `denote-after-rename-file-hook' as - a final step after carrying out their task. - - Aliases for these commands are: `denote-rename-add-keywords' and - `denote-rename-remove-keywords'. - - -[Standard note creation] See section 3.1 - -[Rename a single file based on its front matter] See section 4.2 - -[The `denote-rename-no-confirm' option] See section 4.1.1 - - -4.8 Rename a file by adding or removing a signature interactively -───────────────────────────────────────────────────────────────── - - The commands `denote-rename-add-signature' and - `denote-rename-remove-signature' streamline the process of - interactively adding or removing a signature from a given file ([The - file-naming scheme]). - - The `denote-rename-add-signature' prompts for a file and a - signature. The default value for the file prompt is the file of the - currently open buffer or the file-at-point in a Dired buffer. The - signature is an ordinary string, defaulting to the selected file’s - signature, if any. - - The `denote-rename-remove-signature' uses the same file prompt as - above. It performs its action only if the selected file has a - signature. Otherwise, it does nothing. - - Both commands ask for confirmation before carrying out their action. - They do so unless the user option `denote-rename-no-confirm' is set to - a non-nil value ([The `denote-rename-no-confirm' option]). They also - both take care to reload any Dired buffers and run the - `denote-after-rename-file-hook' as a final step. - - -[The file-naming scheme] See section 5 - -[The `denote-rename-no-confirm' option] See section 4.1.1 - - -4.9 Faces used by rename commands -───────────────────────────────── - - These are the faces used by the various Denote rename commands to - style or highlight the old/new/current file shown in the relevant - minibuffer prompts: - - • `denote-faces-prompt-current-name' - • `denote-faces-prompt-new-name' - • `denote-faces-prompt-old-name' - - -5 The file-naming scheme -════════════════════════ - - Notes are stored in the `denote-directory'. The default path is - `~/Documents/notes'. The `denote-directory' can be a flat listing, - meaning that it has no subdirectories, or it can be a directory tree. - Either way, Denote takes care to only consider “notes” as valid - candidates in the relevant operations and will omit other files or - directories. - - Every note produced by Denote follows this pattern by default ([Points - of entry]): - - ┌──── - │ DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION - └──── - - - The `DATE' field represents the date in year-month-day format followed - by the capital letter `T' (for “time”) and the current time in - hour-minute-second notation. The presentation is compact: - `20220531T091625'. The `DATE' serves as the unique identifier of each - note and, as such, is also known as the file’s ID or identifier. - - File names can include a string of alphanumeric characters in the - `SIGNATURE' field. Signatures have no clearly defined purpose and are - up to the user to define. One use-case is to use them to establish - sequential relations between files (e.g. 1, 1a, 1b, 1b1, 1b2, …). - - Signatures are an optional extension to Denote’s file-naming scheme. - They can be added to newly created files on demand, with the command - `denote-signature', or by modifying the value of the user option - `denote-prompts'. - - The `TITLE' field is the title of the note, as provided by the user. - It automatically gets downcased by default and is also hyphenated - ([Sluggification of file name components]). An entry about “Economics - in the Euro Area” produces an `economics-in-the-euro-area' string for - the `TITLE' of the file name. - - The `KEYWORDS' field consists of one or more entries demarcated by an - underscore (the separator is inserted automatically). Each keyword is - a string provided by the user at the relevant prompt which broadly - describes the contents of the entry. - - Each of the keywords is a single word, with multiple keywords - providing the multi-dimensionality needed for advanced searches - through Denote files. Users who need to compose a keyword out of - multiple words such as camelCase/CamelCase and are encouraged to use - the `denote-file-name-slug-functions' user option accordingly - ([Sluggification of file name components]). - - The `EXTENSION' is the file type. By default, it is `.org' - (`org-mode') though the user option `denote-file-type' provides - support for Markdown with YAML or TOML variants (`.md' which runs - `markdown-mode') and plain text (`.txt' via `text-mode'). Consult its - doc string for the minutiae. While files end in the `.org' extension - by default, the Denote code base does not actually depend on org.el - and/or its accoutrements. - - Examples: - - ┌──── - │ 20220610T043241--initial-thoughts-on-the-zettelkasten-method__notetaking.org - │ 20220610T062201--define-custom-org-hyperlink-type__denote_emacs_package.md - │ 20220610T162327--on-hierarchy-and-taxis__notetaking_philosophy.txt - └──── - - - The different field separators, namely `--' and `__' introduce an - efficient way to anchor searches (such as with Emacs commands like - `isearch' or from the command-line with `find' and related). A query - for `_word' always matches a keyword, while a regexp in the form of, - say, `"\\([0-9T]+?\\)--\\(.*?\\)_"' captures the date in group `\1' - and the title in `\2' (test any regular expression in the current - buffer by invoking `M-x re-builder'). - - [Features of the file-naming scheme for searching or filtering]. - - The `denote-prompts' can be configured in such ways to yield the - following file name permutations: - - ┌──── - │ DATE.EXT - │ DATE--TITLE.EXT - │ DATE__KEYWORDS.EXT - │ DATE==SIGNATURE.EXT - │ DATE==SIGNATURE--TITLE.EXT - │ DATE==SIGNATURE--TITLE__KEYWORDS.EXT - │ DATE==SIGNATURE__KEYWORDS.EXT - └──── - - - When in doubt, stick to the default design. - - While Denote is an Emacs package, notes should work long-term and not - depend on the functionality of a specific program. The file-naming - scheme we apply guarantees that a listing is readable in a variety of - contexts. The Denote file-naming scheme is, in essence, an effective, - low-tech invention. - - -[Points of entry] See section 3 - -[Sluggification of file name components] See section 5.1 - -[Features of the file-naming scheme for searching or filtering] See -section 5.3 - -5.1 Sluggification of file name components -────────────────────────────────────────── - - Files names can contain any character that the file system - permits. Denote imposes a few additional restrictions: - - ⁃ The tokens “`=", =__' and `--' are interpreted by Denote and should - appear only once. - - ⁃ The dot character is not allowed in a note’s file name, except to - indicate the file type extension. Denote recognises two extensions - for encrypted files, like `.txt.gpg'. - - By default, Denote enforces other rules to file names through the user - option `denote-file-name-slug-functions'. These rules are applied to - file names by default: - - ⁃ What we count as “illegal characters” are removed. The constant - `denote-excluded-punctuation-regexp' holds the relevant value. - - ⁃ Input for a file title is hyphenated. The original value is - preserved in the note’s contents ([Front matter]). - - ⁃ Spaces or other delimiters are removed from keywords, meaning that - `hello-world' becomes `helloworld'. This is because hyphens in - keywords do not work everywhere, such as in Org. Plus, hyphens are - word separators in the title and we want to keep distinct separators - for each component to make search easier and semantic ([Features of - the file-naming scheme for searching or filtering]). - - ⁃ Signatures are like the above, but use the equals sign instead of - hyphens as a word separator. - - ⁃ All file name components are downcased. Further down we document how - to deviate from these rules, such as to accept input of the form - `helloWorld' or `HelloWorld' verbatim. - - Denote imposes these restrictions to enforce uniformity, which is - helpful long-term as it keeps all files with the same predictable - pattern. Too many permutations make searches more difficult to express - accurately and be confident that the matches cover all files. - Nevertheless, one of the principles of Denote is its flexibility or - hackability and so users can deviate from the aforementioned - ([User-defined sluggification of file name components]). - - -[Front matter] See section 6 - -[Features of the file-naming scheme for searching or filtering] See -section 5.3 - -[User-defined sluggification of file name components] See section 5.2 - - -5.2 User-defined sluggification of file name components -─────────────────────────────────────────────────────── - - The user option `denote-file-name-slug-functions' controls the - sluggification of file name components ([Sluggification of file name - components]). The default method is outlined above and in the - previous section ([The file-naming scheme]). - - The value of this user option is an alist where each element is a cons - cell of the form `(COMPONENT . METHOD)'. For example, here is the - default value: - - ┌──── - │ '((title . denote-sluggify-title) - │ (signature . denote-sluggify-signature) - │ (keyword . denote-sluggify-keyword)) - └──── - - • The `COMPONENT' is an unquoted symbol among `title', `signature', - `keyword', which refers to the corresponding component of the file - name. - - • The `METHOD' is a function to format the given component. This - function must take a string as its parameter and return the string - formatted for the file name. Note that even in the case of the - `keyword' component, the function receives one string representing a - single keyword and returns it formatted for the file name. Joining - the keywords together is handled internally by Denote. - - One commonly requested deviation from the sluggification rules is to - not sluggify individual keywords, such that the user’s input is taken - as-is. This can be done as follows: - - ┌──── - │ (setq denote-file-name-slug-functions - │ '((title . denote-sluggify-title) - │ (keyword . identity) - │ (signature . denote-sluggify-signature))) - └──── - - The `identity' function simply returns the string it receives, thus - not altering it in any way. - - Another approach is to keep the sluggification but not downcase the - string. We can do this by modifying the original functions used by - Denote. For example, we have this: - - ┌──── - │ ;; The original function for reference - │ (defun denote-sluggify-title (str) - │ "Make STR an appropriate slug for title." - │ (downcase (denote--slug-hyphenate (denote--slug-no-punct str)))) - │ - │ ;; Our variant of the above, which does the same thing except from - │ ;; downcasing the string. - │ (defun my-denote-sluggify-title (str) - │ "Make STR an appropriate slug for title." - │ (denote--slug-hyphenate (denote--slug-no-punct str))) - │ - │ ;; Now we use our function to sluggify titles without affecting their - │ ;; letter casing. - │ (setq denote-file-name-slug-functions - │ '((title . my-denote-sluggify-title) ; our function here - │ (signature . denote-sluggify-signature) - │ (keyword . denote-sluggify-keyword))) - └──── - - Follow this principle for all the sluggification functions. - - To access the source code, use either of the following built-in - methods: - - 1. Call the command `find-library' and search for `denote'. Then - navigate to the symbol you are searching for. - - 2. Invoke the command `describe-symbol', search for the symbol you are - interested in, and from the resulting Help buffer either click on - the first link or do `M-x help-view-source' (bound to `s' in Help - buffers, by default). - - Remember that deviating from the default file-naming scheme of Denote - will make things harder to use in the future, as files can/will have - permutations that create uncertainty. The sluggification scheme and - concomitant restrictions we impose by default are there for a very - good reason: they are the distillation of years of experience. Here we - give you what you wish, but bear in mind it may not be what you need. - You have been warned. - - -[Sluggification of file name components] See section 5.1 - -[The file-naming scheme] See section 5 - - -5.3 Features of the file-naming scheme for searching or filtering -───────────────────────────────────────────────────────────────── - - By default, file names have three fields and two sets of field - delimiters between them: - - ┌──── - │ DATE--TITLE__KEYWORDS.EXTENSION - └──── - - - When a signature is present, this becomes: - - ┌──── - │ DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION - └──── - - - Field delimiters practically serve as anchors for easier searching. - Consider this example: - - ┌──── - │ 20220621T062327==1a2--introduction-to-denote__denote_emacs.txt - └──── - - - You will notice that there are two matches for the word `denote': one - in the title field and another in the keywords’ field. Because of the - distinct field delimiters, if we search for `-denote' we only match - the first instance while `_denote' targets the second one. When - sorting through your notes, this kind of specificity is invaluable—and - you get it for free from the file names alone! Similarly, a search - for `=1' will show all notes that are related to each other by virtue - of their signature. - - Users can get a lot of value out of this simple yet effective - arrangement, even if they have no knowledge of regular expressions. - One thing to consider, for maximum effect, is to avoid using - multi-word keywords as those can get hyphenated like the title and - will thus interfere with the above: either set the user option - `denote-allow-multi-word-keywords' to nil or simply insert single - words at the relevant prompts. - - -6 Front matter -══════════════ - - Notes have their own “front matter”. This is a block of data at the - top of the file, with no empty lines between the entries, which is - automatically generated at the creation of a new note. The front - matter includes the title and keywords (aka “tags” or “filetags”, - depending on the file type) which the user specified at the relevant - prompt, as well as the date and unique identifier, which are derived - automatically. - - This is how it looks for Org mode (when `denote-file-type' is nil or - the `org' symbol): - - ┌──── - │ #+title: This is a sample note - │ #+date: [2022-06-30 Thu 16:09] - │ #+filetags: :denote:testing: - │ #+identifier: 20220630T160934 - └──── - - For Markdown with YAML (`denote-file-type' has the `markdown-yaml' - value), the front matter looks like this: - - ┌──── - │ --- - │ title: "This is a sample note" - │ date: 2022-06-30T16:09:58+03:00 - │ tags: ["denote", "testing"] - │ identifier: "20220630T160958" - │ --- - └──── - - For Markdown with TOML (`denote-file-type' has the `markdown-toml' - value), it is: - - ┌──── - │ +++ - │ title = "This is a sample note" - │ date = 2022-06-30T16:10:13+03:00 - │ tags = ["denote", "testing"] - │ identifier = "20220630T161013" - │ +++ - └──── - - And for plain text (`denote-file-type' has the `text' value), we have - the following: - - ┌──── - │ title: This is a sample note - │ date: 2022-06-30 - │ tags: denote testing - │ identifier: 20220630T161028 - │ --------------------------- - └──── - - The format of the date in the front matter is controlled by the user - option `denote-date-format'. When nil, Denote uses a - file-type-specific format: - - • For Org, an inactive timestamp is used, such as `[2022-06-30 Wed - 15:31]'. - - • For Markdown, the RFC3339 standard is applied: - `2022-06-30T15:48:00+03:00'. - - • For plain text, the format is that of ISO 8601: `2022-06-30'. - - If the value is a string, ignore the above and use it instead. The - string must include format specifiers for the date. These are - described in the doc string of `format-time-string'.. - - -6.1 Change the front matter format -────────────────────────────────── - - Per Denote’s design principles, the code is hackable. All front - matter is stored in variables that are intended for public use. We do - not declare those as “user options” because (i) they expect the user - to have some degree of knowledge in Emacs Lisp and (ii) implement - custom code. - - [ NOTE for tinkerers: code intended for internal use includes double - hyphens in its symbol. “Internal use” means that it can be changed - without warning and with no further reference in the change log. Do - not use any of it without understanding the consequences. ] - - The variables which hold the front matter format are: - - • `denote-org-front-matter' - - • `denote-text-front-matter' - - • `denote-toml-front-matter' - - • `denote-yaml-front-matter' - - These variables have a string value with specifiers that are used by - the `format' function. The formatting operation passes four arguments - which include the values of the given entries. If you are an advanced - user who wants to edit this variable to affect how front matter is - produced, consider using something like `%2$s' to control where the - Nth argument is placed. - - When editing the value, make sure to: - - 1. Not use empty lines inside the front matter block. - - 2. Insert at least one empty line after the front matter block and do - not use any empty line before it. - - These help with consistency and might prove useful if we ever need to - operate on the front matter as a whole. - - With those granted, below are some examples. The approach is the same - for all variables. - - ┌──── - │ ;; Like the default, but upcase the entries - │ (setq denote-org-front-matter - │ "#+TITLE: %s - │ #+DATE: %s - │ #+FILETAGS: %s - │ #+IDENTIFIER: %s - │ \n") - │ - │ ;; Change the order (notice the %N$s notation) - │ (setq denote-org-front-matter - │ "#+title: %1$s - │ #+filetags: %3$s - │ #+date: %2$s - │ #+identifier: %4$s - │ \n") - │ - │ ;; Remove the date - │ (setq denote-org-front-matter - │ "#+title: %1$s - │ #+filetags: %3$s - │ #+identifier: %4$s - │ \n") - │ - │ ;; Remove the date and the identifier - │ (setq denote-org-front-matter - │ "#+title: %1$s - │ #+filetags: %3$s - │ \n") - └──── - - Note that `setq' has a global effect: it affects the creation of all - new notes. Depending on the workflow, it may be preferrable to have a - custom command which `let' binds the different format. We shall not - provide examples at this point as this is a more advanced feature and - we are not yet sure what the user’s needs are. Please provide - feedback and we shall act accordingly. - - -6.2 Regenerate front matter -─────────────────────────── - - Sometimes the user needs to produce new front matter for an existing - note. Perhaps because they accidentally deleted a line and could not - undo the operation. The command `denote-add-front-matter' can be used - for this very purpose. - - In interactive use, `denote-add-front-matter' must be invoked from a - buffer that visits a Denote note. It prompts for a title and then for - keywords. These are the standard prompts we already use for note - creation, so the keywords’ prompt allows minibuffer completion and the - input of multiple entries, each separated by a comma ([Points of - entry]). - - The newly created front matter is added to the top of the file. - - This command does not rename the file (e.g. to update the keywords). - To rename a file by reading its front matter as input, the user can - rely on `denote-rename-file-using-front-matter' ([Renaming files]). - - Note that `denote-add-front-matter' is useful only for existing Denote - notes. If the user needs to convert a generic text file to a Denote - note, they can use one of the command which first rename the file to - make it comply with our file-naming scheme and then add the relevant - front matter. - - -[Points of entry] See section 3 - -[Renaming files] See section 4 - - -7 Linking notes -═══════════════ - - Denote offers several commands for linking between notes. - - All links target files which are Denote files. This means that they - have our file-naming scheme. Files need to be inside the - `denote-directory' or one of its subdirectories. No other file is - recognised. - - The following sections delve into the details. - - -7.1 Adding a single link -──────────────────────── - - The `denote-link' command inserts a link at point to a file specified - at the minibuffer prompt ([The `denote-org-store-link-to-heading' user - option]). Links are formatted depending on the file type of the - current note. In Org and plain text buffers, links are formatted thus: - `[[denote:IDENTIFIER][DESCRIPTION]]'. While in Markdown they are - expressed as `[DESCRIPTION](denote:IDENTIFIER)'. - - When `denote-link' is called with a prefix argument (`C-u' by - default), it formats links like `[[denote:IDENTIFIER]]'. The user - might prefer its simplicity. - - By default, the description of the link is taken from the signature of - the file, if present, and the target file’s front matter’s title or, - if that is not available, from the file name. If the region is - active, its text is used as the link’s description instead. If the - active region has no text, the inserted link uses just the identifier, - as with the `C-u' prefix mentioned above. - - Inserted links are automatically buttonized and remain active for as - long as the buffer is available. In Org this is handled by the major - mode: the `denote:' hyperlink type works exactly like the standard - `file:'. In Markdown and plain text, Denote performs the - buttonization of those links. To buttonize links in existing files - while visiting them, the user must add this snippet to their setup (it - already excludes Org): - - ┌──── - │ (add-hook 'find-file-hook #'denote-link-buttonize-buffer) - └──── - - The `denote-link-buttonize-buffer' is also an interactive function in - case the user needs it. - - Links are created only for files which qualify as a “note” for our - purposes ([Linking notes]). - - Links are styled with the `denote-faces-link' face, which looks - exactly like an ordinary link by default. This is just a convenience - for the user/theme in case they want `denote:' links to remain - distinct from other links. - - In files whose major mode is `markdown-mode', the default key binding - `C-c C-o' (which calls the command `markdown-follow-thing-at-point') - correctly resolves `denote:' links. This method works in addition to - the `RET' key, which is made available by the aforementioned - buttonization. Interested users can refer to the function - `denote-link-markdown-follow' for the implementation details. - - -[The `denote-org-store-link-to-heading' user option] See section 7.2 - -[Linking notes] See section 7 - - -7.2 The `denote-org-store-link-to-heading' user option -────────────────────────────────────────────────────── - - The user option `denote-org-store-link-to-heading' determines whether - `org-store-link' links to the current Org heading (such links are - merely “stored” and need to be inserted afterwards with the command - `org-insert-link'). Note that the `org-capture' command uses the - `org-link' internally if it has to store a link. - - When its value is non-nil, `org-store-link' stores a link to the - current Org heading inside the Denote Org file. If the heading does - not have a `CUSTOM_ID', it creates it and includes it in the heading’s - `PROPERTIES' drawer. If a `CUSTOM_ID' exists, `org-store-link' use it - as-is. - - This makes the resulting link a combination of the `denote:' link - type, pointing to the identifier of the current file, plus the value - of the heading’s `CUSTOM_ID', such as: - - • `[[denote:20240118T060608][Some test]]' - • `[[denote:20240118T060608::#h:eed0fb8e-4cc7-478f-acb6-f0aa1a8bffcd][Some - test::Heading text]]' - - Both lead to the same Denote file, but the latter jumps to the heading - with the given `CUSTOM_ID'. Notice that the link to the heading also - has a different description, which includes the heading text. - - The value of the `CUSTOM_ID' is determined by the Org user option - `org-id-method'. The sample shown above uses the default UUID - infrastructure. - - If `denote-org-store-link-to-heading' is set to a nil value, the - command `org-store-link' only stores links to the Denote file (using - its identifier), but not to the given heading. This is what Denote was - doing in versions prior to `2.3.0'. - - Note that the optional extension `denote-org-extras.el' defines the - command `denote-org-extras-link-to-heading', which always links to a - file+heading regardless of the aforementioned user option ([Insert - link to an Org file with a further pointer to a heading]). - - [ This feature only works in Org mode files, as other file types do - not have a linking mechanism that handles unique identifiers for - headings or other patterns to jump to. If `org-store-link' is - invoked in one such file, it captures only the Denote identifier of - the file, even if this user option is set to a non-nil value. ] - - -[Insert link to an Org file with a further pointer to a heading] See -section 7.3 - - -7.3 Insert link to an Org file with a further pointer to a heading -────────────────────────────────────────────────────────────────── - - As part of the optional `denote-org-extras.el' extension, the command - `denote-org-extras-link-to-heading' prompts for a link to an Org file - and then asks for a heading therein, using minibuffer completion. Once - the user provides input at the two prompts, the command inserts a link - at point which has the following pattern: - `[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading - text]]'. - - Because only Org files can have links to individual headings, the - command `denote-org-extras-link-to-heading' prompts only for Org files - (i.e. files which include the `.org' extension). Remember that Denote - works with many file types ([The file-naming scheme]). - - This feature is similar to the concept of the user option - `denote-org-store-link-to-heading' ([The - `denote-org-store-link-to-heading' user option]). It is, however, - interactive and differs in the directionality of the action. With that - user option, the command `org-store-link' will generate a `CUSTOM_ID' - for the current heading (or capture the value of one as-is), giving - the user the option to then call `org-insert-link' wherever they see - fit. By contrast, the command `denote-org-extras-link-to-heading' - prompts for a file, then a heading, and inserts the link at point. - - -[The file-naming scheme] See section 5 - -[The `denote-org-store-link-to-heading' user option] See section 7.2 - - -7.4 Insert links matching a regexp -────────────────────────────────── - - The command `denote-add-links' adds links at point matching a regular - expression or plain string. The links are inserted as a typographic - list, such as: - - ┌──── - │ - link1 - │ - link2 - │ - link3 - └──── - - Each link is formatted according to the file type of the current note, - as explained further above about the `denote-link' command. The - current note is excluded from the matching entries (adding a link to - itself is pointless). - - When called with a prefix argument (`C-u') `denote-add-links' will - format all links as `[[denote:IDENTIFIER]]', hence a typographic list: - - ┌──── - │ - [[denote:IDENTIFIER-1]] - │ - [[denote:IDENTIFIER-2]] - │ - [[denote:IDENTIFIER-3]] - └──── - - Same examples of a regular expression that can be used with this - command: - - • `journal' match all files which include `journal' anywhere in their - name. - - • `_journal' match all files which include `journal' as a keyword. - - • `^2022.*_journal' match all file names starting with `2022' and - including the keyword `journal'. - - • `\.txt' match all files including `.txt'. In practical terms, this - only applies to the file extension, as Denote automatically removes - dots (and other characters) from the base file name. - - If files are created with `denote-sort-keywords' as non-nil (the - default), then it is easy to write a regexp that includes multiple - keywords in alphabetic order: - - • `_denote.*_package' match all files that include both the `denote' - and `package' keywords, in this order. - - • `\(.*denote.*package.*\)\|\(.*package.*denote.*\)' is the same as - above, but out-of-order. - - Remember that regexp constructs only need to be escaped once (like - `\|') when done interactively but twice when called from Lisp. What - we show above is for interactive usage. - - Links are created only for files which qualify as a “note” for our - purposes ([Linking notes]). - - -[Linking notes] See section 7 - - -7.5 Insert link to file with signature -────────────────────────────────────── - - The command `denote-link-with-signature' prompts for a file among - those that contain a `==SIGNATURE' and inserts a link to it. The - description of the link includes the text of the signature and that of - the file’s title, if any. For example, a link to the following file: - - ┌──── - │ 20230925T144303==abc--my-first-signature-note__denote_testing.txt - └──── - - - will get this link: `[[denote:20230925T144303][abc My first signature - note]]'. - - For more advanced uses, refer to the doc string of the `denote-link' - function. - - -7.6 Insert links from marked files in Dired -─────────────────────────────────────────── - - The command `denote-link-dired-marked-notes' is similar to - `denote-add-links' in that it inserts in the buffer a typographic list - of links to Denote notes ([Insert links matching a regexp]). Though - instead of reading a regular expression, it lets the user mark files - in Dired and link to them. This should be easier for users of all - skill levels, instead of having to write a potentially complex regular - expression. - - If there are multiple buffers that visit a Denote note, this command - will ask to select one among them, using minibuffer completion. If - there is only one buffer, it will operate in it outright. If there - are no buffers, it will produce an error. - - With optional `ID-ONLY' as a prefix argument (`C-u' by default), the - command inserts links with just the identifier, which is the same - principle as with `denote-link' and others ([Adding a single link]). - - The command `denote-link-dired-marked-notes' is meant to be used from - a Dired buffer. - - As always, links are created only for files which qualify as a “note” - for our purposes ([Linking notes]). - - -[Insert links matching a regexp] See section 7.4 - -[Adding a single link] See section 7.1 - -[Linking notes] See section 7 - - -7.7 Link to an existing note or create a new one -──────────────────────────────────────────────── - - In one’s note-taking workflow, there may come a point where they are - expounding on a certain topic but have an idea about another subject - they would like to link to ([Linking notes]). The user can always - rely on the other linking facilities we have covered herein to target - files that already exist. Though they may not know whether they - already have notes covering the subject or whether they would need to - write new ones. To this end, Denote provides two convenience - commands: - - `denote-link-after-creating' - Create new note in the background and link to it directly. - - Use `denote' interactively to produce the new note. Its doc - string or this manual explains which prompts will be used and - under what conditions ([Standard note creation]). - - With optional `ID-ONLY' as a prefix argument (this is the `C-u' - key, by default) create a link that consists of just the - identifier. Else try to also include the file’s title. This - has the same meaning as in `denote-link' ([Adding a single - link]). - - IMPORTANT NOTE: Normally, `denote' does not save the buffer it - produces for the new note ([The - `denote-save-buffer-after-creation' option]). This is a safety - precaution to not write to disk unless the user wants it - (e.g. the user may choose to kill the buffer, thus cancelling - the creation of the note). However, for this command the - creation of the note happens in the background and the user may - miss the step of saving their buffer. We thus have to save the - buffer in order to (i) establish valid links, and (ii) retrieve - whatever front matter from the target file. - - `denote-link-after-creating-with-command' - This command is like `denote-link-after-creating' except it - prompts for a note-creating command ([Points of entry]). Use - this to, for example, call `denote-signature' so that the newly - created note has a signature as part of its file name. Optional - `ID-ONLY' has the same meaning as in the command - `denote-link-after-creating'. - - `denote-link-or-create' - Use `denote-link' on `TARGET' file, creating it if necessary. - - If `TARGET' file does not exist, call - `denote-link-after-creating' which runs the `denote' command - interactively to create the file. The established link will - then be targeting that new file. - - If `TARGET' file does not exist, add the user input that was - used to search for it to the history of the - `denote-file-prompt'. The user can then retrieve and possibly - further edit their last input, using it as the newly created - note’s actual title. At the `denote-file-prompt' type `M-p' - with the default key bindings, which calls - `previous-history-element'. - - With optional `ID-ONLY' as a prefix argument create a link with - just the file’s identifier. This has the same meaning as in - `denote-link'. - - This command has the alias - `denote-link-to-existing-or-new-note', which helps with - discoverability. - - `denote-link-or-create-with-command' - This is like the above, except when it is about to create the - new note it first prompts for the specific file-creating command - to use ([Points of entry]). For example, the user may want to - specify a signature for this new file, so they can select the - `denote-signature' command. - - In all of the above, an optional prefix argument (`C-u' by default) - creates a link that consists of just the identifier. This has the - same meaning as in the regular `denote-link' command. - - Denote provides similar functionality for opening an existing note or - creating a new one ([Open an existing note or create it if missing]). - - -[Linking notes] See section 7 - -[Standard note creation] See section 3.1 - -[Adding a single link] See section 7.1 - -[The `denote-save-buffer-after-creation' option] See section 3.1.5 - -[Points of entry] See section 3 - -[Points of entry] See section 3 - -[Open an existing note or create it if missing] See section 3.6 - - -7.8 The backlinks’ buffer -───────────────────────── - - The command `denote-backlinks' produces a bespoke buffer which - displays backlinks to the current note. A “backlink” is a link back - to the present entry. - - By default, the backlinks’ buffer is designed to display the file name - of the note linking to the current entry. Each file name is presented - on its own line, like this: - - ┌──── - │ Backlinks to "On being honest" (20220614T130812) - │ ------------------------------------------------ - │ - │ 20220614T145606--let-this-glance-become-a-stare__journal.txt - │ 20220616T182958--feeling-butterflies-in-your-stomach__journal.txt - └──── - - When the user option `denote-backlinks-show-context' is non-nil, the - backlinks’ buffer displays the line on which a link to the current - note occurs. It also shows multiple occurrences, if present. It - looks like this (and has the appropriate fontification): - - ┌──── - │ Backlinks to "On being honest" (20220614T130812) - │ ------------------------------------------------ - │ - │ 20220614T145606--let-this-glance-become-a-stare__journal.txt - │ 37: growing into it: [[denote:20220614T130812][On being honest]]. - │ 64: As I said in [[denote:20220614T130812][On being honest]] I have never - │ 20220616T182958--feeling-butterflies-in-your-stomach__journal.txt - │ 62: indifference. In [[denote:20220614T130812][On being honest]] I alluded - └──── - - Note that the width of the lines in the context depends on the - underlying file. In the above example, the lines are split at the - `fill-column'. Long lines will show up just fine. Also note that the - built-in user option `xref-truncation-width' can truncate long lines - to a given maximum number of characters. - - [Speed up backlinks’ buffer creation?] - - The backlinks’ buffer runs the major-mode `denote-backlinks-mode'. It - binds keys to move between links with `n' (next) and `p' (previous). - These are stored in the `denote-backlinks-mode-map' (use `M-x - describe-mode' (`C-h m') in an unfamiliar buffer to learn more about - it). When the user option `denote-backlinks-show-context' is non-nil, - all relevant Xref key bindings are fully functional: again, check - `describe-mode'. - - The backlinking facility uses Emacs’ built-in Xref infrastructure. On - some operating systems, the user may need to add certain executables - to the relevant environment variable. - - [Why do I get “Search failed with status 1” when I search for - backlinks?] - - Backlinks to the current file can also be visited by using the - minibuffer completion interface with the `denote-find-backlink' - command ([Visiting linked files via the minibuffer]). - - The placement of the backlinks’ buffer is subject to the user option - `denote-link-backlinks-display-buffer-action'. Due to the nature of - the underlying `display-buffer' mechanism, this inevitably is a - relatively advanced feature. By default, the backlinks’ buffer is - displayed below the current window. The doc string of our user option - includes a sample configuration that places the buffer in a left side - window instead. Reproducing it here for the sake of convenience: - - ┌──── - │ (setq denote-link-backlinks-display-buffer-action - │ '((display-buffer-reuse-window - │ display-buffer-in-side-window) - │ (side . left) - │ (slot . 99) - │ (window-width . 0.3))) - └──── - - -[Speed up backlinks’ buffer creation?] See section 23.9 - -[Why do I get “Search failed with status 1” when I search for -backlinks?] See section 23.10 - -[Visiting linked files via the minibuffer] See section 7.10 - - -7.9 Writing metanotes -───────────────────── - - A “metanote” is an entry that describes other entries who have - something in common. Writing metanotes can be part of a workflow - where the user periodically reviews their work in search of patterns - and deeper insights. For example, you might want to read your journal - entries from the past year to reflect on your experiences, evolution - as a person, and the like. - - The commands `denote-add-links', `denote-link-dired-marked-notes' are - suited for this task. - - [Insert links matching a regexp]. - - [Insert links from marked files in Dired]. - - You will create your metanote the way you use Denote ordinarily - (metanotes may have the `metanote' keyword, among others), write an - introduction or however you want to go about it, invoke the command - which inserts multiple links at once (see the above-cited nodes), and - continue writing. - - Metanotes can serve as entry points to groupings of individual notes. - They are not the same as a filtered list of files, i.e. what you would - do in Dired or the minibuffer where you narrow the list of notes to a - given query. Metanotes contain the filtered list plus your thoughts - about it. The act of purposefully grouping notes together and - contemplating on their shared patterns is what adds value. - - Your future self will appreciate metanotes for the function they serve - in encapsulating knowledge, while current you will be equipped with - the knowledge derived from the deliberate self-reflection. - - -[Insert links matching a regexp] See section 7.4 - -[Insert links from marked files in Dired] See section 7.6 - - -7.10 Visiting linked files via the minibuffer -───────────────────────────────────────────── - - Denote has a major-mode-agnostic mechanism to collect all linked file - references in the current buffer and return them as an appropriately - formatted list. This list can then be used in interactive commands. - The `denote-find-link' is such a command. It uses minibuffer - completion to visit a file that is linked to from the current note. - The candidates have the correct metadata, which is ideal for - integration with other standards-compliant tools ([Extending Denote]). - For instance, a package such as `marginalia' will display accurate - annotations, while the `embark' package will be able to work its magic - such as in exporting the list into a filtered Dired buffer (i.e. a - familiar Dired listing with only the files of the current minibuffer - session). - - To visit backlinks to the current note via the minibuffer, use - `denote-find-backlink'. This is an alternative to placing backlinks - in a dedicated buffer ([The backlinks’ buffer]). - - -[Extending Denote] See section 15 - -[The backlinks’ buffer] See section 7.8 - - -7.11 Convert `denote:' links to `file:' links -───────────────────────────────────────────── - - Sometimes the user needs to translate all `denote:' link types to - their `file:' equivalent. This may be because some other tool does not - recognise `denote:' links (or other custom links types—which are a - standard feature of Org, by the way). The user thus needs to (i) - either make a copy of their Denote note or edit the existing one, and - (ii) convert all links to the generic `file:' link type that - external/other programs understand. - - The optional extension `denote-org-extras.el' contains two commands - that are relevant for this use-case: - - Convert `denote:' links to `file:' links - The command `denote-org-extras-convert-links-to-file-type' goes - through the buffer to find all `denote:' links. It gets the - identifier of the link and resolves it to the actual file system - path. It then replaces the match so that the link is written - with the `file:' type and then the file system path. The - optional search terms and/or link description are preserved - ([Insert link to an Org file with a further pointer to a - heading]). - - Convert `file:' links to `denote:' links - The command `denote-org-extras-convert-links-to-denote-type' - behaves like the one above. The difference is that it finds the - file system path and converts it into its identifier. - - -[Insert link to an Org file with a further pointer to a heading] See -section 7.3 - - -7.12 Miscellaneous information about links -────────────────────────────────────────── - -7.12.1 Aliases for the linking commands -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - For convenience, the `denote-link' command has an alias called - `denote-insert-link'. The `denote-backlinks' can also be used as - `denote-show-backlinks-buffer'. While `denote-add-links' is aliased - `denote-link-insert-links-matching-regexp'. The purpose of these - aliases is to offer alternative, more descriptive names of select - commands. - - -7.12.2 The `denote-link-description-function' to format links -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - The user option `denote-link-description-function' takes as its value - the symbol of a function. This is used to format the text of the link. - The default function inserts the title. If the file has a signature, - it includes that as well, prepending it to the title. - - The function specified accepts a single `FILE' argument and returns - the description as a string. - - -8 Choose which commands to prompt for -═════════════════════════════════════ - - The user option `denote-commands-for-new-notes' specifies a list of - commands that are available at the `denote-command-prompt'. This - prompt is used by Denote commands that ask the user how to create a - new note, as described elsewhere in this manual: - - • [Open an existing note or create it if missing] - • [Link to a note or create it if missing] - - The default value includes all the basic file-creating commands - ([Points of entry]). Users may customise this value if (i) they only - want to see fewer options and/or (ii) wish to include their own custom - command in the list ([Write your own convenience commands]). - - -[Open an existing note or create it if missing] See section 3.6 - -[Link to a note or create it if missing] See section 7.7 - -[Points of entry] See section 3 - -[Write your own convenience commands] See section 3.1.4.1 - - -9 Fontification in Dired -════════════════════════ - - One of the upsides of Denote’s file-naming scheme is the predictable - pattern it establishes, which appears as a near-tabular presentation - in a listing of notes (i.e. in Dired). The `denote-dired-mode' can - help enhance this impression, by fontifying the components of the file - name to make the date (identifier) and keywords stand out. - - There are two ways to set the mode. Either use it for all - directories, which probably is not needed: - - ┌──── - │ (add-hook 'dired-mode-hook #'denote-dired-mode) - └──── - - Or configure the user option `denote-dired-directories' and then set - up the function `denote-dired-mode-in-directories': - - ┌──── - │ ;; We use different ways to specify a path for demo purposes. - │ (setq denote-dired-directories - │ (list denote-directory - │ (thread-last denote-directory (expand-file-name "attachments")) - │ (expand-file-name "~/Documents/vlog"))) - │ - │ (add-hook 'dired-mode-hook #'denote-dired-mode-in-directories) - └──── - - The user option `denote-dired-directories-include-subdirectories' - specifies whether the `denote-dired-directories' also cover their - subdirectories. By default they do not. Set this option to `t' to - include subdirectories as well. - - The faces we define for this purpose are: - - ⁃ `denote-faces-date' - ⁃ `denote-faces-delimiter' - ⁃ `denote-faces-extension' - ⁃ `denote-faces-keywords' - • `denote-faces-signature' - ⁃ `denote-faces-subdirectory' - ⁃ `denote-faces-time' - ⁃ `denote-faces-title' - - For more control, we also provide these: - - #+vindex denote-faces-year +vindex denote-faces-month +vindex - #denote-faces-day +vindex denote-faces-hour +vindex - #denote-faces-minute +vindex denote-faces-second - ⁃ `denote-faces-year' - ⁃ `denote-faces-month' - ⁃ `denote-faces-day' - ⁃ `denote-faces-hour' - ⁃ `denote-faces-minute' - ⁃ `denote-faces-second' - - For the time being, the `diredfl' package is not compatible with this - facility. - - The `denote-dired-mode' does not only fontify note files that were - created by Denote: it covers every file name that follows our naming - conventions ([The file-naming scheme]). This is particularly useful - for scenaria where, say, one wants to organise their collection of - PDFs and multimedia in a systematic way (and, perhaps, use them as - attachments for the notes Denote produces if you are writing Org notes - and are using its standand attachments’ facility). - - -[The file-naming scheme] See section 5 - - -10 Automatically rename Denote buffers -══════════════════════════════════════ - - The minor mode `denote-rename-buffer-mode' provides the means to - automatically rename the buffer of a Denote file upon visiting the - file. This applies both to existing Denote files as well as new ones - ([Points of entry]). Enable the mode thus: - - ┌──── - │ (denote-rename-buffer-mode 1) - └──── - - Buffers are named by applying the function specified in the user - option `denote-rename-buffer-function'. The default function is - `denote-rename-buffer': it renames the buffer based on the template - set in the user option `denote-rename-buffer-format'. By default, the - formatting template targets only the `TITLE' component of the file - name ([The file-naming scheme]). Other fields are explained elsewhere - in this manual ([The denote-rename-buffer-format]). - - Note that renaming a buffer is not the same as renaming a file - ([Renaming files]). The former is just for convenience inside of - Emacs. Whereas the latter is for writing changes to disk, making them - available to all programs. - - -[Points of entry] See section 3 - -[The file-naming scheme] See section 5 - -[The denote-rename-buffer-format] See section 10.1 - -[Renaming files] See section 4 - -10.1 The `denote-rename-buffer-format' option -───────────────────────────────────────────── - - The user option `denote-rename-buffer-format' controls how the - function `denote-rename-buffer' chooses the name of the - buffer-to-be-renamed. - - The value of this user option is a string. The following specifiers - are placeholders for Denote file name components ([The file-naming - scheme]): - - • The `%t' is the Denote `TITLE' of the file. - • The `%i' is the Denote `IDENTIFIER' of the file. - • The `%d' is the same as `%i' (`DATE' mnemonic). - • The `%s' is the Denote `SIGNATURE' of the file. - • The `%k' is the Denote `KEYWORDS' of the file. - • The `%%' is a literal percent sign. - - In addition, the following flags are available for each of the - specifiers: - - `0' - Pad to the width, if given, with zeros instead of spaces. - `-' - Pad to the width, if given, on the right instead of the left. - `<' - Truncate to the width and precision, if given, on the left. - `>' - Truncate to the width and precision, if given, on the right. - `^' - Convert to upper case. - `_' - Convert to lower case. - - When combined all together, the above are written thus: - - ┌──── - │ %SPECIFIER-CHARACTER - └──── - - - Any other string it taken as-is. Users may want, for example, to - include some text that makes Denote buffers stand out, such as a `[D]' - prefix. Examples: - - ┌──── - │ ;; Use the title (default) - │ (setq denote-rename-buffer-format "%t") - │ - │ ;; Use the title and keywords with some emoji in between - │ (setq denote-rename-buffer-format "%t 🤨 %k") - │ - │ ;; Use the title with a literal "[D]" before it - │ (setq denote-rename-buffer-format "[D] %t") - └──── - - Users who need yet more flexibility are best served by writing their - own function and assigning it to the `denote-rename-buffer-function'. - - -[The file-naming scheme] See section 5 - - -11 Use Org dynamic blocks -═════════════════════════ - - [ As part of version 2.3.0, all dynamic blocks are defined in the file - `denote-org-extras.el'. The file which was once called - `denote-org-dblock.el' contains aliases for the new function names - and dipslays a warning about its deprecation. There is no need to - `require' the `denote-org-extras' feature because all of Denote’s - Org dynamic blocks are autoloaded (meaning that they work as soon as - they are used). For backward compatibility, all dynamic blocks - retain their original names as an alias for the newer one. ] - - Denote can optionally integrate with Org mode’s “dynamic blocks” - facility. This means that it can use special blocks that are evaluated - with `C-c C-x C-u' (`org-dblock-update') to generate their contents. - The following subsections describe the types of Org dynamic blocks - provided by Denote. - - • [Org dynamic blocks to insert links or backlinks] - • [Org dynamic block to insert file contents] - - A dynamic block gets its contents by evaluating a function that - corresponds to the type of block. The block type and its parameters - are stated in the opening `#+BEGIN' line. Typing `C-c C-x C-u' - (`org-dblock-update') with point on that line runs (or re-runs) the - associated function with the given parameters and populates the - block’s contents accordingly. - - Dynamic blocks are particularly useful for metanote entries that - reflect on the status of earlier notes ([Writing metanotes]). - - The Org manual describes the technicalities of Dynamic Blocks. - Evaluate: - - ┌──── - │ (info "(org) Dynamic Blocks") - └──── - - -[Org dynamic blocks to insert links or backlinks] See section 11.1 - -[Org dynamic block to insert file contents] See section 11.2 - -[Writing metanotes] See section 7.9 - -11.1 Org dynamic blocks to insert links or backlinks -──────────────────────────────────────────────────── - - [ As part of version 2.3.0, all dynamic blocks are defined in the file - `denote-org-extras.el'. The file which was once called - `denote-org-dblock.el' contains aliases for the new function names - and dipslays a warning about its deprecation. There is no need to - `require' the `denote-org-extras' feature because all of Denote’s - Org dynamic blocks are autoloaded (meaning that they work as soon as - they are used). For backward compatibility, all dynamic blocks - retain their original names as an alias for the newer one. ] - - The `denote-links' block can be inserted at point with the command - `denote-org-extras-dblock-insert-links' or by manually including the - following in an Org file: - - ┌──── - │ #+BEGIN: denote-links :regexp "YOUR REGEXP HERE" :sort-by-component nil :reverse-sort nil :id-only nil - │ - │ #+END: - └──── - - - The `denote-links' block is also registered as an option for the - command `org-dynamic-block-insert-dblock'. - - Type `C-c C-x C-u' (`org-dblock-update') with point on the `#+BEGIN' - line to update the block. - - • The `:regexp' parameter is mandatory. Its value is a string and its - behaviour is the same as that of the `denote-add-links' command - ([Insert links matching a regexp]). Concretely, it produces a - typographic list of links to files matching the giving regular - expression. The value of the `:regexp' parameter may also be of the - form read by the `rx' macro (Lisp notation instead of a string), as - explained in the Emacs Lisp Reference Manual (evaluate this code to - read the documentation: `(info "(elisp) Rx Notation")'). Note that - you do not need to write an actual regular expression to get - meaningful results: even something like `_journal' will work to - include all files that have a `journal' keyword. - - • The `:sort-by-component' parameter is optional. It sorts the files - by the given Denote file name component. The value it accepts is an - unquoted symbol among `title', `keywords', `signature', - `identifier'. When using the command - `denote-org-extras-dblock-insert-files', this parameter is - automatically inserted together with the (`:regexp' parameter) and - the user is prompted for a file name component. - - • The `:reverse-sort' parameter is optional. It reverses the order in - which files appear in. This is meaningful even without the presence - of the parameter `:sort-by-component', though it also combines with - it. - - • The `:id-only' parameter is optional. It accepts a `t' value, in - which case links are inserted without a description text but only - with the identifier of the given file. This has the same meaning as - with the `denote-link' command and related facilities ([Linking - notes]). - - • An optional `:block-name' parameter can be specified with a string - value to add a `#+name' to the results. This is useful for further - processing using Org facilities (a feature that is outside Denote’s - purview). - - The same as above except for the `:regexp' parameter are true for the - `denote-backlinks' block. The block can be inserted at point with the - command `denote-org-extras-dblock-insert-backlinks' or by manually - writing this in an Org file: - - ┌──── - │ #+BEGIN: denote-backlinks :sort-by-component nil :reverse-sort nil :id-only nil - │ - │ #+END: - └──── - - - Finally, the `denote-missing-links' block is available with the - command `denote-org-extras-dblock-insert-missing-links'. It is like - the aforementioned `denote-links' block, except it only lists links to - files that are not present in the current buffer. The parameters are - otherwise the same: - - ┌──── - │ #+BEGIN: denote-missing-links :regexp "YOUR REGEXP HERE" :sort-by-component nil :reverse-sort nil :id-only nil - │ - │ #+END: - └──── - - - Remember to type `C-c C-x C-u' (`org-dblock-update') with point on the - `#+BEGIN' line to update the block. - - -[Insert links matching a regexp] See section 7.4 - -[Linking notes] See section 7 - - -11.2 Org dynamic block to insert file contents -────────────────────────────────────────────── - - [ As part of version 2.3.0, all dynamic blocks are defined in the file - `denote-org-extras.el'. The file which was once called - `denote-org-dblock.el' contains aliases for the new function names - and dipslays a warning about its deprecation. There is no need to - `require' the `denote-org-extras' feature because all of Denote’s - Org dynamic blocks are autoloaded (meaning that they work as soon as - they are used). For backward compatibility, all dynamic blocks - retain their original names as an alias for the newer one. ] - - Denote can optionally use Org’s dynamic blocks facility to produce a - section that lists entire file contents ([Use Org dynamic blocks]). - This works by instructing Org to match a regular expression of Denote - files, the same way we do with Denote links ([Insert links matching a - regexp]). - - This is useful to, for example, compile a dynamically concatenated - list of scattered thoughts on a given topic, like `^2023.*_emacs' for - a long entry that incorporates all the notes written in 2023 with the - keyword `emacs'. - - To produce such a block, call the command - `denote-org-extras-dblock-insert-files' or manually write the - following block in an Org file and then type `C-c C-x C-u' - (`org-dblock-update') on the `#+BEGIN' line to run it (do it again to - recalculate the block): - - ┌──── - │ #+BEGIN: denote-files :regexp "YOUR REGEXP HERE" - │ - │ #+END: - └──── - - - To fully control the output, include these additional optional - parameters, which are described further below: - - ┌──── - │ #+BEGIN: denote-files :regexp "YOUR REGEXP HERE" :sort-by-component nil :reverse-sort nil :no-front-matter nil :file-separator nil :add-links nil - │ - │ #+END: - └──── - - - • The `:regexp' parameter is mandatory. Its value is a string, - representing a regular expression to match Denote file names. Its - value may also be an `rx' expression instead of a string, as noted - in the previous section ([Org dynamic blocks to insert links or - backlinks]). Note that you do not need to write an actual regular - expression to get meaningful results: even something like `_journal' - will work to include all files that have a `journal' keyword. - - • The `:sort-by-component' parameter is optional. It sorts the files - by the given Denote file name component. The value it accepts is an - unquoted symbol among `title', `keywords', `signature', - `identifier'. When using the command - `denote-org-extras-dblock-insert-files', this parameter is - automatically inserted together with the (`:regexp' parameter) and - the user is prompted for a file name component. - - • The `:reverse-sort' parameter is optional. It reverses the order in - which files appear in. This is meaningful even without the presence - of the parameter `:sort-by-component', though it also combines with - it. - - • The `:file-separator' parameter is optional. If it is omitted, then - Denote will use no separator between the files it inserts. If the - value is `t' the `denote-org-extras-dblock-file-contents-separator' - is applied at the end of each file: it introduces some empty lines - and a horizontal rule between them to visually distinguish - individual files. If the `:file-separator' value is a string, it is - used as the file separator (e.g. use `"\n"' to insert just one empty - new line). - - • The `:no-front-matter' parameter is optional. When set to a `t' - value, Denote tries to remove front matter from the files it is - inserting in the dynamic block. The technique used to perform this - operation is by removing all lines from the top of the file until - the first empty line. This works with the default front matter that - Denote adds, but is not 100% reliable with all sorts of user-level - modifications and edits to the file. When the `:no-front-matter' is - set to a natural number, Denote will omit that many lines from the - top of the file. - - • The `:add-links' parameter is optional. When it is set to a `t' - value, all files are inserted as a typographic list and are indented - accordingly. The first line in each list item is a link to the file - whose contents are inserted in the following lines. When the value - is `id-only', then links are inserted without a description text but - only with the identifier of the given file. This has the same - meaning as with the `denote-link' command and related facilities - ([Linking notes]). Remember that Org can fold the items in a - typographic list the same way it does with headings. So even long - files can be presented in this format without much trouble. - - • An optional `:block-name' parameter can be specified with a string - value to add a `#+name' to the results. This is useful for further - processing using Org facilities (a feature that is outside Denote’s - purview). - - -[Use Org dynamic blocks] See section 11 - -[Insert links matching a regexp] See section 7.4 - -[Org dynamic blocks to insert links or backlinks] See section 11.1 - -[Linking notes] See section 7 - - -12 Sort files by component -══════════════════════════ - - The `denote-sort.el' file is an optional extension to the core - functionality of Denote, which empowers users to sort files by the - given file name component ([The file-naming scheme]). - - The command `denote-sort-dired' produces a Dired file listing with a - flat, filtered, and sorted set of files from the `denote-directory'. - It does so by means of three minibuffer prompts: - - 1. It first asks for a regular expression with which to match Denote - files. Remember that due to Denote’s efficient file-naming scheme, - you do not need to write some complex regular expression. Even - something like `_journal' will match only files with a `journal' - keyword. - 2. Once the regular expression is provided, the command asks for a - Denote file name component to sort files by. This is a symbol among - `title', `keywords', `signature', and `identifier'. - 3. Finally, it asks a “yes or no” on whether to reverse the sort - order. - - The resulting Dired listing is a regular Dired buffer, unlike that of - `dired-virtual-mode' ([Use `dired-virtual-mode' for arbitrary file - listings]). - - The dynamic Org blocks that Denote defines to insert file contents - also use this feature ([Org dynamic block to insert file contents]). - - DEVELOPMENT NOTE as of 2023-11-30 07:24 +0200: The sort mechanism will - be incorporated in more functions on a case-by-case basis, subject to - user feedback. For the time being, I am not documenting the functions - that are only accessed from Lisp. Do not hesitate to contact me - (Protesilaos) in person or on one of the development sources (mailing - list or GitHub/GitLab mirror) if you have a use-case where sorting - seems useful. I am happy to help but do not want to roll this feature - everywhere before eliciting relevant feedback: once we add it, we are - not going back. - - -[The file-naming scheme] See section 5 - -[Use `dired-virtual-mode' for arbitrary file listings] See section 15.3 - -[Org dynamic block to insert file contents] See section 11.2 - - -13 Keep a journal or diary -══════════════════════════ - - Denote provides a general-purpose mechanism to create new files that - broadly count as “notes” ([Points of entry]). Such files can be daily - entries in a journal. While it is possible to use the generic - `denote' command to maintain a journal, we provide an optional set of - convenience options and commands as part of - `denote-journal-extras.el'. To use those, add the following the - Denote configuration: - - ┌──── - │ (require 'denote-journal-extras) - └──── - - The command `denote-journal-extras-new-entry' creates a new entry in - the journal. Such a file has the `denote-journal-extras-keyword', - which is `journal' by default ([The file-naming scheme]). The user - can set this keyword to an arbitrary string (single word is - preferred). New journal entries can be stored in the - `denote-directory' or subdirectory thereof. To make it easier for the - user, the new journal entry will be placed in - `denote-journal-extras-directory', which defaults to a subdirectory of - `denote-directory' called `journal'. - - If `denote-journal-extras-directory' is nil, the `denote-directory' is - used. Journal entries will thus be in a flat listing together with - all other notes. They can still be retrieved easily by searching for - the `denote-journal-extras-keyword' ([Features of the file-naming - scheme for searching or filtering]). - - Furthermore, the command `denote-journal-extras-new-entry' will use - the current date as the title of the new entry. The exact format is - controlled by the user option `denote-journal-extras-title-format'. - Acceptable values for `denote-journal-extras-title-format' and their - corresponding styles are: - - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - Symbol Style - ──────────────────────────────────────────────────────────── - day Monday - day-date-month-year Monday 19 September 2023 - day-date-month-year-24h Monday 19 September 2023 20:49 - day-date-month-year-12h Monday 19 September 2023 08:49 PM - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - For example: - - ┌──── - │ (setq denote-journal-extras-title-format 'day-date-month-year) - └──── - - If the value of this user option is `nil', then - `denote-journal-extras-new-entry' will prompt for a title. - - The `denote-journal-extras-new-entry' command also accepts an optional - `DATE' argument. When called internactively, this is a universal - prefix (e.g. `C-u' with the default key bindings). With `DATE', it - prompts for a date to create a new journal entry for. The date prompt - can optionally use the Org date+calendar selection interface ([The - `denote-date-prompt-use-org-read-date' option]). - - In terms of workflow, using the current date as the title is better - for maintaining a daily journal. A prompt for an arbitrary title is - more suitable for those who like to keep a record of something like a - thought or event (though this can also be achieved by the regular - `denote' command or maybe `denote-subdirectory'). - - The `denote-journal-extras-new-entry' command calls the normal hook - `denote-journal-extras-hook' after it is done. The user can leverage - this to produce consequences therefrom, such as to set a timer with - the `tmr' package from GNU ELPA ([Journaling with a timer]). - - The command `denote-journal-extras-new-or-existing-entry' locates an - existing journal entry or creates a new one. A journal entry is one - that has `denote-journal-extras-keyword' as part of its file name. If - there are multiple journal entries for the current date, it prompts - for one among them using minibuffer completion. If there is only one, - it visits it outright. If there is no journal entry, it creates one - by calling `denote-journal-extra-new-entry' (as described above). - - The command `denote-journal-extras-link-or-create-entry' links to the - journal entry for today or creates it in the background, if missing, - and then links to it from the current file. If there are multiple - journal entries for the same day, it prompts to select one among them - and then links to it. When called with an optional prefix argument - (such as `C-u' with default key bindings), the command prompts for a - date and then performs the aforementioned. With a double prefix - argument (`C-u C-u'), it also produces a link whose description - includes just the file’s identifier. - - -[Points of entry] See section 3 - -[The file-naming scheme] See section 5 - -[Features of the file-naming scheme for searching or filtering] See -section 5.3 - -[The `denote-date-prompt-use-org-read-date' option] See section 3.1.6 - -[Journaling with a timer] See section 13.1 - -13.1 Journaling with a timer -──────────────────────────── - - [ Revised as part of version 2.1.0 to conform with how we now tend to - the needs of users who use Denote for journaling purposes ([Keep a - journal or diary]). ] - - Sometimes journaling is done with the intent to hone one’s writing - skills. Perhaps you are learning a new language or wish to - communicate your ideas with greater clarity and precision. As with - everything that requires a degree of sophistication, you have to work - for it—write, write, write! - - One way to test your progress is to set a timer. It helps you gauge - your output and its quality. To use a timer with Emacs, consider the - `tmr' package. A new timer can be set with something like this: - - ┌──── - │ ;; Set 10 minute timer with the given description - │ (tmr "10" "Practice writing in my journal") - └──── - - To make this timer start as soon as a new journal entry is created - with the command `denote-journal-extras-new-entry', add a function to - the `denote-journal-extras-hook'. For example: - - ┌──── - │ ;; Add an anonymous function, which is more difficult to modify after - │ ;; the fact: - │ (add-hook 'denote-journal-extras-hook (lambda () - │ (tmr "10" "Practice writing in my journal"))) - │ - │ ;; Or write a small function that you can then modify without - │ ;; revaluating the hook: - │ (defun my-denote-tmr () - │ (tmr "10" "Practice writing in my journal")) - │ - │ (add-hook 'denote-journal-extras-hook 'my-denote-tmr) - │ - │ ;; Or to make it fully featured, define variables for the duration and - │ ;; the description and set it up so that you only need to modify - │ ;; those: - │ (defvar my-denote-tmr-duration "10") - │ - │ (defvar my-denote-tmr-description "Practice writing in my journal") - │ - │ (defun my-denote-tmr () - │ (tmr my-denote-tmr-duration my-denote-tmr-description)) - │ - │ (add-hook 'denote-journal-extras-hook 'my-denote-tmr) - └──── - - Once the timer elapses, stop writing and review your performance. - Practice makes perfect! - - Sources for `tmr': - - ⁃ Package name (GNU ELPA): `tmr' - ⁃ Official manual: - ⁃ Change log: - ⁃ Git repo on SourceHut: - • Mirrors: - ⁃ GitHub: - ⁃ GitLab: - ⁃ Mailing list: - - -[Keep a journal or diary] See section 13 - - -14 Minibuffer histories -═══════════════════════ - - Denote has a dedicated minibuffer history for each one of its prompts. - This practically means that using `M-p' (`previous-history-element') - and `M-n' (`next-history-element') will only cycle through the - relevant record of inputs, such as your latest titles in the `TITLE' - prompt, and keywords in the `KEYWORDS' prompt. - - The built-in `savehist' library saves minibuffer histories. Sample - configuration: - - ┌──── - │ (require 'savehist) - │ (setq savehist-file (locate-user-emacs-file "savehist")) - │ (setq history-length 500) - │ (setq history-delete-duplicates t) - │ (setq savehist-save-minibuffer-history t) - │ (add-hook 'after-init-hook #'savehist-mode) - └──── - - -15 Extending Denote -═══════════════════ - - Denote is a tool with a narrow scope: create notes and link between - them, based on the aforementioned file-naming scheme. For other - common operations the user is advised to rely on standard Emacs - facilities or specialised third-party packages. This section covers - the details. - - -15.1 Create a new note in any directory -─────────────────────────────────────── - - The commands that create new files are designed to write to the - `denote-directory'. The idea is that the linking mechanism can find - any file by its identifier if it is in the `denote-directory' - (searching the entire file system would be cumbersome). - - However, these are cases where the user needs to create a new note in - an arbitrary directory. The following command can do this. Put the - code in your configuration file and evaluate it. Then call the command - by its name with `M-x'. - - ┌──── - │ (defun my-denote-create-note-in-any-directory () - │ "Create new Denote note in any directory. - │ Prompt for the directory using minibuffer completion." - │ (declare (interactive-only t)) - │ (interactive) - │ (let ((denote-directory (read-directory-name "New note in: " nil nil :must-match))) - │ (call-interactively 'denote))) - └──── - - -15.2 Narrow the list of files in Dired -────────────────────────────────────── - - Emacs’ standard file manager (or directory editor) can read a regular - expression to mark the matching files. This is the command - `dired-mark-files-regexp', which is bound to `% m' by default. For - example, `% m _denote' will match all files that have the `denote' - keyword ([Features of the file-naming scheme for searching or - filtering]). - - Once the files are matched, the user has two options: (i) narrow the - list to the matching items or (ii) exclude the matching items from the - list. - - For the former, we want to toggle the marks by typing `t' (calls the - command `dired-toggle-marks' by default) and then hit the letter `k' - (for `dired-do-kill-lines'). The remaining files are those that match - the regexp that was provided earlier. - - For the latter approach of filtering out the matching items, simply - involves the use of the `k' command (`dired-do-kill-lines') to omit - the marked files from the list. - - These sequences can be combined to incrementally narrow the list. - Note that `dired-do-kill-lines' does not delete files: it simply hides - them from the current view. - - Revert to the original listing with `g' (`revert-buffer'). - - For a convenient wrapper, consider this example: - - ┌──── - │ (defvar prot-dired--limit-hist '() - │ "Minibuffer history for `prot-dired-limit-regexp'.") - │ - │ ;;;###autoload - │ (defun prot-dired-limit-regexp (regexp omit) - │ "Limit Dired to keep files matching REGEXP. - │ - │ With optional OMIT argument as a prefix (\\[universal-argument]), - │ exclude files matching REGEXP. - │ - │ Restore the buffer with \\`\\[revert-buffer]'." - │ (interactive - │ (list - │ (read-regexp - │ (concat "Files " - │ (when current-prefix-arg - │ (propertize "NOT " 'face 'warning)) - │ "matching PATTERN: ") - │ nil 'prot-dired--limit-hist) - │ current-prefix-arg)) - │ (dired-mark-files-regexp regexp) - │ (unless omit (dired-toggle-marks)) - │ (dired-do-kill-lines)) - └──── - - -[Features of the file-naming scheme for searching or filtering] See -section 5.3 - - -15.3 Use `dired-virtual-mode' for arbitrary file listings -───────────────────────────────────────────────────────── - - Emacs’ Dired is a powerful file manager that builds its functionality - on top of the Unix `ls' command. As noted elsewhere in this manual, - the user can update the `ls' flags that Dired uses to display its - contents ([I want to sort by last modified, why won’t Denote let - me?]). - - What Dired cannot do is parse the output of a result that is produced - by piped commands, such as `ls -l | sort -t _ -k2'. This specific - example targets the second underscore-separated field of the file - name, per our conventions ([The file-naming scheme]). Conceretely, it - matches the “alpha” as the sorting key in something like this: - - ┌──── - │ 20220929T200432--testing-file-one__alpha.txt - └──── - - Consider then, how Dired will sort those files by their identifier: - - ┌──── - │ 20220929T200432--testing-file-one__alpha.txt - │ 20220929T200532--testing-file-two__beta.txt - │ 20220929T200632--testing-file-three__alpha.txt - │ 20220929T200732--testing-file-four__beta.txt - └──── - - Whereas on the command line, we can get the following: - - ┌──── - │ $ ls | sort -t _ -k 2 - │ 20220929T200432--testing-file-one__alpha.txt - │ 20220929T200632--testing-file-three__alpha.txt - │ 20220929T200532--testing-file-two__beta.txt - │ 20220929T200732--testing-file-four__beta.txt - └──── - - This is where `dired-virtual-mode' shows its utility. If we tweak our - command-line invocation to include `ls -l', this mode can behave like - Dired on the listed files. (We omit the output of the `-l' flag from - this tutorial, as it is too verbose.) - - What we now need is to capture the output of `ls -l | sort -t _ -k 2' - in an Emacs buffer and then enable `dired-virtual-mode'. To do that, - we can rely on either `M-x shell' or `M-x eshell' and then manually - copy the relevant contents. - - For the user’s convenience, I share what I have for Eshell to quickly - capture the last command’s output in a dedicated buffer: - - ┌──── - │ (defcustom prot-eshell-output-buffer "*Exported Eshell output*" - │ "Name of buffer with the last output of Eshell command. - │ Used by `prot-eshell-export'." - │ :type 'string - │ :group 'prot-eshell) - │ - │ (defcustom prot-eshell-output-delimiter "* * *" - │ "Delimiter for successive `prot-eshell-export' outputs. - │ This is formatted internally to have newline characters before - │ and after it." - │ :type 'string - │ :group 'prot-eshell) - │ - │ (defun prot-eshell--command-prompt-output () - │ "Capture last command prompt and its output." - │ (let ((beg (save-excursion - │ (goto-char (eshell-beginning-of-input)) - │ (goto-char (point-at-bol))))) - │ (when (derived-mode-p 'eshell-mode) - │ (buffer-substring-no-properties beg (eshell-end-of-output))))) - │ - │ ;;;###autoload - │ (defun prot-eshell-export () - │ "Produce a buffer with output of the last Eshell command. - │ If `prot-eshell-output-buffer' does not exist, create it. Else - │ append to it, while separating multiple outputs with - │ `prot-eshell-output-delimiter'." - │ (interactive) - │ (let ((eshell-output (prot-eshell--command-prompt-output))) - │ (with-current-buffer (get-buffer-create prot-eshell-output-buffer) - │ (let ((inhibit-read-only t)) - │ (goto-char (point-max)) - │ (unless (eq (point-min) (point-max)) - │ (insert (format "\n%s\n\n" prot-eshell-output-delimiter))) - │ (goto-char (point-at-bol)) - │ (insert eshell-output) - │ (switch-to-buffer-other-window (current-buffer)))))) - └──── - - Bind `prot-eshell-export' to a key in the `eshell-mode-map' and give - it a try (I use `C-c C-e'). In the produced buffer, activate the - `dired-virtual-mode'. - - -[I want to sort by last modified, why won’t Denote let me?] See section -23.7 - -[The file-naming scheme] See section 5 - - -15.4 Use Embark to collect minibuffer candidates -──────────────────────────────────────────────── - - `embark' is a remarkable package that lets you perform relevant, - context-dependent actions using a prefix key (simplifying in the - interest of brevity). - - For our purposes, Embark can be used to produce a Dired listing - directly from the minibuffer. Suppose the current note has links to - three other notes. You might use the `denote-find-link' command to - pick one via the minibuffer. But why not turn those three links into - their own Dired listing? While in the minibuffer, invoke `embark-act' - which you may have already bound to `C-.' and then follow it up with - `E' (for the `embark-export' command). - - This pattern can be repeated with any list of candidates, meaning that - you can narrow the list by providing some input before eventually - exporting the results with Embark. - - Overall, this is very powerful and you might prefer it over doing the - same thing directly in Dired, since you also benefit from all the - power of the minibuffer ([Narrow the list of files in Dired]). - - -[Narrow the list of files in Dired] See section 15.2 - - -15.5 Search file contents -───────────────────────── - - Emacs provides built-in commands which are wrappers of standard Unix - tools: `M-x grep' lets the user input the flags of a `grep' call and - pass a regular expression to the `-e' flag. - - The author of Denote uses this thin wrapper instead: - - ┌──── - │ (defvar prot-search--grep-hist '() - │ "Input history of grep searches.") - │ - │ ;;;###autoload - │ (defun prot-search-grep (regexp &optional recursive) - │ "Run grep for REGEXP. - │ - │ Search in the current directory using `lgrep'. With optional - │ prefix argument (\\[universal-argument]) for RECURSIVE, run a - │ search starting from the current directory with `rgrep'." - │ (interactive - │ (list - │ (read-from-minibuffer (concat (if current-prefix-arg - │ (propertize "Recursive" 'face 'warning) - │ "Local") - │ " grep for PATTERN: ") - │ nil nil nil 'prot-search--grep-hist) - │ current-prefix-arg)) - │ (unless grep-command - │ (grep-compute-defaults)) - │ (if recursive - │ (rgrep regexp "*" default-directory) - │ (lgrep regexp "*" default-directory))) - └──── - - Rather than maintain custom code, consider using the excellent - `consult' package: it provides commands such as `consult-grep' and - `consult-find' which provide live results and are generally easier to - use than the built-in commands. - - -15.6 Bookmark the directory with the notes -────────────────────────────────────────── - - Part of the reason Denote does not reinvent existing functionality is - to encourage you to learn more about Emacs. You do not need a bespoke - “jump to my notes” directory because such commands do not scale well. - Will you have a “jump to my downloads” then another for multimedia and - so on? No. - - Emacs has a built-in framework for recording persistent markers to - locations. Visit the `denote-directory' (or any dir/file for that - matter) and invoke the `bookmark-set' command (bound to `C-x r m' by - default). It lets you create a bookmark. - - The list of bookmarks can be reviewed with the `bookmark-bmenu-list' - command (bound to `C-x r l' by default). A minibuffer interface is - available with `bookmark-jump' (`C-x r b'). - - If you use the `consult' package, its default `consult-buffer' command - has the means to group together buffers, recent files, and bookmarks. - Each of those types can be narrowed to with a prefix key. The package - `consult-dir' is an extension to `consult' which provides useful - extras for working with directories, including bookmarks. - - -15.7 Use the `denote-explore' package to explore your notes -─────────────────────────────────────────────────────────── - - Peter Prevos has developed the `denote-explore' package which provides - four groups of Emacs commands to explore your Denote files: - - Summary statistics - Count notes, attachments and keywords. - Random walks - Generate new ideas using serendipity. - Janitor - Manage your denote collection. - Visualisations - Visualise your Denote network. - - The package’s documentation covers the details: - . - - -15.8 Use the `citar-denote' package for bibliography notes -────────────────────────────────────────────────────────── - - Peter Prevos has produced the `citar-denote' package which makes it - possible to write notes on BibTeX entries with the help of the `citar' - package. These notes have the citation’s unique key associated with - them in the file’s front matter. They also get a configurable keyword - in their file name, making it easy to find them in Dired and/or - retrieve them with the various Denote methods. - - With `citar-denote', the user leverages standard minibuffer completion - mechanisms (e.g. with the help of the `vertico' and `embark' packages) - to manage bibliographic notes and access those notes with ease. The - package’s documentation covers the details: - . - - -15.9 Use the `consult-notes' package -──────────────────────────────────── - - If you are using Daniel Mendler’s `consult' (which is a brilliant - package), you will most probably like its `consult-notes' extension, - developed by Colin McLear. It uses the familiar mechanisms of Consult - to preview the currently selected entry and to filter searches via a - prefix key. For example: - - ┌──── - │ (setq consult-notes-file-dir-sources - │ `(("Denote Notes" ?d ,(denote-directory)) - │ ("Books" ?b "~/Documents/books/"))) - └──── - - With the above, `M-x consult-notes' will list the files in those two - directories. If you type `d' and space, it narrows the list to just - the notes, while `b' does the same for books. - - The other approach is to enable the `consult-notes-denote-mode'. It - takes care to add the `denote-directory' to the sources that - `consult-notes' reads from. Denote notes are then filtered by the `d' - prefix followed by a space. - - The minor mode has the extra feature of reformatting the title of - notes shown in the minibuffer. It isolates the `TITLE' component of - each note and shows it without hyphens, while presenting keywords in - their own column. The user option `consult-notes-denote-display-id' - can be set to `nil' to hide the identifier. Depending on how one - searches through their notes, this refashioned presentation may be the - best option ([Features of the file-naming scheme for searching or - filtering]). - - -[Features of the file-naming scheme for searching or filtering] See -section 5.3 - - -15.10 Use the `denote-menu' package -─────────────────────────────────── - - Denote’s file-naming scheme is designed to be efficient and to provide - valueable meta information about the file. The cost, however, is that - it is terse and harder to read, depending on how the user chooses to - filter and process their notes. - - To this end, [the `denote-menu' package by Mohamed Suliman] provides - the convenience of a nice tabular interface for all notes. - `denote-menu' removes the delimiters that are found in Denote file - names and presents the information in a human-readable format. - Furthermore, the package provides commands to interact with the list - of notes, such as to filter them and to transition from the tabular - list to Dired. Its documentation expands on the technicalities. - - -[the `denote-menu' package by Mohamed Suliman] - - - -15.11 Treat your notes as a project -─────────────────────────────────── - - Emacs has a built-in library for treating a directory tree as a - “project”. This means that the contents of this tree are seen as part - of the same set, so commands like `project-switch-to-buffer' (`C-x p - b' by default) will only consider buffers in the current project - (e.g. three notes that are currently being visited). - - Normally, a “project” is a directory tree whose root is under version - control. For our purposes, all you need is to navigate to the - `denote-directory' (for the shell or via Dired) and use the - command-line to run this (requires the `git' executable): - - ┌──── - │ git init - └──── - - - From Dired, you can type `M-!' which invokes - `dired-smart-shell-command' and then run the git call there. - - The project can then be registered by invoking any project-related - command inside of it, such as `project-find-file' (`C-x p f'). - - It is a good idea to keep your notes under version control, as that - gives you a history of changes for each file. We shall not delve into - the technicalities here, though suffice to note that Emacs’ built-in - version control framework or the exceptionally well-crafted `magit' - package will get the job done (VC can work with other backends besides - Git). - - -15.12 Use the tree-based file prompt for select commands -──────────────────────────────────────────────────────── - - Older versions of Denote had a file prompt that resembled that of the - standard `find-file' command (bound to `C-x C-f' by default). This - means that it used a tree-based method of navigating the filesystem by - selecting the specific directory and then the given file. - - Currently, Denote flattens the file prompt so that every file in the - `denote-directory' and its subdirectories can be matched from anywhere - using the power of Emacs’ minibuffer completion (such as with the help - of the `orderless' package in addition to built-in options). - - Users who need the old behaviour on a per-command basis can define - their own wrapper functions as shown in the following code block. - - ┌──── - │ ;; This is the old `denote-file-prompt' that we renamed to - │ ;; `denote-file-prompt-original' for clarity. - │ (defun denote-file-prompt-original (&optional initial-text) - │ "Prompt for file with identifier in variable `denote-directory'. - │ With optional INITIAL-TEXT, use it to prepopulate the minibuffer." - │ (read-file-name "Select note: " (denote-directory) nil nil initial-text - │ (lambda (f) - │ (or (denote-file-has-identifier-p f) - │ (file-directory-p f))))) - │ - │ ;; Our wrapper command that changes the current `denote-file-prompt' - │ ;; to the functionality of `denote-file-prompt-original' only when - │ ;; this command is used. - │ (defun my-denote-link () - │ "Call `denote-link' but use Denote's original file prompt. - │ See `denote-file-prompt-original'." - │ (interactive) - │ (cl-letf (((symbol-function 'denote-file-prompt) #'denote-file-prompt-original)) - │ (call-interactively #'denote-link))) - └──── - - -15.13 Rename files with Denote in the Image Dired thumbnails buffer -─────────────────────────────────────────────────────────────────── - - [Rename files with Denote using `dired-preview'] - - Just as with the `denote-dired-rename-marked-files-with-keywords', we - can use Denote in the Image Dired buffer ([Rename multiple files at - once]). Here is the custom code: - - ┌──── - │ (autoload 'image-dired--with-marked "image-dired") - │ (autoload 'image-dired-original-file-name "image-dired-util") - │ - │ (defun my-denote-image-dired-rename-marked-files (keywords) - │ "Like `denote-dired-rename-marked-files-with-keywords' but for Image Dired. - │ Prompt for KEYWORDS and rename all marked files in the Image - │ Dired buffer to have a Denote-style file name with the given - │ KEYWORDS. - │ - │ IMPORTANT NOTE: if there are marked files in the corresponding - │ Dired buffers, those will be targeted as well. This is not the - │ fault of Denote: it is how Dired and Image Dired work in tandem. - │ To only rename the marked thumbnails, start by unmarking - │ everything in Dired. Then mark the items in Image Dired and - │ invoke this command." - │ (interactive (list (denote-keywords-prompt)) image-dired-thumbnail-mode) - │ (image-dired--with-marked - │ (when-let* ((file (image-dired-original-file-name)) - │ (dir (file-name-directory file)) - │ (id (or (denote-retrieve-filename-identifier file) "")) - │ (file-type (denote-filetype-heuristics file)) - │ (title (denote--retrieve-title-or-filename file file-type)) - │ (signature (or (denote-retrieve-filename-signature file) "") - │ (extension (file-name-extension file t)) - │ (new-name (denote-format-file-name dir id keywords title extension signature)) - │ (default-directory dir)) - │ (denote-rename-file-and-buffer file new-name)))) - └──── - - While the `my-denote-image-dired-rename-marked-files' renames files in - the helpful Denote-compliant way, users may still need to not prepend - a unique identifier and not sluggify (hyphenate and downcase) the - image’s existing file name. To this end, the following custom command - can be used instead: - - ┌──── - │ (defun my-image-dired-rename-marked-files (keywords) - │ "Like `denote-dired-rename-marked-files-with-keywords' but for Image Dired. - │ Prompt for keywords and rename all marked files in the Image - │ Dired buffer to have Denote-style keywords, but none of the other - │ conventions of Denote's file-naming scheme." - │ (interactive (list (denote-keywords-prompt)) image-dired-thumbnail-mode) - │ (image-dired--with-marked - │ (when-let* ((file (image-dired-original-file-name)) - │ (dir (file-name-directory file)) - │ (file-type (denote-filetype-heuristics file)) - │ (title (denote--retrieve-title-or-filename file file-type)) - │ (extension (file-name-extension file t)) - │ (kws (denote--keywords-combine keywords)) - │ (new-name (concat dir title "__" kws extension)) - │ (default-directory dir)) - │ (denote-rename-file-and-buffer file new-name)))) - └──── - - -[Rename files with Denote using `dired-preview'] See section 15.14 - -[Rename multiple files at once] See section 4.3 - - -15.14 Rename files with Denote using `dired-preview' -──────────────────────────────────────────────────── - - The `dired-preview' package (by me/Protesilaos) automatically displays - a preview of the file at point in Dired. This can be helpful in - tandem with Denote when we want to rename multiple files by taking a - quick look at their contents. - - The command `denote-dired-rename-marked-files-with-keywords' will - generate Denote-style file names based on the keywords it prompts - for. Identifiers are derived from each file’s modification date - ([Rename multiple files at once]). There is no need for any custom - code in this scenario. - - As noted in the section about Image Dired, the user may sometimes not - need a fully fledged Denote-style file name but only append - Denote-like keywords to each file name (e.g. `Original - Name__denote_test.jpg' instead of - `20230710T195843--original-name__denote_test.jpg'). - - [Rename files with Denote in the Image Dired thumbnails buffer] - - In such a workflow, it is unlikely to be dealing with ordinary text - files where front matter can be helpful. A custom command does not - need to behave like what Denote provides out-of-the-box, but can - instead append keywords to file names without conducting any further - actions. We thus have: - - ┌──── - │ (defun my-denote-dired-rename-marked-files-keywords-only () - │ "Like `denote-dired-rename-marked-files-with-keywords' but only for keywords in file names. - │ - │ Prompt for keywords and rename all marked files in the Dired - │ buffer to only have Denote-style keywords, but none of the other - │ conventions of Denote's file-naming scheme." - │ (interactive nil dired-mode) - │ (if-let ((marks (dired-get-marked-files))) - │ (let ((keywords (denote-keywords-prompt))) - │ (dolist (file marks) - │ (let* ((dir (file-name-directory file)) - │ (file-type (denote-filetype-heuristics file)) - │ (title (denote--retrieve-title-or-filename file file-type)) - │ (extension (file-name-extension file t)) - │ (kws (denote--keywords-combine keywords)) - │ (new-name (concat dir title "__" kws extension))) - │ (denote-rename-file-and-buffer file new-name))) - │ (revert-buffer)) - │ (user-error "No marked files; aborting"))) - └──── - - -[Rename multiple files at once] See section 4.3 - -[Rename files with Denote in the Image Dired thumbnails buffer] See -section 15.13 - - -15.15 Avoid duplicate identifiers when exporting Denote notes -───────────────────────────────────────────────────────────── - - When exporting Denote notes to, for example, an HTML or PDF file, - there is a high probability that the same file name is used with a new - extension. This is problematic because it creates files with - duplicate identifiers. The `20230515T085612--example__keyword.org' - produces a `20230515T085612--example__keyword.pdf'. Any link to the - `20230515T085612' will thus break: it does not honor Denote’s - expectation of finding unique identifiers. This is not the fault of - Denote: exporting is done by the user without Denote’s involvement. - - Org Mode and Markdown use different approaches to exporting files. No - recommended method is available for plain text files as there is no - standardised export functionality for this format (the user can always - create a new note using the file type they want on a case-by-case - basis: [Convenience commands for note creation]). - - -[Convenience commands for note creation] See section 3.1.4 - -15.15.1 Export Denote notes with Org Mode -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - Org Mode has a built-in configurable export engine. You can prevent - duplicate identifiers when exporting manually for each exported file - or by advising the Org export function. - - Denote also provides commands to convert `denote:' links to their - `file:' equivalent, in case this is a required pre-processing step for - export purposes ([Convert `denote:' links to `file:' links]). - - -[Convert `denote:' links to `file:' links] See section 7.11 - -◊ 15.15.1.1 Manually configure Org export - - Insert `#+export_file_name: FILENAME' in the front matter before - exporting to force a filename called whatever the value of `FILENAME' - is. The `FILENAME' does not specify the file type extension, such as - `.pdf'. This is up to the export engine. For example, a Denote note - with a complete file name of `20230515T085612--example__keyword.org' - and a front matter entry of `#+export_file_name: hello' will be - exported as `hello.pdf'. - - The advantage of this manual method is that it gives the user full - control over the resulting file name. The disadvantage is that it - depends on the user’s behaviour. Forgetting to add a new name can - lead to duplicate identifiers, as already noted in the introduction to - this section ([Export Denote notes]). - - - [Export Denote notes] See section 15.15 - - -◊ 15.15.1.2 Automatically store Org exports in another folder - - It is possible to automatically place all exports in another folder by - making Org’s function `org-export-output-file-name' create the target - directory if needed and move the exported file there. Remember that - advising Elisp code must be handled with care, as it might break the - original function in subtle ways. - - ┌──── - │ (defvar my-org-export-output-directory-prefix "./export_" - │ "Prefix of directory used for org-mode export. - │ - │ The single dot means that the directory is created on the same - │ level as the one where the Org file that performs the exporting - │ is. Use two dots to place the directory on a level above the - │ current one. - │ - │ If this directory is part of `denote-directory', make sure it is - │ not read by Denote. See `denote-excluded-directories-regexp'. - │ This way there will be no known duplicate Denote identifiers - │ produced by the Org export mechanism.") - │ - │ (defun my-org-export-create-directory (fn extension &rest args) - │ "Move Org export file to its appropriate directory. - │ - │ Append the file type EXTENSION of the exported file to - │ `my-org-export-output-directory-prefix' and, if absent, create a - │ directory named accordingly. - │ - │ Install this as advice around `org-export-output-file-name'. The - │ EXTENSION is supplied by that function. ARGS are its remaining - │ arguments." - │ (let ((export-dir (format "%s%s" my-org-export-output-directory-prefix extension))) - │ (unless (file-directory-p export-dir) - │ (make-directory export-dir))) - │ (apply fn extension args)) - │ - │ (advice-add #'org-export-output-file-name :around #'my-org-export-create-directory) - └──── - - The target export directory should not be a subdirectory of - `denote-directory', as that will result in duplicate identifiers. - Exclude it with the `denote-excluded-directories-regexp' user option - ([Exclude certain directories from all operations]). - - [ NOTE: I (Protesilaos) am not a LaTeX user and cannot test the - following. ] - - Using a different directory will require some additional configuration - when exporting using LaTeX. The export folder cannot be inside the - path of the `denote-directory' to prevent Denote from recognising it - as an attachment: - . - - - [Exclude certain directories from all operations] See section 3.8 - - -◊ 15.15.1.3 Org Mode Publishing - - Org Mode also has a publishing tool for exporting a collection of - files. Some user might apply this approach to convert their note - collection to a public or private website. - - The `org-publish-project-alist' variable drives the publishing - process, including the publishing directory. - - The publishing directory should not be a subdirectory of - `denote-directory', as that will result in duplicate identifiers. - Exclude it with the `denote-excluded-directories-regexp' user option - ([Exclude certain directories from all operations]). - - - [Exclude certain directories from all operations] See section 3.8 - - -15.15.2 Export Denote notes with Markdown -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - Exporting from Markdown requires an external processor (e.g., - Markdown.pl, Pandoc, or MultiMarkdown). The `markdown-command' - variable defines the command line used in export, for example: - - ┌──── - │ (setq markdown-command "multimarkdown") - └──── - - The export process thus occurs outside of Emacs. Users need to read - the documentation of their preferred processor to prevent the creation - of duplicate Denote identifiers. - - -16 Installation -═══════════════ - - - - -16.1 GNU ELPA package -───────────────────── - - The package is available as `denote'. Simply do: - - ┌──── - │ M-x package-refresh-contents - │ M-x package-install - └──── - - - And search for it. - - GNU ELPA provides the latest stable release. Those who prefer to - follow the development process in order to report bugs or suggest - changes, can use the version of the package from the GNU-devel ELPA - archive. Read: - . - - -16.2 Manual installation -──────────────────────── - - Assuming your Emacs files are found in `~/.emacs.d/', execute the - following commands in a shell prompt: - - ┌──── - │ cd ~/.emacs.d - │ - │ # Create a directory for manually-installed packages - │ mkdir manual-packages - │ - │ # Go to the new directory - │ cd manual-packages - │ - │ # Clone this repo, naming it "denote" - │ git clone https://git.sr.ht/~protesilaos/denote denote - └──── - - Finally, in your `init.el' (or equivalent) evaluate this: - - ┌──── - │ ;; Make Elisp files in that directory available to the user. - │ (add-to-list 'load-path "~/.emacs.d/manual-packages/denote") - └──── - - Everything is in place to set up the package. - - -17 Sample configuration -═══════════════════════ - - ┌──── - │ (require 'denote) - │ - │ ;; Remember to check the doc strings of those variables. - │ (setq denote-directory (expand-file-name "~/Documents/notes/")) - │ (setq denote-save-buffer-after-creation nil) - │ (setq denote-known-keywords '("emacs" "philosophy" "politics" "economics")) - │ (setq denote-infer-keywords t) - │ (setq denote-sort-keywords t) - │ (setq denote-file-type nil) ; Org is the default, set others here - │ (setq denote-prompts '(title keywords)) - │ (setq denote-excluded-directories-regexp nil) - │ (setq denote-excluded-keywords-regexp nil) - │ (setq denote-rename-no-confirm nil) ; Set to t if you are familiar with `denote-rename-file' - │ - │ ;; Pick dates, where relevant, with Org's advanced interface: - │ (setq denote-date-prompt-use-org-read-date t) - │ - │ - │ ;; Read this manual for how to specify `denote-templates'. We do not - │ ;; include an example here to avoid potential confusion. - │ - │ - │ (setq denote-date-format nil) ; read doc string - │ - │ ;; By default, we do not show the context of links. We just display - │ ;; file names. This provides a more informative view. - │ (setq denote-backlinks-show-context t) - │ - │ ;; Also see `denote-link-backlinks-display-buffer-action' which is a bit - │ ;; advanced. - │ - │ ;; If you use Markdown or plain text files (Org renders links as buttons - │ ;; right away) - │ (add-hook 'find-file-hook #'denote-link-buttonize-buffer) - │ - │ ;; We use different ways to specify a path for demo purposes. - │ (setq denote-dired-directories - │ (list denote-directory - │ (thread-last denote-directory (expand-file-name "attachments")) - │ (expand-file-name "~/Documents/books"))) - │ - │ ;; Generic (great if you rename files Denote-style in lots of places): - │ ;; (add-hook 'dired-mode-hook #'denote-dired-mode) - │ ;; - │ ;; OR if only want it in `denote-dired-directories': - │ (add-hook 'dired-mode-hook #'denote-dired-mode-in-directories) - │ - │ - │ ;; Automatically rename Denote buffers using the `denote-rename-buffer-format'. - │ (denote-rename-buffer-mode 1) - │ - │ ;; Denote DOES NOT define any key bindings. This is for the user to - │ ;; decide. For example: - │ (let ((map global-map)) - │ (define-key map (kbd "C-c n n") #'denote) - │ (define-key map (kbd "C-c n c") #'denote-region) ; "contents" mnemonic - │ (define-key map (kbd "C-c n N") #'denote-type) - │ (define-key map (kbd "C-c n d") #'denote-date) - │ (define-key map (kbd "C-c n z") #'denote-signature) ; "zettelkasten" mnemonic - │ (define-key map (kbd "C-c n s") #'denote-subdirectory) - │ (define-key map (kbd "C-c n t") #'denote-template) - │ ;; If you intend to use Denote with a variety of file types, it is - │ ;; easier to bind the link-related commands to the `global-map', as - │ ;; shown here. Otherwise follow the same pattern for `org-mode-map', - │ ;; `markdown-mode-map', and/or `text-mode-map'. - │ (define-key map (kbd "C-c n i") #'denote-link) ; "insert" mnemonic - │ (define-key map (kbd "C-c n I") #'denote-add-links) - │ (define-key map (kbd "C-c n b") #'denote-backlinks) - │ (define-key map (kbd "C-c n f f") #'denote-find-link) - │ (define-key map (kbd "C-c n f b") #'denote-find-backlink) - │ ;; Note that `denote-rename-file' can work from any context, not just - │ ;; Dired bufffers. That is why we bind it here to the `global-map'. - │ (define-key map (kbd "C-c n r") #'denote-rename-file) - │ (define-key map (kbd "C-c n R") #'denote-rename-file-using-front-matter)) - │ - │ ;; Key bindings specifically for Dired. - │ (let ((map dired-mode-map)) - │ (define-key map (kbd "C-c C-d C-i") #'denote-link-dired-marked-notes) - │ (define-key map (kbd "C-c C-d C-r") #'denote-dired-rename-files) - │ (define-key map (kbd "C-c C-d C-k") #'denote-dired-rename-marked-files-with-keywords) - │ (define-key map (kbd "C-c C-d C-R") #'denote-dired-rename-marked-files-using-front-matter)) - │ - │ (with-eval-after-load 'org-capture - │ (setq denote-org-capture-specifiers "%l\n%i\n%?") - │ (add-to-list 'org-capture-templates - │ '("n" "New note (with denote.el)" plain - │ (file denote-last-path) - │ #'denote-org-capture - │ :no-save t - │ :immediate-finish nil - │ :kill-buffer t - │ :jump-to-captured t))) - │ - │ ;; Also check the commands `denote-link-after-creating', - │ ;; `denote-link-or-create'. You may want to bind them to keys as well. - │ - │ - │ ;; If you want to have Denote commands available via a right click - │ ;; context menu, use the following and then enable - │ ;; `context-menu-mode'. - │ (add-hook 'context-menu-functions #'denote-context-menu) - └──── - - -18 For developers or advanced users -═══════════════════════════════════ - - Denote is in a stable state and can be relied upon as the basis for - custom extensions. Further below is a list with the functions or - variables we provide for public usage. Those are in addition to all - user options and commands that are already documented in the various - sections of this manual. - - In this context “public” is any form with single hyphens in its - symbol, such as `denote-directory-files'. We expressly support those, - meaning that we consider them reliable and commit to documenting any - changes in their particularities (such as through `make-obsolete', a - record in the change log, a blog post on the maintainer’s website, and - the like). - - By contradistinction, a “private” form is declared with two hyphens in - its symbol such as `denote--file-extension'. Do not use those as we - might change them without further notice. - - Variable `denote-id-format' - Format of ID prefix of a note’s filename. The note’s ID is - derived from the date and time of its creation ([The file-naming - scheme]). - - Variable `denote-id-regexp' - Regular expression to match `denote-id-format'. - - Variable `denote-signature-regexp' - Regular expression to match the `SIGNATURE' field in a file - name. - - Variable `denote-title-regexp' - Regular expression to match the `TITLE' field in a file name - ([The file-naming scheme]). - - Variable `denote-keywords-regexp' - Regular expression to match the `KEYWORDS' field in a file name - ([The file-naming scheme]). - - Variable `denote-excluded-punctuation-regexp' - Punctionation that is removed from file names. We consider - those characters illegal for our purposes. - - Variable `denote-excluded-punctuation-extra-regexp' - Additional punctuation that is removed from file names. This - variable is for advanced users who need to extend the - `denote-excluded-punctuation-regexp'. Once we have a better - understanding of what we should be omitting, we will update - things accordingly. - - Function `denote-file-is-note-p' - Return non-nil if `FILE' is an actual Denote note. For our - purposes, a note must not be a directory, must satisfy - `file-regular-p', its path must be part of the variable - `denote-directory', it must have a Denote identifier in its - name, and use one of the extensions implied by - `denote-file-type'. - - Function `denote-file-has-identifier-p' - Return non-nil if `FILE' has a Denote identifier. - - Function `denote-file-has-signature-p' - Return non-nil if `FILE' has a signature. - - Function `denote-file-has-supported-extension-p' - Return non-nil if `FILE' has supported extension. Also account - for the possibility of an added `.gpg' suffix. Supported - extensions are those implied by `denote-file-type'. - - Function `denote-file-is-writable-and-supported-p' - Return non-nil if `FILE' is writable and has supported - extension. - - Function `denote-file-type-extensions' - Return all file type extensions in `denote-file-types'. - - Variable `denote-encryption-file-extensions' - List of strings specifying file extensions for encryption. - - Function `denote-file-type-extensions-with-encryption' - Derive `denote-file-type-extensions' plus - `denote-encryption-file-extensions'. - - Function `denote-get-file-extension' - Return extension of `FILE' with dot included. Account for - `denote-encryption-file-extensions'. In other words, return - something like `.org.gpg' if it is part of the file, else return - `.org'. - - Function `denote-get-file-extension-sans-encryption' - Return extension of `FILE' with dot included and without the - encryption part. Build on top of `denote-get-file-extension' - though always return something like `.org' even if the actual - file extension is `.org.gpg'. - - Function `denote-keywords' - Return appropriate list of keyword candidates. If - `denote-infer-keywords' is non-nil, infer keywords from existing - notes and combine them into a list with `denote-known-keywords'. - Else use only the latter set of keywords ([Standard note - creation]). - - Function `denote-convert-file-name-keywords-to-crm' - Make `STRING' with keywords readable by - `completing-read-multiple'. `STRING' consists of - underscore-separated words, as those appear in the keywords - component of a Denote file name. `STRING' is the same as the - return value of `denote-retrieve-filename-keywords'. - - Function `denote-keywords-sort' - Sort `KEYWORDS' if `denote-sort-keywords' is non-nil. - `KEYWORDS' is a list of strings, per `denote-keywords-prompt'. - - Function `denote-keywords-combine' - Combine `KEYWORDS' list of strings into a single - string. Keywords are separated by the underscore character, per - the Denote file-naming scheme. - - Function `denote-valid-date-p' - Return `DATE' as a valid date. A valid `DATE' is a value that - can be parsed by either `decode-time' or `date-to-time' .Those - functions signal an error if `DATE' is a value they do not - recognise. If `DATE' is nil, return nil. - - Function `denote-parse-date' - Return `DATE' as an appropriate value for the `denote' - command. Pass `DATE' through `denote-valid-date-p' and use its - return value. If either that or `DATE' is nil, return - `current-time'. - - Function `denote-directory' - Return path of the variable `denote-directory' as a proper - directory, also because it accepts a directory-local value for - what we internally refer to as “silos” ([Maintain separate - directories for notes]). Custom Lisp code can `let' bind the - value of the variable `denote-directory' to override what this - function returns. - - Function `denote-directory-files' - Return list of absolute file paths in variable - `denote-directory'. Files only need to have an identifier. The - return value may thus include file types that are not implied by - `denote-file-type'. Remember that the variable - `denote-directory' accepts a dir-local value, as explained in - its doc string ([Maintain separate directories for notes]). With - optional `FILES-MATCHING-REGEXP', restrict files to those - matching the given regular expression. With optional - `OMIT-CURRENT' as a non-nil value, do not include the current - Denote file in the returned list. With optional `TEXT-ONLY' as a - non-nil value, limit the results to text files that satisfy - `denote-file-is-note-p'. - - Function `denote-directory-subdirectories' - Return list of subdirectories in variable - `denote-directory'. Omit dotfiles (such as .git) - unconditionally. Also exclude whatever matches - `denote-excluded-directories-regexp'. Note that the - `denote-directory' accepts a directory-local value for what we - call “silos” ([Maintain separate directories for notes]). - - Function `denote-file-name-relative-to-denote-directory' - Return name of `FILE' relative to the variable - `denote-directory'. `FILE' must be an absolute path. - - Function `denote-get-path-by-id' - Return absolute path of `ID' string in `denote-directory-files'. - - Function `denote-barf-duplicate-id' - Throw a `user-error' if `IDENTIFIER' already exists. - - Function `denote-sluggify' - Make `STR' an appropriate slug for file names and related - ([Sluggification of file name components]). - - Function `denote-sluggify-keyword' - Sluggify `STR' while joining separate words. - - Function `denote-sluggify-signature' - Make `STR' an appropriate slug for signatures ([Sluggification - of file name components]). - - Function `denote-sluggify-keywords' - Sluggify `KEYWORDS', which is a list of strings ([Sluggification - of file name components]). - - Function `denote-filetype-heuristics' - Return likely file type of `FILE'. If in the process of - `org-capture', consider the file type to be that of - Org. Otherwise, use the file extension to detect the file type - of `FILE'. - - If more than one file type correspond to this file extension, - use the first file type for which the :title-key-regexp in - `denote-file-types' matches in the file. - - If no file type in `denote-file-types' has the file extension, - the file type is assumed to be the first one in - `denote-file-types'. - - Function `denote-format-file-name' - Format file name. `DIR-PATH', `ID', `KEYWORDS', `TITLE', - `EXTENSION' and `SIGNATURE' are expected to be supplied by - `denote' or equivalent command. - - `DIR-PATH' is a string pointing to a directory. It ends with a - forward slash (the function `denote-directory' makes sure this - is the case when returning the value of the variable - `denote-directory'). `DIR-PATH' cannot be nil or an empty - string. - - `ID' is a string holding the identifier of the note. It cannot - be nil or an empty string and must match `denote-id-regexp'. - - `DIR-PATH' and `ID' form the base file name. - - `KEYWORDS' is a list of strings that is reduced to a single - string by `denote-keywords-combine'. `KEYWORDS' can be an empty - list or a nil value, in which case the relevant file name - component is not added to the base file name. - - `TITLE' and `SIGNATURE' are strings. They can be an empty - string, in which case their respective file name component is - not added to the base file name. - - `EXTENSION' is a string that contains a dot followed by the file - type extension. It can be an empty string or a nil value, in - which case it is not added to the base file name. - - Function `denote-extract-keywords-from-path' - Extract keywords from `PATH' and return them as a list of - strings. `PATH' must be a Denote-style file name where keywords - are prefixed with an underscore. If `PATH' has no such - keywords, which is possible, return nil ([The file-naming - scheme]). - - Function `denote-extract-id-from-string' - Return existing Denote identifier in `STRING', else nil. - - Function `denote-retrieve-filename-identifier' - Extract identifier from `FILE' name, if present, else return - nil. To create a new one, refer to the - `denote-create-unique-file-identifier' function. - - Function `denote-retrieve-filename-title' - Extract Denote title component from `FILE' name, if present, - else return nil. - - Function `denote-retrieve-filename-keywords' - Extract keywords from `FILE' name, if present, else return - nil. Return matched keywords as a single string. - - Function `denote-retrieve-filename-signature' - Extract signature from `FILE' name, if present, else return nil. - - Function `denote-retrieve-title-or-filename' - Return appropriate title for `FILE' given its `TYPE'. Try to - find the value of the title in the front matter of FILE, - otherwise use its file name. This is a wrapper for - `denote-retrieve-front-matter-title-value' and - `denote-retrieve-filename-title'. - - Function `denote-get-identifier' - Convert `DATE' into a Denote identifier using - `denote-id-format'. `DATE' is parsed by - `denote-valid-date-p'. If `DATE' is nil, use the current time. - - Function `denote-create-unique-file-identifier' - Create a new unique `FILE' identifier. Test that the identifier - is unique among `USED-IDS'. The conditions are as follows: - - • If `DATE' is non-nil, invoke - `denote-prompt-for-date-return-id'. - - • If `DATE' is nil, use the file attributes to determine the - last modified date and format it as an identifier. - - • As a fallback, derive an identifier from the current time. - - With optional `USED-IDS' as nil, test that the identifier is - unique among all files and buffers in variable - `denote-directory'. - - To only return an existing identifier, refer to the function - `denote-retrieve-filename-identifier'. - - Function `denote-retrieve-front-matter-title-value' - Return title value from `FILE' front matter per `FILE-TYPE'. - - Function `denote-retrieve-front-matter-title-line' - Return title line from `FILE' front matter per `FILE-TYPE'. - - Function `denote-retrieve-front-matter-keywords-value' - Return keywords value from `FILE' front matter per - `FILE-TYPE'. The return value is a list of strings. To get a - combined string the way it would appear in a Denote file name, - use `denote-retrieve-front-matter-keywords-value-as-string'. - - Function `denote-retrieve-front-matter-keywords-value-as-string' - Return keywords value from `FILE' front matter per - `FILE-TYPE'. The return value is a string, with the underscrore - as a separator between individual keywords. To get a list of - strings instead, use - `denote-retrieve-front-matter-keywords-value' (the current - function uses that internally). - - Function `denote-retrieve-front-matter-keywords-line' - Return keywords line from `FILE' front matter per `FILE-TYPE'. - - Function `denote-signature-prompt' - Prompt for signature string. With optional `INITIAL-SIGNATURE' - use it as the initial minibuffer text. With optional - `PROMPT-TEXT' use it in the minibuffer instead of the default - prompt. Previous inputs at this prompt are available for - minibuffer completion if the user option - `denote-history-completion-in-prompts' is set to a non-nil value - ([The `denote-history-completion-in-prompts' option]). - - Function `denote-file-prompt' - Prompt for file with identifier in variable - `denote-directory'. With optional `FILES-MATCHING-REGEXP', - filter the candidates per the given regular expression. With - optional `PROMPT-TEXT', use it instead of the default “Select - NOTE”. - - Function `denote-keywords-prompt' - Prompt for one or more keywords. Read entries as separate when - they are demarcated by the `crm-separator', which typically is a - comma. With optional `PROMPT-TEXT', use it to prompt the user - for keywords. Else use a generic prompt. With optional - `INITIAL-KEYWORDS' use them as the initial minibuffer text. - - Function `denote-title-prompt' - Prompt for title string. With optional `INITIAL-TITLE' use it as - the initial minibuffer text. With optional `PROMPT-TEXT' use it - in the minibuffer instead of the default prompt. Previous inputs - at this prompt are available for minibuffer completion if the - user option `denote-history-completion-in-prompts' is set to a - non-nil value ([The `denote-history-completion-in-prompts' - option]). - - Variable `denote-title-prompt-current-default' - Currently bound default title for `denote-title-prompt'. Set - the value of this variable within the lexical scope of a command - that needs to supply a default title before calling - `denote-title-prompt' and use `unwind-protect' to set its value - back to nil. - - Function `denote-file-type-prompt' - Prompt for `denote-file-type'. Note that a non-nil value other - than `text', `markdown-yaml', and `markdown-toml' falls back to - an Org file type. We use `org' here for clarity. - - Function `denote-date-prompt' - Prompt for date, expecting `YYYY-MM-DD' or that plus `HH:MM' (or - even `HH:MM:SS'). Use Org’s more advanced date selection - utility if the user option - `denote-date-prompt-use-org-read-date' is non-nil. It requires - Org ([The denote-date-prompt-use-org-read-date option]). - - Function `denote-command-prompt' - Prompt for command among `denote-commands-for-new-notes' - ([Points of entry]). - - Variable `denote-prompts-with-history-as-completion' - Prompts that conditionally perform completion against their - history. These are minibuffer prompts that ordinarily accept a - free form string input, as opposed to matching against a - predefined set. These prompts can optionally perform completion - against their own minibuffer history when the user option - `denote-history-completion-in-prompts' is set to a non-nil value - ([The `denote-history-completion-in-prompts' option]). - - Function `denote-files-matching-regexp-prompt' - Prompt for `REGEXP' to filter Denote files by. With optional - `PROMPT-TEXT' use it instead of a generic prompt. - - Function `denote-prompt-for-date-return-id' - Use `denote-date-prompt' and return it as `denote-id-format'. - - Function `denote-template-prompt' - Prompt for template key in `denote-templates' and return its - value. - - Function `denote-subdirectory-prompt' - Prompt for subdirectory of the variable `denote-directory'. The - table uses the `file' completion category (so it works with - packages such as `marginalia' and `embark'). - - Function `denote-rename-file-prompt' - Prompt to rename file named `OLD-NAME' to `NEW-NAME'. - - Function `denote-rename-file-and-buffer' - Rename file named `OLD-NAME' to `NEW-NAME', updating buffer - name. - - Function `denote-rewrite-front-matter' - Rewrite front matter of note after `denote-rename-file' (or - related) The `FILE', `TITLE', `KEYWORDS', and `FILE-TYPE' - arguments are given by the renaming command and are used to - construct new front matter values if appropriate. With optional - `NO-CONFIRM', do not prompt to confirm the rewriting of the - front matter. Otherwise produce a `y-or-n-p' prompt to that - effect. With optional `NO-CONFIRM', save the buffer after - performing the rewrite. Otherwise leave it unsaved for furthter - review by the user. - - Function `denote-rewrite-keywords' - Rewrite `KEYWORDS' in `FILE' outright according to - `FILE-TYPE'. Do the same as `denote-rewrite-front-matter' for - keywords, but do not ask for confirmation. With optional - `SAVE-BUFFER', save the buffer corresponding to `FILE'. This - function is for use in the commands `denote-keywords-add', - `denote-keywords-remove', `denote-dired-rename-files', or - related. - - Function `denote-update-dired-buffers' - Update Dired buffers of variable `denote-directory'. Also revert - the current Dired buffer even if it is not inside the - `denote-directory'. Note that the `denote-directory' accepts a - directory-local value for what we internally refer to as “silos” - ([Maintain separate directories for notes]). - - Variable `denote-file-types' - Alist of `denote-file-type' and their format properties. - - Each element is of the form `(SYMBOL PROPERTY-LIST)'. `SYMBOL' - is one of those specified in `denote-file-type' or an arbitrary - symbol that defines a new file type. - - `PROPERTY-LIST' is a plist that consists of the following - elements: - - 1. `:extension' is a string with the file extension including - the period. - - 2. `:date-function' is a function that can format a date. See - the functions `denote--date-iso-8601', - `denote--date-rfc3339', and `denote--date-org-timestamp'. - - 3. `:front-matter' is either a string passed to `format' or a - variable holding such a string. The `format' function - accepts four arguments, which come from `denote' in this - order: `TITLE', `DATE', `KEYWORDS', `IDENTIFIER'. Read the - doc string of `format' on how to reorder arguments. - - 4. `:title-key-regexp' is a regular expression that is used to - retrieve the title line in a file. The first line matching - this regexp is considered the title line. - - 5. `:title-value-function' is the function used to format the - raw title string for inclusion in the front matter (e.g. to - surround it with quotes). Use the `identity' function if no - further processing is required. - - 6. `:title-value-reverse-function' is the function used to - retrieve the raw title string from the front matter. It - performs the reverse of `:title-value-function'. - - 7. `:keywords-key-regexp' is a regular expression used to - retrieve the keywords’ line in the file. The first line - matching this regexp is considered the keywords’ line. - - 8. `:keywords-value-function' is the function used to format the - keywords’ list of strings as a single string, with - appropriate delimiters, for inclusion in the front matter. - - 9. `:keywords-value-reverse-function' is the function used to - retrieve the keywords’ value from the front matter. It - performs the reverse of the `:keywords-value-function'. - - 10. `:link' is a string, or variable holding a string, that - specifies the format of a link. See the variables - `denote-org-link-format', `denote-md-link-format'. - - 11. `:link-in-context-regexp' is a regular expression that is - used to match the aforementioned link format. See the - variables `denote-org-link-in-context-regexp', - `denote-md-link-in-context-regexp'. - - If `denote-file-type' is nil, use the first element of this list - for new note creation. The default is `org'. - - Variable `denote-org-front-matter' - Specifies the Org front matter. It is passed to `format' with - arguments `TITLE', `DATE', `KEYWORDS', `ID' ([Change the front - matter format]) - - Variable `denote-yaml-front-matter' - Specifies the YAML (Markdown) front matter. It is passed to - `format' with arguments `TITLE', `DATE', `KEYWORDS', `ID' - ([Change the front matter format]) - - Variable `denote-toml-front-matter' - Specifies the TOML (Markdown) front matter. It is passed to - `format' with arguments `TITLE', `DATE', `KEYWORDS', `ID' - ([Change the front matter format]) - - Variable `denote-text-front-matter' - Specifies the plain text front matter. It is passed to `format' - with arguments `TITLE', `DATE', `KEYWORDS', `ID' ([Change the - front matter format]) - - Variable `denote-org-link-format' - Format of Org link to note. The value is passed to `format' - with `IDENTIFIER' and `TITLE' arguments, in this order. Also - see `denote-org-link-in-context-regexp'. - - Variable `denote-md-link-format' - Format of Markdown link to note. The `%N$s' notation used in - the default value is for `format' as the supplied arguments are - `IDENTIFIER' and `TITLE', in this order. Also see - `denote-md-link-in-context-regexp'. - - Variable `denote-id-only-link-format' - Format of identifier-only link to note. The value is passed to - `format' with `IDENTIFIER' as its sole argument. Also see - `denote-id-only-link-in-context-regexp'. - - Variable `denote-org-link-in-context-regexp' - Regexp to match an Org link in its context. The format of such - links is `denote-org-link-format'. - - Variable `denote-md-link-in-context-regexp' - Regexp to match an Markdown link in its context. The format of - such links is `denote-md-link-format'. - - Variable `denote-id-only-link-in-context-regexp' - Regexp to match an identifier-only link in its context. The - format of such links is `denote-id-only-link-format'. - - Function `denote-date-org-timestamp' - Format `DATE' using the Org inactive timestamp notation. - - Function `denote-date-rfc3339' - Format `DATE' using the RFC3339 specification. - - Function `denote-date-iso-8601' - Format `DATE' according to ISO 8601 standard. - - Function `denote-trim-whitespace' - Trim whitespace around string `S'. This can be used in - `denote-file-types' to format front mattter. - - Function `denote-trim-whitespace-then-quotes' - Trim whitespace then quotes around string `S'. This can be used - in `denote-file-types' to format front mattter. - - Function `denote-format-string-for-org-front-matter' - Return string `S' as-is for Org or plain text front matter. If - `S' is not a string, return an empty string. - - Function `denote-format-string-for-md-front-matter' - Surround string `S' with quotes. If `S' is not a string, return - a literal emptry string. This can be used in `denote-file-types' - to format front mattter. - - Function `denote-format-keywords-for-md-front-matter' - Format front matter `KEYWORDS' for markdown file type. - `KEYWORDS' is a list of strings. Consult the - `denote-file-types' for how this is used. - - Function `denote-format-keywords-for-text-front-matter' - Format front matter `KEYWORDS' for text file type. `KEYWORDS' - is a list of strings. Consult the `denote-file-types' for how - this is used. - - Function `denote-format-keywords-for-org-front-matter' - Format front matter `KEYWORDS' for org file type. `KEYWORDS' is - a list of strings. Consult the `denote-file-types' for how this - is used. - - Function `denote-extract-keywords-from-front-matter' - Format front matter `KEYWORDS' for org file type. `KEYWORDS' is - a list of strings. Consult the `denote-file-types' for how this - is used. - - Function `denote-link-return-links' - Return list of links in current or optional `FILE'. Also see - `denote-link-return-backlinks'. - - Function `denote-link-return-backlinks' - Return list of backlinks in current or optional `FILE'. Also - see `denote-link-return-links'. - - Variable `denote-link-signature-format' - Format of link description for `denote-link-with-signature'. - - Function `denote-link-description-with-signature-and-title' - Return link description for `FILE'. Produce a description as - follows: - - - If the region is active, use it as the description. - - - If `FILE' as a signature, then use the - `denote-link-signature-format'. By default, this looks like - “signature title”. - - - If `FILE' does not have a signature, then use its title as the - description. - - Variable `denote-link-description-function' - Function to use to create the description of links. The function - specified should take a `FILE' argument and should return the - description as a string. By default, the title of the file is - returned as the description. - - -[The file-naming scheme] See section 5 - -[Standard note creation] See section 3.1 - -[Maintain separate directories for notes] See section 3.7 - -[Sluggification of file name components] See section 5.1 - -[The `denote-history-completion-in-prompts' option] See section 3.1.2 - -[The denote-date-prompt-use-org-read-date option] See section 3.1.6 - -[Points of entry] See section 3 - -[Change the front matter format] See section 6.1 - - -19 Troubleshoot Denote in a pristine environment -════════════════════════════════════════════════ - - Sometimes we get reports on bugs that may not be actually caused by - some error in the Denote code base. To help gain insight into what - the problem is, we need to be able to reproduce the issue in a minimum - viable system. Below is one way to achieve this. - - 1. Find where your `denote.el' file is stored on your filesystem. - - 2. Assuming you have already installed the package, one way to do this - is to invoke `M-x find-library' and search for `denote'. It will - take you to the source file. There do `M-x eval-expression', which - will bring up a minibuffer prompt. At the prompt evaluate: - - ┌──── - │ (kill-new (expand-file-name (buffer-file-name))) - └──── - - 1. The above will save the full file system path to your kill ring. - - 2. In a terminal emulator or an `M-x shell' buffer execute: - - ┌──── - │ emacs -Q - └──── - - 1. This will open a new instance of Emacs in a pristine environment. - Only the default settings are loaded. - - 2. In the `*scratch*' buffer of `emacs -Q', add your configurations - like the following and try to reproduce the issue: - - ┌──── - │ (require 'denote "/full/path/to/what/you/got/denote.el") - │ - │ ;; Your configurations here - └──── - - Then try to see if your problem still occurs. If it does, then the - fault is with Denote. Otherwise there is something external to it - that we need to account for. Whatever the case, this exercise helps - us get a better sense of the specifics. - - -20 Contributing -═══════════════ - - Denote is a GNU ELPA package. As such, any significant change to the - code requires copyright assignment to the Free Software Foundation - (more below). - - You do not need to be a programmer to contribute to this package. - Sharing an idea or describing a workflow is equally helpful, as it - teaches us something we may not know and might be able to cover either - by extending Denote or expanding this manual. If you prefer to write a - blog post, make sure you share it with us: we can add a section herein - referencing all such articles. Everyone gets acknowledged - ([Acknowledgements]). There is no such thing as an “insignificant - contribution”—they all matter. - - ⁃ Package name (GNU ELPA): `denote' - ⁃ Official manual: - ⁃ Change log: - ⁃ Git repositories: - ⁃ GitHub: - ⁃ GitLab: - - If our public media are not suitable, you are welcome to contact me - (Protesilaos) in private: . - - Copyright assignment is a prerequisite to sharing code. It is a simple - process. Check the request form below (please adapt it accordingly). - You must write an email to the address mentioned in the form and then - wait for the FSF to send you a legal agreement. Sign the document and - file it back to them. This could all happen via email and take about a - week. You are encouraged to go through this process. You only need to - do it once. It will allow you to make contributions to Emacs in - general. - - ┌──── - │ Please email the following information to assign@gnu.org, and we - │ will send you the assignment form for your past and future changes. - │ - │ Please use your full legal name (in ASCII characters) as the subject - │ line of the message. - │ - │ REQUEST: SEND FORM FOR PAST AND FUTURE CHANGES - │ - │ [What is the name of the program or package you're contributing to?] - │ - │ GNU Emacs - │ - │ [Did you copy any files or text written by someone else in these changes? - │ Even if that material is free software, we need to know about it.] - │ - │ Copied a few snippets from the same files I edited. Their author, - │ Protesilaos Stavrou, has already assigned copyright to the Free Software - │ Foundation. - │ - │ [Do you have an employer who might have a basis to claim to own - │ your changes? Do you attend a school which might make such a claim?] - │ - │ - │ [For the copyright registration, what country are you a citizen of?] - │ - │ - │ [What year were you born?] - │ - │ - │ [Please write your email address here.] - │ - │ - │ [Please write your postal address here.] - │ - │ - │ - │ - │ - │ [Which files have you changed so far, and which new files have you written - │ so far?] - │ - └──── - - -[Acknowledgements] See section 24 - -20.1 Wishlist of what we can do to extend Denote -──────────────────────────────────────────────── - - These are various ideas to extend Denote. Whether they should be in - the core package or a separate extension is something we can discuss. - I, Protesilaos, am happy to help anyone who wants to do any of this. - - denote-consult.el - This can be a separate package that enhances or replaces the - various prompts we have for files (and maybe more) by using the - `consult' package. Consult provides the preview mechanism and - can probably be used for more things, such as to define a source - for Denote-only buffers in the `consult-buffer' command. If we - need to tweak things in `denote.el', I am happy to do it. For - example, we could have a `denote-file-prompt-function' variable, - which would default to `denote-file-prompt' (what we currently - have) and would also such a hypothetical package to easily plug - into what we have. - - denote-embark.el - Provide integration with the `embark' package. This can be for - doing something with the identifier/link at point. For example, - it could provide an action to produce backlinks for the - identifier/file we are linking to, not just the current one. - - denote-transient.el - The `transient' package is built into Emacs 29 (Denote supports - Emacs 28 though). We can use it to define an alternative to what - we have for the menu bar. Perhaps this interface can used to - toggle various options, such as to call `denote' with a - different set of prompts. - - A `denote-directories' user option - This can be either an extension of the `denote-directory' - (accept a list of file paths value) or a new variable. The idea - is to let the user define separate Denote directories which do - know about the presence of each other (unlike silos). This way, - a user can have an entry in `~/Documents/notes/' link to - something `~/Git/projects/' and everything work as if the - `denote-directory' is set to the `~/' (with the status quo as of - 2024-02-18 08:27 +0200). - - Signatures before identifiers - This is probably going to increase the complixity of `denote.el' - and may not be worth pursuing. But just to explore the idea: we - could have an option to rearrange file names such that the - signature appears before the identifier. If we can do this in a - smart way, we can probably extend the principle for all file - name components. Again though, this may be too complex and not - worth doing. - - Encode the day in the identifier - The idea is to use some coded reference for Monday, Tuesday, - etc. instead of having the generic `T' in the identifier. For - example, Monday is `A' so the identifier for it is something - like `20240219A101522' instead of what we now have as - `20240219T101522'. The old method should still be supported. - Apart from changing a few regular expressions, this does not - seem too complex to me. We would need a user option to opt in to - such a feature. Then tweak the relevant parts. The tricky issue - is to define a mapping of day names to letters/symbols that - works for everyone. Do all countries have a seven-day week, for - example? We need something universally applicable here. - - Anything else? You are welcome to discuss these and/or add to the - list. - - -21 Publications about Denote -════════════════════════════ - - The Emacs community is putting Denote to great use. This section - includes publications that show how people configure their note-taking - setup. If you have a blog post, video, or configuration file about - Denote, feel welcome to tell us about it ([Contributing]). - - ⁃ David Wilson (SystemCrafters): /Generating a Blog Site from Denote - Entries/, 2022-09-09, - - ⁃ David Wilson (SystemCrafters): /Trying Out Prot’s Denote, an Org - Roam Alternative?/, 2022-07-15, - - - ⁃ Jack Baty: /Keeping my Org Agenda updated based on Denote keywords/, - 2022-11-30, - - ⁃ Jeremy Friesen: /Denote Emacs Configuration/, 2022-10-02, - - - ⁃ Jeremy Friesen: /Exploring the Denote Emacs Package/, 2022-10-01, - - - ⁃ Jeremy Friesen: /Migration Plan for Org-Roam Notes to Denote/, - 2022-10-02, - - - ⁃ Jeremy Friesen: /Project Dispatch Menu with Org Mode Metadata, - Denote, and Transient/, 2022-11-19, - - - ⁃ Mohamed Suliman: /Managing a bibliography of BiBTeX entries with - Denote/, 2022-12-20, - - - ⁃ Peter Prevos: /Simulating Text Files with R to Test the Emacs Denote - Package/, 2022-07-28, - - - ⁃ Peter Prevos: /Emacs Writing Studio/, 2023-10-19. A configuration - for authors, using Denote for taking notes, literature reviews and - manage collections of images: - • - • - • - • - - ⁃ Stefan Thesing: /Denote as a Zettelkasten/, 2023-03-02, - - - ⁃ Summer Emacs: /An explanation of how I use Emacs/, 2023-05-04, - - - -[Contributing] See section 20 - - -22 Alternatives to Denote -═════════════════════════ - - What follows is a list of Emacs packages for note-taking. I - (Protesilaos) have not used any of them, as I was manually applying my - file-naming scheme beforehand and by the time those packages were - available I was already hacking on the predecessor of Denote as a - means of learning Emacs Lisp (a package which I called “Unassuming - Sidenotes of Little Significance”, aka “USLS” which is pronounced as - “U-S-L-S” or “useless”). As such, I cannot comment at length on the - differences between Denote and each of those packages, beside what I - gather from their documentation. - - [org-roam] - The de facto standard in the Emacs milieu—and rightly so! It - has a massive community, is featureful, and should be an - excellent companion to anyone who is invested in the Org - ecosystem and/or knows what “Roam” is (I don’t). It has been - explained to me that Org Roam uses a database to store a cache - about your notes. It otherwise uses standard Org files. The - cache helps refer to the same node through aliases which can - provide lots of options. Personally, I follow a - single-topic-per-note approach, so anything beyond that is - overkill. If the database is only for a cache, then maybe that - has no downside, though I am careful with any kind of - specialised program as it creates a dependency. If you ask me - about database software in particular, I have no idea how to use - one, let alone debug it or retrieve data from it if something - goes awry (I could learn, but that is beside the point). - - [zk (or zk.el)] - Reading its documentation makes me think that this is Denote’s - sibling—the two projects have a lot of things in common, - including the preference to rely on plain files and standard - tools. The core difference is that Denote has a strict - file-naming scheme. Other differences in available features - are, in principle, matters of style or circumstance: both - packages can have them. As its initials imply, ZK enables a - zettelkasten-like workflow. It does not enforce it though, - letting the user adapt the method to their needs and - requirements. - - [zettelkasten] - This is another one of Denote’s relatives, at least insofar as - the goal of simplicity is concerned. The major difference is - that according to its documentation “the name of the file that - is created is just a unique ID”. This is not consistent with - our file-naming scheme which is all about making sense of your - files by their name alone and being able to visually parse a - listing of them without any kind of specialised tool (e.g. `ls - -l' or `ls -C' on the command-line from inside the - `denote-directory' give you a human-readable set of files names, - while `find * -maxdepth 0 -type f' is another approach). - - [zetteldeft] - This is a zettelkasten note-taking system built on top of the - `deft' package. Deft provides a search interface to a - directory, in this case the one holding the user’s `zetteldeft' - notes. Denote has no such dependency and is not opinionated - about how the user prefers to search/access their notes: use - Dired, Grep, the `consult' package, or whatever else you already - have set up for all things Emacs, not just your notes. - - Searching through `M-x list-packages' for “zettel” brings up more - matches. `zetteldesk' is an extension to Org Roam and, as such, I - cannot possibly know what Org Roam truly misses and what the - added-value of this package is. `neuron-mode' builds on top of an - external program called `neuron', which I have never used. - - Searching for “note” gives us a few more results. `notes-mode' has - precious little documentation and I cannot tell what it actually does - (as I said in my presentation for LibrePlanet 2022, inadequate docs - are a bug). `side-notes' differs from what we try to do with Denote, - as it basically gives you the means to record your thoughts about some - other project you are working on and keep them on the side: so it and - Denote should not be mutually exclusive. - - If I missed something, please let me know. - - -[org-roam] - -[zk (or zk.el)] - -[zettelkasten] - -[zetteldeft] - -22.1 Alternative implementations and further reading -──────────────────────────────────────────────────── - - This section covers blog posts and implementations from the Emacs - community about the topic of note-taking and file organization. They - may refer to some of the packages covered in the previous section or - provide their custom code ([Alternatives to Denote]). The list is - unsorted. - - ⁃ José Antonio Ortega Ruiz (aka “jao”) explains a note-taking method - that is simple like Denote but differs in other ways. An - interesting approach overall: - . - - ⁃ Jethro Kuan (the main `org-roam' developer) explains their - note-taking techniques: - . Good ideas all - round, regardless of the package/code you choose to use. - - ⁃ Karl Voit’s tools [date2name], [filetags], [appendfilename], and - [move2archive] provide a Python-based implementation to organize - individual files which do not require Emacs. His approach ([blog - post] and his [presentation at GLT18]) has been complemented by - [memacs] to process e.g., the date of creation of photographs, or - the log of a phone call in a format compatible to org. - - [ Development note: help expand this list. ] - - -[Alternatives to Denote] See section 22 - -[date2name] - -[filetags] - -[appendfilename] - -[move2archive] - -[blog post] - -[presentation at GLT18] - -[memacs] - - -23 Frequently Asked Questions -═════════════════════════════ - - I (Protesilaos) answer some questions I have received or might get. - It is assumed that you have read the rest of this manual: I will not - go into the specifics of how Denote works. - - -23.1 Why develop Denote when PACKAGE already exists? -──────────────────────────────────────────────────── - - I wrote Denote because I was using a variant of Denote’s file-naming - scheme before I was even an Emacs user (I switched to Emacs from - Tmux+Vim+CLI in the summer of 2019). I was originally inspired by - Jekyll, the static site generator, which I started using for my - website in 2016 (was on WordPress before). Jekyll’s files follow the - `YYYY-MM-DD-TITLE.md' pattern. I liked its efficiency relative to the - unstructured mess I had before. Eventually, I started using that - scheme outside the confines of my website’s source code. Over time I - refined it and here we are. - - Note-taking is something I take very seriously, as I am a prolific - writer (just check my website, which only reveals the tip of the - iceberg). As such, I need a program that does exactly what I want and - which I know how to extend. I originally tried to use Org capture - templates to create new files with a Denote-style file-naming scheme - but never managed to achieve it. Maybe because `org-capture' has some - hard-coded assumptions or I simply am not competent enough to hack on - core Org facilities. Whatever the case, an alternative was in order. - - The existence of PACKAGE is never a good reason for me not to conduct - my own experiments for recreational, educational, or practical - purposes. When the question arises of “why not contribute to PACKAGE - instead?” the answer is that without me experimenting in the first - place, I would lack the skills for such a task. Furthermore, - contributing to another package does not guarantee I get what I want - in terms of workflow. - - Whether you should use Denote or not is another matter altogether: - choose whatever you want. - - -23.2 Why not rely exclusively on Org? -───────────────────────────────────── - - I think Org is one of Emacs’ killer apps. I also believe it is not - the right tool for every job. When I write notes, I want to focus on - writing. Nothing more. I thus have no need for stuff like org-babel, - scheduling to-do items, clocking time, and so on. The more “mental - dependencies” you add to your workflow, the heavier the burden you - carry and the less focused you are on the task at hand: there is - always that temptation to tweak the markup, tinker with some syntactic - construct, obsess about what ought to be irrelevant to writing as - such. - - In technical terms, I also am not fond of Org’s code base (I - understand why it is the way it is—just commenting on the fact). Ever - tried to read it? You will routinely find functions that are - tens-to-hundreds of lines long and have all sorts of special casing. - As I am not a programmer and only learnt to write Elisp through trial - and error, I have no confidence in my ability to make Org do what I - want at that level, hence `denote' instead of `org-denote' or - something. - - Perhaps the master programmer is one who can deal with complexity and - keep adding to it. I am of the opposite view, as language—code - included—is at its communicative best when it is clear and accessible. - - Make no mistake: I use Org for the agenda and also to write technical - documentation that needs to be exported to various formats, including - this very manual. - - -23.3 Why care about Unix tools when you use Emacs? -────────────────────────────────────────────────── - - My notes form part of my longer-term storage. I do not want to have - to rely on a special program to be able to read them or filter them. - Unix is universal, at least as far as I am concerned. - - Denote streamlines some tasks and makes things easier in general, - which is consistent with how Emacs provides a layer of interactivity - on top of Unix. Still, Denote’s utilities can, in principle, be - implemented as POSIX shell scripts (minus the Emacs-specific parts - like fontification in Dired or the buttonization of links). - - Portability matters. For example, in the future I might own a - smartphone, so I prefer not to require Emacs, Org, or some other - executable to access my files on the go. - - Furthermore, I might want to share those files with someone. If I - make Emacs a requirement, I am limiting my circle to a handful of - relatively advanced users. - - Please don’t misinterpret this: I am using Emacs full-time for my - computing and maintain a growing list of packages for it. This is - just me thinking long-term. - - -23.4 Why many small files instead of few large ones? -──────────────────────────────────────────────────── - - I have read that Org favours the latter method. If true, I strongly - disagree with it because of the implicit dependency it introduces and - the way it favours machine-friendliness over human-readability in - terms of accessing information. Notes are long-term storage. I might - want to access them on (i) some device with limited features, (ii) - print on paper, (iii) share with another person who is not a tech - wizard. - - There are good arguments for few large files, but all either - prioritize machine-friendliness or presuppose the use of sophisticated - tools like Emacs+Org. - - Good luck using `less' on a generic TTY to read a file with a zillion - words, headings, sub-headings, sub-sub-headings, property drawers, and - other constructs! You will not get the otherwise wonderful folding of - headings the way you do in Emacs—do not take such features for - granted. - - My point is that notes should be atomic to help the user—and - potentially the user’s family, friends, acquaintances—make sense of - them in a wide range of scenaria. The more program-agnostic your file - is, the better for you and/or everyone else you might share your - writings with. - - Human-readability means that we optimize for what matters to us. If - (a) you are the only one who will ever read your notes, (b) always - have access to good software like Emacs+Org, (c) do not care about - printing on paper, then Denote’s model is not for you. Maybe you need - to tweak some `org-capture' template to append a new entry to one mega - file (I do that for my Org agenda, by the way, as I explained before - about using the right tool for the job). - - -23.5 Does Denote perform well at scale? -─────────────────────────────────────── - - Denote does not do anything fancy and has no special requirements: it - uses standard tools to accomplish ordinary tasks. If Emacs can cope - with lots of files, then that is all you need to know: Denote will - work. - - To put this to the test, Peter Prevos is running simulations with R - that generate large volumes of notes. You can read the technicalities - here: . - Excerpt: - - Using this code I generated ten thousands notes and used - this to test the Denote package to see it if works at a - large scale. This tests shows that Prot’s approach is - perfectly capable of working with thousands of notes. - - Of course, we are always prepared to make refinements to the code, - where necessary, without compromising on the project’s principles. - - -23.6 I add TODOs to my notes; will many files slow down the Org agenda? -─────────────────────────────────────────────────────────────────────── - - Yes, many files will slow down the agenda due to how that works. Org - collects all files specified in the `org-agenda-files', searches - through their contents for timestamped entries, and then loops through - all days to determine where each entry belongs. The more days and - more files, the longer it takes to build the agenda. Doing this with - potentially hundreds of files will have a noticeable impact on - performance. - - This is not a deficiency of Denote. It happens with generic Org - files. The way the agenda is built is heavily favoring the use of a - single file that holds all your timestamped entries (or at least a few - such files). Tens or hundreds of files are inefficient for this job. - Plus doing so has the side-effect of making Emacs open all those - files, which you probably do not need. - - If you want my opinion though, be more forceful with the separation of - concerns. Decouple your knowledge base from your ephemeral to-do - list: Denote (and others) can be used for the former, while you let - standard Org work splendidly for the latter—that is what I do, anyway. - - Org has a powerful linking facility, whether you use `org-store-link' - or do it via an `org-capture' template. If you want a certain note to - be associated with a task, just store the task in a single `tasks.org' - (or however you name it) and link to the relevant context. - - Do not mix your knowledge base with your to-do items. If you need - help figuring out the specifics of this workflow, you are welcome to - ask for help in our relevant channels ([Contributing]). - - -[Contributing] See section 20 - - -23.7 I want to sort by last modified in Dired, why won’t Denote let me? -─────────────────────────────────────────────────────────────────────── - - Denote does not control how Dired sorts files. I encourage you to read - the manpage of the `ls' executable. It will help you in general, while - it applies to Emacs as well via Dired. The gist is that you can update - the `ls' flags that Dired uses on-the-fly: type `C-u M-x - dired-sort-toggle-or-edit' (`C-u s' by default) and append - `--sort=time' at the prompt. To reverse the order, add the `-r' flag. - The user option `dired-listing-switches' sets your default preference. - - For an on-demand sorted and filtered Dired listing of Denote files, - use the command `denote-sort-dired' ([Sort files by component]). - - -[Sort files by component] See section 12 - - -23.8 How do you handle the last modified case? -────────────────────────────────────────────── - - Denote does not insert any meta data or heading pertaining to edits in - the file. I am of the view that these either do not scale well or are - not descriptive enough. Suppose you use a “lastmod” heading with a - timestamp: which lines where edited and what did the change amount to? - - This is where an external program can be helpful. Use a Version - Control System, such as Git, to keep track of all your notes. Every - time you add a new file, record the addition. Same for post-creation - edits. Your VCS will let you review the history of those changes. - For instance, Emacs’ built-in version control framework has a command - that produces a log of changes for the current file: `M-x - vc-print-log', bound to `C-x v l' by default. From there one can - access the corresponding diff output (use `M-x describe-mode' (`C-h - m') in an unfamiliar buffer to learn more about it). With Git in - particular, Emacs users have the option of the all-round excellent - `magit' package. - - In short: let Denote (or equivalent) create notes and link between - them, the file manager organise and provide access to files, search - programs deal with searching and narrowing, and version control - software handle the tracking of changes. - - -23.9 Speed up backlinks’ buffer creation? -───────────────────────────────────────── - - Denote leverages the built-in `xref' library to search for the - identifier of the current file and return any links to it. For users - of Emacs version 28 or higher, there exists a user option to specify - the program that performs this search: `xref-search-program'. The - default is `grep', which can be slow, though one may opt for `ugrep', - `ripgrep', or even specify something else (read the doc string of that - user option for the details). - - Try either for these for better results: - - ┌──── - │ (setq xref-search-program 'ripgrep) - │ - │ ;; OR - │ - │ (setq xref-search-program 'ugrep) - └──── - - To use whatever executable is available on your system, use something - like this: - - ┌──── - │ ;; Prefer ripgrep, then ugrep, and fall back to regular grep. - │ (setq xref-search-program - │ (cond - │ ((or (executable-find "ripgrep") - │ (executable-find "rg")) - │ 'ripgrep) - │ ((executable-find "ugrep") - │ 'ugrep) - │ (t - │ 'grep))) - └──── - - -23.10 Why do I get “Search failed with status 1” when I search for backlinks? -───────────────────────────────────────────────────────────────────────────── - - Denote uses [Emacs’ Xref] to find backlinks. Xref requires `xargs' - and one of `grep' or `ripgrep', depending on your configuration. - - This is usually not an issue on *nix systems, but the necessary - executables are not available on Windows Emacs distributions. Please - ensure that you have both `xargs' and either `grep' or `ripgrep' - available within your `PATH' environment variable. - - If you have `git' on Windows installed, then you may use the following - code (adjust the git’s installation path if necessary): - ┌──── - │ (setenv "PATH" (concat (getenv "PATH") ";" "C:\\Program Files\\Git\\usr\\bin")) - └──── - - -[Emacs’ Xref] - - -23.11 Why do I get a double `#+title' in Doom Emacs? -──────────────────────────────────────────────────── - - Doom Emacs provides a set of bespoke templates for Org. One of those - prefills any new Org file with a `#+title' field. So when Denote - creates a new Org file and inserts front matter to it, it inevitably - adds an extra title to the existing one. - - This is not a Denote problem. We can only expect a new file to be - empty by default. Check how to disable the relevant module in your - Doom Emacs configuration file. - - -24 Acknowledgements -═══════════════════ - - Denote is meant to be a collective effort. Every bit of help matters. - - Author/maintainer - Protesilaos Stavrou. - - Contributions to code or the manual - Abin Simon, Adam Růžička, Alan Schmitt, Ashton Wiersdorf, - Benjamin Kästner, Bruno Boal, Charanjit Singh, Clemens - Radermacher, Colin McLear, Damien Cassou, Eduardo Grajeda, Elias - Storms, Eshel Yaron, Florian, Glenna D., Graham Marlow, Hilde - Rhyne, Ivan Sokolov, Jack Baty, Jean-Charles Bagneris, - Jean-Philippe Gagné Guay, Joseph Turner, Jürgen Hötzel, Kaushal - Modi, Kai von Fintel, Kostas Andreadis, Kristoffer Balintona, - Kyle Meyer, Marc Fargas, Matthew Lemon, Noboru Ota (nobiot), - Norwid Behrnd, Peter Prevos, Philip Kaludercic, Quiliro Ordóñez, - Stephen R. Kifer, Stefan Monnier, Stefan Thesing, Thibaut - Benjamin, Tomasz Hołubowicz, Vedang Manerikar, Wesley Harvey, - Zhenxu Xu, arsaber101, ezchi, jarofromel, leinfink (Henrik), - l-o-l-h (Lincoln), mattyonweb, maxbrieiev, mentalisttraceur, - pmenair, relict007. - - Ideas and/or user feedback - Abin Simon, Aditya Yadav, Alan Schmitt, Aleksandr Vityazev, Alex - Hirschfeld, Alfredo Borrás, Ashton Wiersdorf, Benjamin Kästner, - Claudiu Tănăselia, Colin McLear, Damien Cassou, Elias Storms, - Federico Stilman, Florian, Frédéric Willem Frank Ehmsen, Glenna - D., Guo Yong, Hanspeter Gisler, Jack Baty, Jay Rajput, - Jean-Charles Bagneris, Jens Östlund, Jeremy Friesen, Jonathan - Sahar, Johan Bolmsjö, Jousimies, Juanjo Presa, Kai von Fintel, - Kaushal Modi, M. Hadi Timachi, Mark Olson, Mirko Hernandez, - Niall Dooley, Paul van Gelder, Peter Prevos, Peter Smith, Suhail - Singh, Shreyas Ragavan, Stefan Thesing, Summer Emacs, Sven - Seebeck, Taoufik, TJ Stankus, Vick (VicZz), Viktor Haag, Wade - Mealing, Yi Liu, Ypot, atanasj, babusri, doolio, drcxd, - fingerknight, hpgisler, mentalisttraceur, pRot0ta1p, rbenit68, - relict007, sienic, sundar bp. - - Special thanks to Peter Povinec who helped refine the file-naming - scheme, which is the cornerstone of this project. - - Special thanks to Jean-Philippe Gagné Guay for the numerous - contributions to the code base. - - -25 GNU Free Documentation License -═════════════════════════════════ - - -26 Indices -══════════ - -26.1 Function index -─────────────────── - - -26.2 Variable index -─────────────────── - - -26.3 Concept index -────────────────── blob - 08540f63c17e06cdba24c8ce6c447e6950d89c6e (mode 644) blob + /dev/null --- elpa/denote-2.3.5/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# denote: Simple notes with an efficient file-naming scheme - -Denote is a simple note-taking tool for Emacs. It is based on the idea -that notes should follow a predictable and descriptive file-naming -scheme. The file name must offer a clear indication of what the note is -about, without reference to any other metadata. Denote basically -streamlines the creation of such files while providing facilities to -link between them. - -Denote's file-naming scheme is not limited to "notes". It can be used -for all types of file, including those that are not editable in Emacs, -such as videos. Naming files in a consistent way makes their -filtering and retrieval considerably easier. Denote provides relevant -facilities to rename files, regardless of file type. - -+ Package name (GNU ELPA): `denote` -+ Official manual: -+ Change log: -+ Git repositories: - + GitHub: - + GitLab: -+ Video demo: -+ Backronyms: Denote Everything Neatly; Omit The Excesses. Don't Ever - Note Only The Epiphenomenal. blob - 1093db9c23bf95b3bd2b978705bcc473e957138f (mode 644) blob + /dev/null --- elpa/denote-2.3.5/README.org +++ /dev/null @@ -1,5857 +0,0 @@ -#+title: denote: Simple notes with an efficient file-naming scheme -#+author: Protesilaos Stavrou -#+email: info@protesilaos.com -#+language: en -#+options: ':t toc:nil author:t email:t num:t -#+startup: content -#+macro: stable-version 2.3.0 -#+macro: release-date 2024-03-24 -#+macro: development-version 3.0.0-dev -#+export_file_name: denote.texi -#+texinfo_filename: denote.info -#+texinfo_dir_category: Emacs misc features -#+texinfo_dir_title: Denote: (denote) -#+texinfo_dir_desc: Simple notes with an efficient file-naming scheme -#+texinfo_header: @set MAINTAINERSITE @uref{https://protesilaos.com,maintainer webpage} -#+texinfo_header: @set MAINTAINER Protesilaos Stavrou -#+texinfo_header: @set MAINTAINEREMAIL @email{info@protesilaos.com} -#+texinfo_header: @set MAINTAINERCONTACT @uref{mailto:info@protesilaos.com,contact the maintainer} - -#+texinfo: @insertcopying - -This manual, written by Protesilaos Stavrou, describes the customization -options for the Emacs package called ~denote~ (or =denote.el=), and -provides every other piece of information pertinent to it. - -The documentation furnished herein corresponds to stable version -{{{stable-version}}}, released on {{{release-date}}}. Any reference to -a newer feature which does not yet form part of the latest tagged -commit, is explicitly marked as such. - -Current development target is {{{development-version}}}. - -+ Package name (GNU ELPA): ~denote~ -+ Official manual: -+ Change log: -+ Git repositories: - + GitHub: - + GitLab: -+ Video demo: -+ Backronyms: Denote Everything Neatly; Omit The Excesses. Don't Ever - Note Only The Epiphenomenal. - -If you are viewing the README.org version of this file, please note that -the GNU ELPA machinery automatically generates an Info manual out of it. - -#+toc: headlines 8 insert TOC here, with eight headline levels - -* COPYING -:PROPERTIES: -:COPYING: t -:CUSTOM_ID: h:40b18bb2-4dc1-4202-bd0b-6fab535b2a0f -:END: - -Copyright (C) 2022-2024 Free Software Foundation, Inc. - -#+begin_quote -Permission is granted to copy, distribute and/or modify this document -under the terms of the GNU Free Documentation License, Version 1.3 or -any later version published by the Free Software Foundation; with no -Invariant Sections, with the Front-Cover Texts being “A GNU Manual,” and -with the Back-Cover Texts as in (a) below. A copy of the license is -included in the section entitled “GNU Free Documentation License.” - -(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and -modify this GNU manual.” -#+end_quote - -* Overview -:PROPERTIES: -:CUSTOM_ID: h:a09b70a2-ae0b-4855-ac14-1dddfc8e3241 -:END: - -Denote aims to be a simple-to-use, focused-in-scope, and effective -note-taking and file-naming tool for Emacs. - -Denote is based on the idea that files should follow a predictable and -descriptive file-naming scheme. The file name must offer a clear -indication of what the contents are about, without reference to any -other metadata. Denote basically streamlines the creation of such -files or file names while providing facilities to link between them -(where those files are editable). - -Denote's file-naming scheme is not limited to "notes". It can be used -for all types of file, including those that are not editable in Emacs, -such as videos. Naming files in a consistent way makes their -filtering and retrieval considerably easier. Denote provides relevant -facilities to rename files, regardless of file type. - -Denote is based on the following core design principles: - -+ Predictability :: File names must follow a consistent and descriptive - naming convention ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). The file name alone - should offer a clear indication of what the contents are, without - reference to any other metadatum. This convention is not specific to - note-taking, as it is pertinent to any form of file that is part of - the user's long-term storage ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]). - -+ Composability :: Be a good Emacs citizen, by integrating with other - packages or built-in functionality instead of re-inventing functions - such as for filtering or greping. The author of Denote (Protesilaos, - aka "Prot") writes ordinary notes in plain text (=.txt=), switching on - demand to an Org file only when its expanded set of functionality is - required for the task at hand ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). - -+ Portability :: Notes are plain text and should remain portable. The - way Denote writes file names, the front matter it includes in the - note's header, and the links it establishes must all be adequately - usable with standard Unix tools. No need for a database or some - specialised software. As Denote develops and this manual is fully - fleshed out, there will be concrete examples on how to do the - Denote-equivalent on the command-line. - -+ Flexibility :: Do not assume the user's preference for a note-taking - methodology. Denote is conceptually similar to the Zettelkasten - Method, which you can learn more about in this detailed introduction: - . Notes are atomic (one file - per note) and have a unique identifier. However, Denote does not - enforce a particular methodology for knowledge management, such as a - restricted vocabulary or mutually exclusive sets of keywords. Denote - also does not check if the user writes thematically atomic notes. It - is up to the user to apply the requisite rigor and/or creativity in - pursuit of their preferred workflow ([[#h:6060a7e6-f179-4d42-a9de-a9968aaebecc][Writing metanotes]]). - -+ Hackability :: Denote's code base consists of small and reusable - functions. They all have documentation strings. The idea is to make - it easier for users of varying levels of expertise to understand what - is going on and make surgical interventions where necessary (e.g. to - tweak some formatting). In this manual, we provide concrete examples - on such user-level configurations ([[#h:4a6d92dd-19eb-4fcc-a7b5-05ce04da3a92][Keep a journal or diary]]). - -Now the important part... "Denote" is the familiar word, though it also -is a play on the "note" concept. Plus, we can come up with acronyms, -recursive or otherwise, of increasingly dubious utility like: - -+ Don't Ever Note Only The Epiphenomenal -+ Denote Everything Neatly; Omit The Excesses - -But we'll let you get back to work. Don't Eschew or Neglect your -Obligations, Tasks, and Engagements. - -* Points of entry -:PROPERTIES: -:CUSTOM_ID: h:17896c8c-d97a-4faa-abf6-31df99746ca6 -:END: - -#+findex: denote -#+findex: denote-type -#+findex: denote-org-capture -#+findex: denote-date -#+findex: denote-subdirectory -#+findex: denote-template -#+findex: denote-signature -There are five main ways to write a note with Denote: invoke the -~denote~, ~denote-type~, ~denote-date~, ~denote-subdirectory~, -~denote-template~, ~denote-signature~ commands, or leverage the -~org-capture-templates~ by setting up a template which calls the -function ~denote-org-capture~. We explain all of those in the -subsequent sections. Other more specialised commands exist as well, -which one shall learn about as they read through this manual. We do -not want to overwhelm the user with options at this stage. - -** Standard note creation -:PROPERTIES: -:CUSTOM_ID: h:6a92a8b5-d766-42cc-8e5b-8dc255466a23 -:END: - -The ~denote~ command will prompt for a title. If a region is active, -the text of the region becomes the default at the minibuffer prompt -(meaning that typing =RET= without any input will use the default -value). Once the title is supplied, the ~denote~ command will then ask -for keywords. The resulting note will have a file name as already -explained: [[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file naming scheme]] - -#+vindex: denote-after-new-note-hook -The ~denote~ command runs the hook ~denote-after-new-note-hook~ after -creating the new note. - -The file type of the new note is determined by the user option -~denote-file-type~ ([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]). - -#+vindex: denote-known-keywords -#+vindex: denote-infer-keywords -The keywords' prompt supports minibuffer completion. Available -candidates are those defined in the user option ~denote-known-keywords~. -More candidates can be inferred from the names of existing notes, by -setting ~denote-infer-keywords~ to non-nil (which is the case by -default). - -#+vindex: denote-sort-keywords -Multiple keywords can be inserted by separating them with a comma (or -whatever the value of the ~crm-separator~ is---which should be a comma). -When the user option ~denote-sort-keywords~ is non-nil (the default), -keywords are sorted alphabetically (technically, the sorting is done -with ~string-lessp~). - -The interactive behaviour of the ~denote~ command is influenced by the -user option ~denote-prompts~ ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]]). - -The ~denote~ command can also be called from Lisp. Read its doc string -for the technicalities. - -#+findex: denote-create-note -In the interest of discoverability, ~denote~ is also available under the -alias ~denote-create-note~. - -*** The ~denote-prompts~ option -:PROPERTIES: -:CUSTOM_ID: h:f9204f1f-fcee-49b1-8081-16a08a338099 -:END: - -#+vindex: denote-prompts -The user option ~denote-prompts~ determines how the ~denote~ command -will behave interactively ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). - -Commands that prompt for user input to construct a Denote file name -include, but are not limited to: ~denote~, ~denote-signature~, -~denote-type~, ~denote-date~, ~denote-subdirectory~, -~denote-rename-file~, ~denote-dired-rename-files~. - -- [[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]]. -- [[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]. - -The value of this user option is a list of symbols, which includes any -of the following: - -- =title=: Prompt for the title of the new note ([[#h:403422a7-7578-494b-8f33-813874c12da3][The ~denote-history-completion-in-prompts~ option]]). - -- =keywords=: Prompts with completion for the keywords of the new note. - Available candidates are those specified in the user option - ~denote-known-keywords~. If the user option ~denote-infer-keywords~ - is non-nil, keywords in existing note file names are included in the - list of candidates. The =keywords= prompt uses - ~completing-read-multiple~, meaning that it can accept multiple - keywords separated by a comma (or whatever the value of ~crm-separator~ - is). - -- =file-type=: Prompts with completion for the file type of the new - note. Available candidates are those specified in the user option - ~denote-file-type~. Without this prompt, ~denote~ uses the value of - ~denote-file-type~. - -- =subdirectory=: Prompts with completion for a subdirectory in which to - create the note. Available candidates are the value of the user - option ~denote-directory~ and all of its subdirectories. Any - subdirectory must already exist: Denote will not create it. - -- =date=: Prompts for the date of the new note. It will expect an input - like 2022-06-16 or a date plus time: 2022-06-16 14:30. Without the - =date= prompt, the ~denote~ command uses the ~current-time~. - - [[#h:e7ef08d6-af1b-4ab3-bb00-494a653e6d63][The denote-date-prompt-use-org-read-date option]]. - -- =template=: Prompts for a KEY among the ~denote-templates~. The value - of that KEY is used to populate the new note with content, which is - added after the front matter ([[#h:f635a490-d29e-4608-9372-7bd13b34d56c][The denote-templates option]]). - -- =signature=: - Prompts for an arbitrary string that can be used to - establish a sequential relationship between files (e.g. 1, 1a, 1b, - 1b1, 1b2, ...). Signatures have no strictly defined function and - are up to the user to apply as they see fit. One use-case is to - implement Niklas Luhmann's Zettelkasten system for a sequence of - notes (Folgezettel). Signatures are not included in a file's front - matter. They are reserved solely for creating a sequence in a file - listing, at least for the time being. To insert a link that - includes the signature, use the command ~denote-link-with-signature~ - ([[#h:066e5221-9844-474b-8858-398398646f86][Insert link to file with signature]]). - -The prompts occur in the given order. - -If the value of this user option is nil, no prompts are used. The -resulting file name will consist of an identifier (i.e. the date and -time) and a supported file type extension (per ~denote-file-type~). - -Recall that Denote's standard file-naming scheme is defined as follows -([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]): - -: DATE--TITLE__KEYWORDS.EXT - -If either or both of the =title= and =keywords= prompts are not -included in the value of this variable, file names will be any of -those permutations: - -: DATE.EXT -: DATE--TITLE.EXT -: DATE__KEYWORDS.EXT - -When in doubt, always include the =title= and =keywords= prompts. - -Finally, this user option only affects the interactive use of the -~denote~ or other relevant commands (advanced users can call it from -Lisp). In Lisp usage, the behaviour is always what the caller -specifies, based on the supplied arguments. - -*** The ~denote-history-completion-in-prompts~ option -:PROPERTIES: -:CUSTOM_ID: h:403422a7-7578-494b-8f33-813874c12da3 -:END: - -#+vindex: denote-history-completion-in-prompts -The user option ~denote-history-completion-in-prompts~ toggles history -completion in all ~denote-prompts-with-history-as-completion~. - -When this user option is set to a non-nil value, Denote will use -minibuffer history entries as completion candidates in all of the -~denote-prompts-with-history-as-completion~. Those will show previous -inputs from their respective history as possible values to select, -either to (i) re-insert them verbatim or (ii) with the intent to edit -them further (depending on the minibuffer user interface, one can -select a candidate with =TAB= without exiting the minibuffer, as -opposed to what =RET= normally does by selecting and exiting). - -When this user option is set to a nil value, all of the -~denote-prompts-with-history-as-completion~ will not use minibuffer -completion: they will just prompt for a string of characters. Their -history is still available through all the standard ways of retrieving -minibuffer history, such as with the command ~previous-history-element~. - -History completion still allows arbitrary values to be provided as -input: they do not have to match the available minibuffer completion -candidates. - -Note that some prompts, like ~denote-keywords-prompt~, always use -minibuffer completion, due to the specifics of their data. - -[ Consider enabling the built-in ~savehist-mode~ to persist minibuffer - histories between sessions.] - -*** The ~denote-templates~ option -:PROPERTIES: -:CUSTOM_ID: h:f635a490-d29e-4608-9372-7bd13b34d56c -:END: - -#+vindex: denote-templates -The user option ~denote-templates~ is an alist of content templates for -new notes. A template is arbitrary text that Denote will add to a newly -created note right below the front matter. - -Templates are expressed as a =(KEY . STRING)= association. - -- The =KEY= is the name which identifies the template. It is an - arbitrary symbol, such as =report=, =memo=, =statement=. - -- The =STRING= is ordinary text that Denote will insert as-is. It can - contain newline characters to add spacing. Below we show some - concrete examples. - -The user can choose a template either by invoking the command -~denote-template~ or by changing the user option ~denote-prompts~ to -always prompt for a template when calling the ~denote~ command. - -[[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]]. - -[[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]]. - -Templates can be written directly as one large string. For example (the -=\n= character is read as a newline): - -#+begin_src emacs-lisp -(setq denote-templates - '((report . "* Some heading\n\n* Another heading") - (memo . "* Some heading - -,* Another heading - -"))) -#+end_src - -Long strings may be easier to type but interpret indentation literally. -Also, they do not scale well. A better way is to use some Elisp code to -construct the string. This would typically be the ~concat~ function, -which joins multiple strings into one. The following is the same as the -previous example: - -#+begin_src emacs-lisp -(setq denote-templates - `((report . "* Some heading\n\n* Another heading") - (memo . ,(concat "* Some heading" - "\n\n" - "* Another heading" - "\n\n")))) -#+end_src - -Notice that to evaluate a function inside of an alist we use the -backtick to quote the alist (NOT the straight quote) and then prepend a -comma to the expression that should be evaluated. The ~concat~ form -here is not sensitive to indentation, so it is easier to adjust for -legibility. - -DEV NOTE: We do not provide more examples at this point, though feel -welcome to ask for help if the information provided herein is not -sufficient. We shall expand the manual accordingly. - -*** Convenience commands for note creation -:PROPERTIES: -:CUSTOM_ID: h:887bdced-9686-4e80-906f-789e407f2e8f -:END: - -Sometimes the user needs to create a note that has different -requirements from those of ~denote~ ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). While -this can be achieved globally by changing the ~denote-prompts~ user -option, there are cases where an ad-hoc method is the appropriate one -([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]]). - -To this end, Denote provides the following interactive convenience -commands for note creation. They all work by appending a new prompt to -the existing ~denote-prompts~. - -+ Create note by specifying file type :: The ~denote-type~ command - creates a note while prompting for a file type. - - This is the equivalent of calling ~denote~ when ~denote-prompts~ has - the =file-type= prompt appended to its existing prompts. In practical - terms, this lets you produce, say, a note in Markdown even though - you normally write in Org ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). - - #+findex: denote-create-note-using-type - The ~denote-create-note-using-type~ is an alias of ~denote-type~. - -+ Create note using a date :: Normally, Denote reads the current date - and time to construct the unique identifier of a newly created note - ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). Sometimes, however, the user needs to set - an explicit date+time value. - - This is where the ~denote-date~ command comes in. It creates a note - while prompting for a date. The date can be in YEAR-MONTH-DAY - notation like =2022-06-30= or that plus the time: =2022-06-16 14:30=. - - [[#h:e7ef08d6-af1b-4ab3-bb00-494a653e6d63][The denote-date-prompt-use-org-read-date option]]. - - This is the equivalent of calling ~denote~ when ~denote-prompts~ has - the =date= prompt appended to its existing prompts. - - #+findex: denote-create-note-using-date - The ~denote-create-note-using-date~ is an alias of ~denote-date~. - -+ Create note in a specific directory :: The ~denote-subdirectory~ - command creates a note while prompting for a subdirectory. Available - candidates include the value of the variable ~denote-directory~ and - any subdirectory thereof (Denote does not create subdirectories). - - This is the equivalent of calling ~denote~ when ~denote-prompts~ has - the =subdirectory= prompt appended to its existing prompts. - - #+findex: denote-create-note-in-subdirectory - The ~denote-create-note-in-subdirectory~ is a more descriptive alias - of ~denote-subdirectory~. - -+ Create note and add a template :: The ~denote-template~ command - creates a new note and inserts the specified template below the front - matter ([[#h:f635a490-d29e-4608-9372-7bd13b34d56c][The denote-templates option]]). Available candidates for - templates are specified in the user option ~denote-templates~. - - This is the equivalent of calling ~denote~ when ~denote-prompts~ has - the =template= prompt appended to its existing prompts. - - #+findex: denote-create-note-with-template - The ~denote-create-note-with-template~ is an alias of the command - ~denote-template~, meant to help with discoverability. - -+ Create note with a signature :: The ~denote-signature~ command first - prompts for an arbitrary string to use in the optional =SIGNATURE= - field of the file name and then asks for a title and keywords. - Signatures are arbitrary strings of alphanumeric characters which - can be used to establish sequential relations between file at the - level of their file name (e.g. 1, 1a, 1b, 1b1, 1b2, ...). - - This is the equivalent of calling ~denote~ when ~denote-prompts~ has - the =signature= prompt appended to its existing prompts. - - The ~denote-create-note-using-signature~ is an alias of the command - ~denote-signature~ intended to make the functionality more - discoverable. - -**** Write your own convenience commands -:PROPERTIES: -:CUSTOM_ID: h:11946562-7eb0-4925-a3b5-92d75f1f5895 -:END: - -The convenience commands we provide only cover some basic use-cases -([[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]]). The user may require -combinations that are not covered, such as to prompt for a template and -for a subdirectory, instead of only one of the two. To this end, we -show how to follow the code we use in Denote to write your own variants -of those commands. - -First let's take a look at the definition of one of those commands. -They all look the same, but we use ~denote-subdirectory~ for this -example: - -#+begin_src emacs-lisp -(defun denote-subdirectory () - "Create note while prompting for a subdirectory. - -Available candidates include the value of the variable -`denote-directory' and any subdirectory thereof. - -This is equivalent to calling `denote' when `denote-prompts' is -set to '(subdirectory title keywords)." - (declare (interactive-only t)) - (interactive) - (let ((denote-prompts '(subdirectory title keywords))) - (call-interactively #'denote))) -#+end_src - -The hyphenated word after ~defun~ is the name of the function. It has -to be unique. Then we have the documentation string (or "doc string") -which is for the user's convenience. - -This function is ~interactive~, meaning that it can be called via =M-x= -or be assigned to a key binding. Then we have the local binding of the -~denote-prompts~ to the desired combination ("local" means specific to -this function without affecting other contexts). Lastly, it calls the -standard ~denote~ command interactively, so it uses all the prompts in -their specified order. - -Now let's say we want to have a command that (i) asks for a template and -(ii) for a subdirectory ([[#h:f635a490-d29e-4608-9372-7bd13b34d56c][The denote-templates option]]). All we need to -do is tweak the ~let~ bound value of ~denote-prompts~ and give our -command a unique name: - -#+begin_src emacs-lisp -;; Like `denote-subdirectory' but also ask for a template -(defun denote-subdirectory-with-template () - "Create note while also prompting for a template and subdirectory. - -This is equivalent to calling `denote' when `denote-prompts' is -set to '(template subdirectory title keywords)." - (declare (interactive-only t)) - (interactive) - (let ((denote-prompts '(template subdirectory title keywords))) - (call-interactively #'denote))) -#+end_src - -The tweaks to ~denote-prompts~ determine how the command will behave -([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]]). Use this paradigm to write your own -variants which you can then assign to keys, invoke with =M-x=, or add -to the list of commands available at the ~denote-command-prompt~ -([[#h:98c732ac-da0e-4ebd-a0e3-5c47f9075e51][Choose which commands to prompt for]]). - -*** The ~denote-save-buffer-after-creation~ option -:PROPERTIES: -:CUSTOM_ID: h:bf80f4cd-6f56-4f7c-a991-8573161e4511 -:END: - -#+vindex: denote-save-buffer-after-creation -The user option ~denote-save-buffer-after-creation~ controls whether -commands that creeate new notes save their buffer outright. - -The default behaviour of commands such as ~denote~ (or related) is to -not save the buffer they create ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). This gives the user -the chance to review the text before writing it to a file. The user -may choose to delete the unsaved buffer, thus not creating a new note -([[#h:bf80f4cd-6f56-4f7c-a991-8573161e4511][The ~denote-save-buffer-after-creation~ option]]). - -If ~denote-save-buffer-after-creation~ is set to a non-nil value, such -buffers are saved automatically. - -*** The ~denote-date-prompt-use-org-read-date~ option -:PROPERTIES: -:CUSTOM_ID: h:e7ef08d6-af1b-4ab3-bb00-494a653e6d63 -:END: - -By default, Denote uses its own simple prompt for date or date+time -input ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]]). This is done when the -~denote-prompts~ option includes a =date= symbol and/or when the user -invokes the ~denote-date~ command. - -#+vindex: denote-date-prompt-use-org-read-date -Users who want to benefit from the more advanced date selection method -that is common in interactions with Org mode, can set the user option -~denote-date-prompt-use-org-read-date~ to a non-nil value. - -** Create a note from the current Org subtree -:PROPERTIES: -:CUSTOM_ID: h:d0c7cb79-21e5-4176-a6af-f4f68578c8dd -:END: - -In Org parlance, an entry with all its subheadings and other contents -is a "subtree". Denote can operate on the subtree to extract it from -the current file and create a new file out of it. One such workflow is -to collect thoughts in a single document and produce longer standalone -notes out of them upon review. - -#+findex: denote-org-extras-extract-org-subtree -The command ~denote-org-extras-extract-org-subtree~ (part of the -optional =denote-org-extras.el= extension) is used for this purpose. -It creates a new Denote note using the current Org subtree. In doing -so, it removes the subtree from its current file and moves its -contents into a new file. - -The text of the subtree's heading becomes the =#+title= of the new -note. Everything else is inserted as-is. - -If the heading has any tags, they are used as the keywords of the new -note. If the Org file has any =#+filetags= they are taken as well -(Org's =#+filetags= are inherited by the headings). If none of these -are true and the user option ~denote-prompts~ includes an entry for -keywords, then ~denote-org-extras-extract-org-subtree~ prompts for -keywords. Else the new note has no keywords ([[#h:ad4dde4a-8e88-470a-97ae-e7b9d4b41fb4][Add or remove keywords interactively]]). - -If the heading has a =PROPERTIES= drawer, it is retained for further -review. - -If the heading's =PROPERTIES= drawer includes a =DATE= or =CREATED= -property, or there exists a =CLOSED= statement with a timestamp value, -use that to derive the date (or date and time) of the new note (if -there is only a date, the time is taken as 00:00). If more than one of -these is present, the order of preference is =DATE=, then =CREATED=, -then =CLOSED=. If none of these is present, the current time is used. -If the ~denote-prompts~ includes an entry for a date, then the command -prompts for a date at this stage (also see ~denote-date-prompt-use-org-read-date~). - -For the rest, it consults the value of the user option -~denote-prompts~ in the following scenaria: - -- To optionally prompt for a subdirectory, otherwise it produces the - new note in the ~denote-directory~. -- To optionally prompt for a file signature, otherwise to not use any. - -The new note is an Org file regardless of the user option -~denote-file-type~. - -** Create note using Org capture -:PROPERTIES: -:CUSTOM_ID: h:656c70cd-cf9a-4471-a0b5-4f0aaf60f881 -:END: - -For integration with ~org-capture~, the user must first add the relevant -template. Such as: - -#+begin_src emacs-lisp -(with-eval-after-load 'org-capture - (add-to-list 'org-capture-templates - '("n" "New note (with Denote)" plain - (file denote-last-path) - #'denote-org-capture - :no-save t - :immediate-finish nil - :kill-buffer t - :jump-to-captured t))) -#+end_src - -[ In the future, we might develop Denote in ways which do not require such - manual intervention. More user feedback is required to identify the - relevant workflows. ] - -Once the template is added, it is accessed from the specified key. If, -for instance, ~org-capture~ is bound to =C-c c=, then the note creation -is initiated with =C-c c n=, per the above snippet. After that, the -process is the same as with invoking ~denote~ directly, namely: a prompt -for a title followed by a prompt for keywords ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). - -#+vindex: denote-org-capture-specifiers -Users may prefer to leverage ~org-capture~ in order to extend file -creation with the specifiers described in the ~org-capture-templates~ -documentation (such as to capture the active region and/or create a -hyperlink pointing to the given context). - -IMPORTANT. Due to the particular file-naming scheme of Denote, which is -derived dynamically, such specifiers or other arbitrary text cannot be -written directly in the template. Instead, they have to be assigned to -the user option ~denote-org-capture-specifiers~, which is interpreted by -the function ~denote-org-capture~. Example with our default value: - -#+begin_src emacs-lisp -(setq denote-org-capture-specifiers "%l\n%i\n%?") -#+end_src - -Note that ~denote-org-capture~ ignores the ~denote-file-type~: it always -sets the Org file extension for the created note to ensure that the -capture process works as intended, especially for the desired output of -the ~denote-org-capture-specifiers~. - -** Create note with specific prompts using Org capture -:PROPERTIES: -:CUSTOM_ID: h:95b78582-9086-47e8-967f-62373e2369a0 -:END: - -This section assumes knowledge of how Denote+org-capture work, as -explained in the previous section ([[#h:656c70cd-cf9a-4471-a0b5-4f0aaf60f881][Create note using Org capture]]). - -#+findex: denote-org-capture-with-prompts -The previous section shows how to define an Org capture template that -always prompts for a title and keywords. There are, however, cases -where the user wants more control over what kind of input Denote will -prompt for. To this end, we provide the function -~denote-org-capture-with-prompts~. Below we explain it and then show -some examples of how to use it. - -The ~denote-org-capture-with-prompts~ is like ~denote-org-capture~ but -with optional prompt parameters. - -When called without arguments, it does not prompt for anything. It -just returns the front matter with title and keyword fields empty and -the date and identifier fields specified. It also makes the file name -consist of only the identifier plus the Org file name extension. - -[[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]. - -Otherwise, it produces a minibuffer prompt for every non-nil value -that corresponds to the =TITLE=, =KEYWORDS=, =SUBDIRECTORY=, =DATE=, -and =TEMPLATE= arguments. The prompts are those used by the standard -~denote~ command and all of its utility commands. - -[[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]. - -When returning the contents that fill in the Org capture template, the -sequence is as follows: front matter, =TEMPLATE=, and then the value -of the user option ~denote-org-capture-specifiers~. - -Important note: in the case of =SUBDIRECTORY= actual subdirectories -must exist---Denote does not create them. Same principle for -=TEMPLATE= as templates must exist and are specified in the user -option ~denote-templates~. - -This is how one can incorporate ~denote-org-capture-with-prompts~ in -their Org capture templates. Instead of passing a generic ~t~ which -makes it hard to remember what the argument means, we use semantic -keywords like =:title= for our convenience (internally this does not -matter as the value still counts as non-nil, so =:foo= for =TITLE= is -treated the same as =:title= or ~t~). - -#+begin_src emacs-lisp -;; This prompts for TITLE, KEYWORDS, and SUBDIRECTORY -(add-to-list 'org-capture-templates - '("N" "New note with prompts (with denote.el)" plain - (file denote-last-path) - (function - (lambda () - (denote-org-capture-with-prompts :title :keywords :subdirectory))) - :no-save t - :immediate-finish nil - :kill-buffer t - :jump-to-captured t)) - -;; This prompts only for SUBDIRECTORY -(add-to-list 'org-capture-templates - '("N" "New note with prompts (with denote.el)" plain - (file denote-last-path) - (function - (lambda () - (denote-org-capture-with-prompts nil nil :subdirectory))) - :no-save t - :immediate-finish nil - :kill-buffer t - :jump-to-captured t)) - -;; This prompts for TITLE and SUBDIRECTORY -(add-to-list 'org-capture-templates - '("N" "New note with prompts (with denote.el)" plain - (file denote-last-path) - (function - (lambda () - (denote-org-capture-with-prompts :title nil :subdirectory))) - :no-save t - :immediate-finish nil - :kill-buffer t - :jump-to-captured t)) -#+end_src - -** Create a note with the region's contents -:PROPERTIES: -:CUSTOM_ID: h:2f8090f1-50af-4965-9771-d5a91a0a87bd -:END: - -#+findex: denote-region -The command ~denote-region~ takes the contents of the active region -and then calls the ~denote~ command. Once a new note is created, it -inserts the contents of the region therein. This is useful to -quickly elaborate on some snippet of text or capture it for future -reference. - -#+vindex: denote-region-after-new-note-functions -When the ~denote-region~ command is called with an active region, it -finalises its work by calling ~denote-region-after-new-note-functions~. -This is an abnormal hook, meaning that the functions added to it are -called with arguments. The arguments are two, representing the -beginning and end positions of the newly inserted text. - -A common use-case for Org mode users is to call the command -~org-insert-structure-template~ after a region is inserted. Emacs -will thus prompt for a structure template, such as the one -corresponding to a source block. In this case the function added to -~denote-region-after-new-note-functions~ does not actually need -aforementioned arguments: it can simply declare those as ignored by -prefixing the argument names with an underscore (an underscore is -enough, but it is better to include a name for clarity). For example, -the following will prompt for a structure template as soon as -~denote-region~ is done: - -#+begin_src emacs-lisp -(defun my-denote-region-org-structure-template (_beg _end) - (when (derived-mode-p 'org-mode) - (activate-mark) - (call-interactively 'org-insert-structure-template))) - -(add-hook 'denote-region-after-new-note-functions #'my-denote-region-org-structure-template) -#+end_src - -Remember that ~denote-region-after-new-note-functions~ are not called -if ~denote-region~ is used without an active region. - -** Open an existing note or create it if missing -:PROPERTIES: -:CUSTOM_ID: h:ad91ca39-cf10-4e16-b224-fdf78f093883 -:END: - -#+findex: denote-open-or-create -#+findex: denote-open-or-create-with-command -Sometimes it is necessary to briefly interrupt the ongoing writing -session to open an existing note or, if that is missing, to create it. -This happens when a new tangential thought occurs and the user wants -to confirm that an entry for it is in place. To this end, Denote -provides the command ~denote-open-or-create~ as well as its more -flexible counterpart ~denote-open-or-create-with-command~. - -The ~denote-open-or-create~ prompts to visit a file in the -~denote-directory~. At this point, the user must type in search terms -that match a file name. If the input does not return any matches and -the user confirms their choice to proceed (usually by typing RET -twice, depending on the minibuffer settings), ~denote-open-or-create~ -will call the ~denote~ command interactively to create a new note. It -will then use whatever prompts ~denote~ normally has, per the user -option ~denote-prompts~ ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). If the title prompt -is involved (the default behaviour), the ~denote-open-or-create~ sets -up this prompt to have the previous input as the default title of the -note to-be-created. This means that the user can type RET at the -empty prompt to re-use what they typed in previously. Commands to use -previous inputs from the history are also available (=M-p= or =M-n= in -the minibuffer, which call ~previous-history-element~ and -~next-history-element~ by default). Accessing the history is helpful -to, for example, make further edits to the available text. - -The ~denote-open-or-create-with-command~ is like the above, except -when it is about to create the new note it first prompts for the -specific file-creating command to use ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). For example, -the user may want to specify a signature for this new file, so they -can select the ~denote-signature~ command. - -Denote provides similar functionality for linking to an existing note -or creating a new one ([[#h:b6056e6b-93df-4e6b-a778-eebd105bac46][Link to a note or create it if missing]]). - -** Maintain separate directory silos for notes -:PROPERTIES: -:CUSTOM_ID: h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5 -:END: -#+cindex: Note silos - -The user option ~denote-directory~ accepts a value that represents the -path to a directory, such as =~/Documents/notes=. Normally, the user -will have one place where they store all their notes, in which case this -arrangement shall suffice. - -There is, however, the possibility to maintain separate directories of -notes. By "separate", we mean that they do not communicate with each -other: no linking between them, no common keywords, nothing. Think of -the scenario where one set of notes is for private use and another is -for an employer. We call these separate directories "silos". - -To create silos, the user must specify a local variable at the root of -the desired directory. This is done by creating a =.dir-locals.el= -file, with the following contents: - -#+begin_src emacs-lisp -;;; Directory Local Variables. For more information evaluate: -;;; -;;; (info "(emacs) Directory Variables") - -((nil . ((denote-directory . "/path/to/silo/")))) -#+end_src - -When inside the directory that contains this =.dir-locals.el= file, all -Denote commands/functions for note creation, linking, the inference of -available keywords, et cetera will use the silo as their point of -reference. They will not read the global value of ~denote-directory~. -The global value of ~denote-directory~ is read everywhere else except -the silos. - -[[#h:0f72e6ea-97f0-42e1-8fd4-0684af0422e0][Use custom commands to select a silo]]. - -In concrete terms, this is a representation of the directory structures -(notice the =.dir-locals.el= file is needed only for the silos): - -#+begin_example -;; This is the global value of 'denote-directory' (no need for a .dir-locals.el) -~/Documents/notes -|-- 20210303T120534--this-is-a-test__journal_philosophy.txt -|-- 20220303T120534--another-sample__journal_testing.md -`-- 20220620T181255--the-third-test__keyword.org - -;; A silo with notes for the employer -~/different/path/to/notes-for-employer -|-- .dir-locals.el -|-- 20210303T120534--this-is-a-test__conference.txt -|-- 20220303T120534--another-sample__meeting.md -`-- 20220620T181255--the-third-test__keyword.org - -;; Another silo with notes for my volunteering -~/different/path/to/notes-for-volunteering -|-- .dir-locals.el -|-- 20210303T120534--this-is-a-test__activism.txt -|-- 20220303T120534--another-sample__teambuilding.md -`-- 20220620T181255--the-third-test__keyword.org -#+end_example - -It is possible to configure other user options of Denote to have a -silo-specific value. For example, this one changes the -~denote-known-keywords~ only for this particular silo: - -#+begin_src emacs-lisp -;;; Directory Local Variables. For more information evaluate: -;;; -;;; (info "(emacs) Directory Variables") - -((nil . ((denote-directory . "/path/to/silo/") - (denote-known-keywords . ("food" "drink"))))) -#+end_src - -This one is like the above, but also disables ~denote-infer-keywords~: - -#+begin_src emacs-lisp -;;; Directory Local Variables. For more information evaluate: -;;; -;;; (info "(emacs) Directory Variables") - -((nil . ((denote-directory . "/path/to/silo/") - (denote-known-keywords . ("food" "drink")) - (denote-infer-keywords . nil)))) -#+end_src - -To expand the list of local variables to, say, cover specific major -modes, we can do something like this: - -#+begin_src emacs-lisp -;;; Directory Local Variables. For more information evaluate: -;;; -;;; (info "(emacs) Directory Variables") - -((nil . ((denote-directory . "/path/to/silo/") - (denote-known-keywords . ("food" "drink")) - (denote-infer-keywords . nil))) - (org-mode . ((org-hide-emphasis-markers . t) - (org-hide-macro-markers . t) - (org-hide-leading-stars . t)))) -#+end_src - -As not all user options have a "safe" local value, Emacs will ask the -user to confirm their choice and to store it in the Custom code -snippet that is normally appended to init file (or added to the file -specified by the user option ~custom-file~). - -Finally, it is possible to have a =.dir-locals.el= for subdirectories -of any ~denote-directory~. Perhaps to specify a different set of -known keywords, while not making the subdirectory a silo in its own -right. We shall not expand on such an example, as we trust the user -to experiment with the best setup for their workflow. - -Feel welcome to ask for help if the information provided herein is not -sufficient. The manual shall be expanded accordingly. - -*** Use custom commands to select a silo -:PROPERTIES: -:CUSTOM_ID: h:0f72e6ea-97f0-42e1-8fd4-0684af0422e0 -:END: - -[ As part of version 2.1.0, the contents of this section - are formally provided in the file =denote-silo-extras.el=. We keep - this here for existing users. Otherwise consult the new entry in - the manual ([[#h:e43baf95-f201-4fec-8620-c0eb5eaa1c85][The =denote-silo-extras.el=]]). ] - -We implement silos as directory-local values of the user option -~denote-directory~. This means that all Denote commands read from the -local value if they are invoked from that context. For example, if -=~/Videos/recordings= is a silo and =~/Documents/notes= is the -default/global value of ~denote-directory~ all Denote commands will -read the video's path when called from there (e.g. by using Emacs' -~dired~); any other context reads the global value. - -[[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directory silos for notes]]. - -There are cases where the user (i) wants to maintain multiple silos -and (ii) prefers an interactive way to switch between them without -going through Dired. Since this is specific to the user's workflow, -it is easier to have some custom code for it. The following should be -added to the user's Denote configuration: - -#+begin_src emacs-lisp -(defvar my-denote-silo-directories - `("/home/prot/Videos/recordings" - "/home/prot/Documents/books" - ;; You don't actually need to include the `denote-directory' here - ;; if you use the regular commands in their global context. I am - ;; including it for completeness. - ,denote-directory) - "List of file paths pointing to my Denote silos. - This is a list of strings.") - -(defvar my-denote-commands-for-silos - '(denote - denote-date - denote-subdirectory - denote-template - denote-type) - "List of Denote commands to call after selecting a silo. - This is a list of symbols that specify the note-creating - interactive functions that Denote provides.") - -(defun my-denote-pick-silo-then-command (silo command) - "Select SILO and run Denote COMMAND in it. - SILO is a file path from `my-denote-silo-directories', while - COMMAND is one among `my-denote-commands-for-silos'." - (interactive - (list (completing-read "Select a silo: " my-denote-silo-directories nil t) - (intern (completing-read - "Run command in silo: " - my-denote-commands-for-silos nil t)))) - (let ((denote-directory silo)) - (call-interactively command))) -#+end_src - -With this in place, =M-x my-denote-pick-silo-then-command= will use -minibuffer completion to select a silo among the predefined options -and then ask for the command to run in that context. - -Note that =let= binding ~denote-directory~ can be used in custom -commands and other wrapper functions to override the global default -value of ~denote-directory~ to select silos. - -To see another example of a wrapper function that =let= binds -~denote-directory~, see: - -[[#h:d0c7cb79-21e5-4176-a6af-f4f68578c8dd][Extending Denote: Split an Org subtree into its own note]]. - -*** The =denote-silo-extras.el= -:PROPERTIES: -:CUSTOM_ID: h:e43baf95-f201-4fec-8620-c0eb5eaa1c85 -:END: - -The =denote-silo-extras.el= provides optional convenience functions for -working with silos ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directory silos for notes]]). -Start by loading the relevant library: - -#+begin_src emacs-lisp -(require 'denote-silo-extras) -#+end_src - -#+vindex: denote-silo-extras-directories -The user option ~denote-silo-extras-directories~ specifies a list of -directories that the user has set up as ~denote-directory~ silos. - -#+findex: denote-silo-extras-create-note -The command ~denote-silo-extras-create-note~ prompts for a directory -among ~denote-silo-extras-directories~ and runs the ~denote~ command -from there. - -#+findex: denote-silo-extras-open-or-create -Similar to the above, the command ~denote-silo-extras-open-or-create~ -prompts for a directory among ~denote-silo-extras-directories~ and runs -the ~denote-open-or-create~ command from there. - -#+findex: denote-silo-extras-select-silo-then-command -The command ~denote-silo-extras-select-silo-then-command~ prompts with -minibuffer completion for a directory among ~denote-silo-extras-directories~. -Once the user selects a silo, a second prompt asks for a Denote -note-creation command to call from inside that silo ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). - -** Exclude certain directories from all operations -:PROPERTIES: -:CUSTOM_ID: h:8458f716-f9c2-4888-824b-2bf01cc5850a -:END: - -#+vindex: denote-excluded-directories-regexp -The user option ~denote-excluded-directories-regexp~ instructs all -Denote functions that read or check file/directory names to omit -directories that match the given regular expression. The regexp needs -to match only the name of the directory, not its full path. - -Affected operations include file prompts and functions that return the -available files in the value of the user option ~denote-directory~ -([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directory silos for notes]]). - -File prompts are used by several commands, such as ~denote-link~ and -~denote-subdirectory~. - -Functions that check for files include ~denote-directory-files~ and -~denote-directory-subdirectories~. - -The match is performed with ~string-match-p~. - -[[#h:c916d8c5-540a-409f-b780-6ccbd90e088e][For developers or advanced users]]. - -** Exclude certain keywords from being inferred -:PROPERTIES: -:CUSTOM_ID: h:69e518ee-ed43-40ab-a5f4-c780a23e5358 -:END: - -#+vindex: denote-excluded-keywords-regexp -The user option ~denote-excluded-keywords-regexp~ omits keywords that -match a regular expression from the list of inferred keywords. - -Keywords are inferred from file names and provided at relevant prompts -as completion candidates when the user option ~denote-infer-keywords~ -is non-nil. - -The match is performed with ~string-match-p~. - -** Use Denote commands from the menu bar or context menu -:PROPERTIES: -:CUSTOM_ID: h:c4290e15-e97e-4a9b-b8db-6b9738e37e78 -:END: - -Denote registers a submenu for the ~menu-bar-mode~. Users will find -the entry called "Denote". From there they can use their pointer to -select a command. For a sample of how this looks, read the -development log: . - -#+findex: denote-menu-bar-mode -The command ~denote-menu-bar-mode~ toggles the presentation of the -menu. It is enabled by default. - -Emacs also provides support for operations through a context menu. -This is typically the set of actions that are made available via a -right mouse click. Users who enable ~context-menu-mode~ can register -the Denote entry for it by adding the following to their configuration -file: - -#+begin_src emacs-lisp -(add-hook 'context-menu-functions #'denote-context-menu) -#+end_src - -* Renaming files -:PROPERTIES: -:CUSTOM_ID: h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca -:END: - -Denote provides commands to rename files and update their front matter -where relevant. For Denote to work, only the file name needs to be in -order, by following our naming conventions ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). -The linking mechanism, in particular, needs just the identifier in the -file name ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]). - -We write front matter in notes for the user's convenience and for other -tools to make use of that information (e.g. Org's export mechanism). -The renaming mechanism takes care to keep this data in sync with the -file name, when the user performs a change. - -Renaming is useful for managing existing files created with Denote, -but also for converting older text files to Denote notes. Denote's -file-naming scheme is not specific to notes or text files: it is -relevant for all sorts of items, such as multimedia and PDFs that form -part of the user's longer-term storage. While Denote does not manage -such files (e.g. doesn't create links to them), it already has all the -mechanisms to facilitate the task of renaming them. - -#+vindex: denote-after-rename-file-hook -All renaming commands run the ~denote-after-rename-file-hook~ after a -succesful operation. - -Apart from renaming files, Denote can also rename only the buffer. -The idea is that the underlying file name is correct but it can be -easier to use shorter buffer names when displaying them on the mode -line or switching between then with commands like ~switch-to-buffer~. - -[[#h:3ca4db16-8f26-4d7d-b748-bac48ae32d69][Automatically rename Denote buffers]]. - -** Rename a single file -:PROPERTIES: -:CUSTOM_ID: h:7cc9e000-806a-48da-945c-711bbc7426b0 -:END: - -#+findex: denote-rename-file -The ~denote-rename-file~ command renames a file and updates existing -front matter if appropriate. It is possible to do the same with -multiple files ([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files interactively]]). - -It always renames the file where it is located in the file system: -it never moves it to another directory. - -If in Dired, it considers =FILE= to be the one at point, else it -prompts with minibuffer completion for one. When called from Lisp, -=FILE= is a file system path represented as a string. - -If =FILE= has a Denote-compliant identifier, it retains it while -updating components of the file name referenced by the user option -~denote-prompts~ ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The ~denote-prompts~ option]]). By default, these are -the =TITLE= and =KEYWORDS=. The =SIGNATURE= is another one. When -called from Lisp, =TITLE= and =SIGNATURE= are strings, while -=KEYWORDS= is a list of strings. - -If there is no identifier, ~denote-rename-file~ creates an identifier -based on the following conditions: - -1. If the ~denote-prompts~ includes an entry for date prompts, then it - prompts for =DATE= and takes its input to produce a new identifier. For - use in Lisp, =DATE= must conform with ~denote-valid-date-p~. - -2. If =DATE= is nil (e.g. when ~denote-prompts~ does not include a - date entry), it uses the file attributes to determine the last - modified date of =FILE= and formats it as an identifier. - -3. As a fallback, it derives an identifier from the current date and - time. - -4. At any rate, if the resulting identifier is not unique among the - files in the variable ~denote-directory~, it increments it such - that it becomes unique. - -In interactive use, and assuming ~denote-prompts~ includes a title -entry, the ~denote-rename-file~ makes the =TITLE= prompt have -prefilled text in the minibuffer that consists of the current title of -=FILE=. The current title is either retrieved from the front matter -(such as the =#+title= in Org) or from the file name. - -The command does the same for the =SIGNATURE= prompt, subject to -~denote-prompts~, by prefilling the minibuffer with the current -signature of =FILE=, if any. - -Same principle for the =KEYWORDS= prompt: it converts the keywords in -the file name into a comma-separated string and prefills the minibuffer -with it (the =KEYWORDS= prompt accepts more than one keywords, each -separated by a comma, else the ~crm-separator~). - -For all prompts, the ~denote-rename-file~ interprets an empty input as -an instruction to remove that file name component. For example, if a -=TITLE= prompt is available and =FILE= is =20240211T093531--some-title__keyword1.org= -then it renames =FILE= to =20240211T093531__keyword1.org=. If a file -name component is present, but there is no entry for it in -~denote-prompts~, keep it as-is. - -[ NOTE: Please check with your minibuffer user interface how to - provide an empty input. The Emacs default setup accepts the empty - minibuffer contents as they are, though popular packages like - ~vertico~ use the first available completion candidate instead. For - ~vertico~, the user must either move one up to select the prompt and - then type =RET= there with empty contents, or use the command - ~vertico-exit-input~ with empty contents. That Vertico command is - bound to =M-RET= as of this writing on 2024-02-13 08:08 +0200. ] - -When renaming =FILE=, the command reads its file type extension (like -=.org=) and preserves it through the renaming process. Files that have -no extension are left without one. - -As a final step, it asks for confirmation, showing the difference -between old and new file names. It does not ask for confirmation if -the user option ~denote-rename-no-confirm~ is set to a non-nil value -([[#h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7][The ~denote-rename-no-confirm~ option]]). - -If =FILE= has front matter for =TITLE= and =KEYWORDS=, -~denote-rename-file~ asks to rewrite their values in order to reflect -the new input, unless ~denote-rename-no-confirm~ is non-nil. When the -~denote-rename-no-confirm~ is nil (the default), the underlying buffer -is not saved, giving the user the change to invoking ~diff-buffer-with-file~ to -double-check the effect. The rewrite of the =TITLE= and =KEYWORDS= in -the front matter should not affect the rest of the front matter. - -If the file does not have front matter but is among the supported file -types (per ~denote-file-type~), the ~denote-rename-file~ adds front -matter to the top of it and leaves the buffer unsaved for further -inspection. It actually saves the buffer if ~denote-rename-no-confirm~ -is non-nil ([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]). - -This command is intended to (i) rename Denote files, (ii) convert -existing supported file types to Denote notes, and (ii) rename -non-note files (e.g. =PDF=) that can benefit from Denote's file-naming -scheme. - -For a version of this command that works with multiple files -one-by-one, use ~denote-dired-rename-files~ ([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files interactively]]). - -*** The ~denote-rename-no-confirm~ option -:PROPERTIES: -:CUSTOM_ID: h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7 -:END: - -#+vindex: denote-rename-no-confirm -The user option ~denote-rename-no-confirm~ makes all commands that -rename files not prompt for confirmation and save buffers outright ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]). - -This affects the behaviour of the commands ~denote-rename-file~, -~denote-dired-rename-files~, ~denote-rename-file-using-front-matter~, -~denote-dired-rename-marked-files-with-keywords~, -~denote-dired-rename-marked-files-using-front-matter~, -~denote-keywords-add~, ~denote-keywords-remove~, and any other command -that builds on top of them. - -The default behaviour of the ~denote-rename-file~ command (and others -like it) is to ask for an affirmative answer as a final step before -changing the file name and, where relevant, inserting or updating the -corresponding front matter. It also does not save the affected file's -buffer to let the user inspect and confirm the changes (such as by -invoking the command ~diff-buffer-with-file~). - -With this user option bound to a non-nil value, buffers are saved as -well. The assumption is that the user who opts in to this feature is -familiar with the ~denote-rename-file~ (or related) operation and -knows it is reliable. - -Specialised commands that build on top of ~denote-rename-file~ (or -related) may internally bind this user option to a non-nil value in -order to perform their operation (e.g. ~denote-dired-rename-files~ -goes through each marked Dired file, prompting for the information to -use, but carries out the renaming without asking for confirmation -([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files interactively]])). - -** Rename a single file based on its front matter -:PROPERTIES: -:CUSTOM_ID: h:3ab08ff4-81fa-4d24-99cb-79f97c13a373 -:END: - -#+findex: denote-rename-file-using-front-matter -In the previous section, we covered the more general mechanism of the -command ~denote-rename-file~ ([[#h:7cc9e000-806a-48da-945c-711bbc7426b0][Rename a single file]]). There is also a -way to have the same outcome by making Denote read the data in the -current file's front matter and use it to construct/update the file -name. The command for this is ~denote-rename-file-using-front-matter~. -It is only relevant for files that (i) are among the supported file -types, per ~denote-file-type~, and (ii) have the requisite front matter -in place. - -Suppose you have an =.org= file with this front matter ([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]): - -#+begin_example -#+title: My sample note file -#+date: [2022-08-05 Fri 13:10] -#+filetags: :testing: -#+identifier: 20220805T131044 -#+end_example - -Its file name reflects this information: - -: 20220805T131044--my-sample-note-file__testing.org - -You want to change its title and keywords manually, so you modify it thus: - -#+begin_example -#+title: My modified sample note file -#+date: [2022-08-05 Fri 13:10] -#+filetags: :testing:denote:emacs: -#+identifier: 20220805T131044 -#+end_example - -At this stage, the file name still shows the old title and keywords. -You now invoke ~denote-rename-file-using-front-matter~ and it updates -the file name to: - -: 20220805T131044--my-modified-sample-note-file__testing_denote_emacs.org - -The renaming is subject to a "yes or no" prompt that shows the old and -new names, just so the user is certain about the change. - -If called interactively with a prefix argument (=C-u= by default) or -from Lisp with a non-nil =NO-CONFIRM= argument, this "yes or no" -prompt is skipped and the renaming is done outright. - -If called interactively with a double prefix argument (=C-u C-u= by -default) or from Lisp with a non-nil =SAVE-BUFFER= argument, the -buffer is saved after the front matter is updated and the file is -renamed. - -If the user option ~denote-rename-no-confirm~ is non-nil, it is -interpreted the same way as a combination of =NO-CONFIRM= and -=SAVE-BUFFER= ([[#h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7][The ~denote-rename-no-confirm~ option]]). - -The identifier of the file, if any, is never modified even if it is -edited in the front matter: Denote considers the file name to be the -source of truth in this case, to avoid potential breakage with typos and -the like. - -** Rename multiple files interactively -:PROPERTIES: -:CUSTOM_ID: h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde -:END: - -#+findex: denote-dired-rename-files -#+findex: denote-dired-rename-marked-files -The command ~denote-dired-rename-files~ (alias -~denote-dired-rename-marked-files~) renames the files that are -marked in a Dired buffer. Its behaviour is similar to the -~denote-rename-file~ in that it prompts for a title, keywords, and -signature ([[#h:7cc9e000-806a-48da-945c-711bbc7426b0][Rename a single file]]). It does so over each marked file, -renaming one after the other. - -Unlike ~denote-rename-file~, the command ~denote-dired-rename-files~ -does not ask to confirm the changes made to the files: it performs -them outright. This is done to make it easier to rename multiple files -without having to confirm each step. For an even more direct approach, -check the command ~denote-dired-rename-marked-files-with-keywords~. - -- [[#h:f365ff7e-2140-4e14-a92f-666ae97382a4][Rename by writing only keywords]] -- [[#h:ea5673cd-e6ca-4c42-a066-07dc6c9d57f8][Rename multiple files based on their front matter]] - -** Rename multiple files at once by asking only for keywords -:PROPERTIES: -:CUSTOM_ID: h:f365ff7e-2140-4e14-a92f-666ae97382a4 -:END: - -#+findex: denote-dired-rename-marked-files-with-keywords -The ~denote-dired-rename-marked-files-with-keywords~ command renames -marked files in Dired to conform with our file-naming scheme. It does -so by writing keywords to them. Specifically, it does the following: - -- retains the file's existing name and makes it the =TITLE= field, per - Denote's file-naming scheme; - -- sluggifies the =TITLE= and adjusts its letter casing, according to - our conventions; - -- prepends an identifier to the =TITLE=, if one is missing; - -- preserves the file's extension, if any; - -- prompts once for =KEYWORDS= and applies the user's input to the - corresponding field in the file name, rewriting any keywords that - may exist while removing keywords that do exist if =KEYWORDS= is - empty; - -- adds or rewrites existing front matter to the underlying file, if it - is recognized as a Denote note (per the ~denote-file-type~ user - option), such that it includes the new keywords. - -[ Note that the affected buffers are not saved, unless the user option - ~denote-rename-no-confirm~ is non-nil. Users can thus check them to - confirm that the new front matter does not cause any problems (e.g. - with the ~diff-buffer-with-file~ command). Multiple buffers can be - saved in one go with the command ~save-some-buffers~ (read its doc - string). ] - -** Rename multiple files based on their front matter -:PROPERTIES: -:CUSTOM_ID: h:ea5673cd-e6ca-4c42-a066-07dc6c9d57f8 -:END: - -#+findex: denote-dired-rename-marked-files-using-front-matter -As already noted, Denote can rename a file based on the data in its -front matter ([[#h:3ab08ff4-81fa-4d24-99cb-79f97c13a373][Rename a single file based on its front matter]]). The -command ~denote-dired-rename-marked-files-using-front-matter~ extends -this principle to a batch operation which applies to all marked files in -Dired. - -Marked files must count as notes for the purposes of Denote, which -means that they at least have an identifier in their file name and use -a supported file type, per ~denote-file-type~. Files that do not meet -this criterion are ignored, because Denote cannot know if they have -front matter and what that may be. For such files, it is still -possible to rename them interactively ([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files interactively]]). - -** Rename a file by changing only its file type -:PROPERTIES: -:CUSTOM_ID: h:85b65995-89fd-4978-bba3-7bb6c8d6f945 -:END: - -#+findex: denote-change-file-type-and-front-matter -The command ~denote-change-file-type-and-front-matter~ provides the -convenience of converting a note taken in one file type, say, =.txt= -into another like =.org=. It presents a choice among the -~denote-file-type~ options. - -The conversion does NOT modify the existing front matter. Instead, it -prepends new front matter to the top of the file. We do this as a -safety precaution since the user can, in principle, add arbitrary -extras to their front matter that we would not want to touch. - -If in Dired, ~denote-change-file-type-and-front-matter~ operates on -the file at point, else it prompts with minibuffer completion for one. - -The title of the file is retrieved from a line starting with a title -field in the file's front matter, depending on the previous file type -(e.g. =#+title= for Org). The same process applies for keywords. - -As a final step, the command asks for confirmation, showing the -difference between old and new file names. - -** Rename a file by adding or removing keywords interactively -:PROPERTIES: -:CUSTOM_ID: h:ad4dde4a-8e88-470a-97ae-e7b9d4b41fb4 -:END: - -#+findex: denote-keywords-add -#+findex: denote-keywords-remove -The commands ~denote-keywords-add~ and ~denote-keywords-remove~ -streamline the process of interactively updating a file's keywords in -the front matter and renaming it accordingly. - -The ~denote-keywords-add~ asks for keywords using the familiar -minibuffer prompt ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). It then renames the file -([[#h:3ab08ff4-81fa-4d24-99cb-79f97c13a373][Rename a single file based on its front matter]]). - -Similarly, the ~denote-keywords-remove~ removes one or more keywords -from the list of existing keywords and then renames the file -accordingly. - -Both commands accept an optional prefix argument to automatically save -the buffer. Similarly, they both interpret a non-nil value for the -user option ~denote-rename-no-confirm~ the same as the prefix argument -([[#h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7][The ~denote-rename-no-confirm~ option]]). - -Furthermore, both commands call the ~denote-after-rename-file-hook~ as -a final step after carrying out their task. - -#+findex: denote-rename-add-keywords -#+findex: denote-rename-remove-keywords -Aliases for these commands are: ~denote-rename-add-keywords~ and -~denote-rename-remove-keywords~. - -** Rename a file by adding or removing a signature interactively -:PROPERTIES: -:CUSTOM_ID: h:b08a350f-b269-47ed-8c2a-b8ecf1b63c7f -:END: - -#+findex: denote-rename-add-signature -#+findex: denote-rename-remove-signature -The commands ~denote-rename-add-signature~ and -~denote-rename-remove-signature~ streamline the process of -interactively adding or removing a signature from a given file ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). - -The ~denote-rename-add-signature~ prompts for a file and a signature. The -default value for the file prompt is the file of the currently open -buffer or the file-at-point in a Dired buffer. The signature is an -ordinary string, defaulting to the selected file's signature, if any. - -The ~denote-rename-remove-signature~ uses the same file prompt as -above. It performs its action only if the selected file has a -signature. Otherwise, it does nothing. - -Both commands ask for confirmation before carrying out their action. -They do so unless the user option ~denote-rename-no-confirm~ is set to -a non-nil value ([[#h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7][The ~denote-rename-no-confirm~ option]]). They also -both take care to reload any Dired buffers and run the -~denote-after-rename-file-hook~ as a final step. - -** Faces used by rename commands -:PROPERTIES: -:CUSTOM_ID: h:ab3f355a-f763-43ae-a4c9-179d2d9265a5 -:END: - -These are the faces used by the various Denote rename commands to -style or highlight the old/new/current file shown in the relevant -minibuffer prompts: - -- ~denote-faces-prompt-current-name~ -- ~denote-faces-prompt-new-name~ -- ~denote-faces-prompt-old-name~ - -* The file-naming scheme -:PROPERTIES: -:CUSTOM_ID: h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d -:END: - -#+vindex: denote-directory -Notes are stored in the ~denote-directory~. The default path is -=~/Documents/notes=. The ~denote-directory~ can be a flat listing, -meaning that it has no subdirectories, or it can be a directory tree. -Either way, Denote takes care to only consider "notes" as valid -candidates in the relevant operations and will omit other files or -directories. - -Every note produced by Denote follows this pattern by default -([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]): - -: DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION - -The =DATE= field represents the date in year-month-day format followed -by the capital letter =T= (for "time") and the current time in -hour-minute-second notation. The presentation is compact: -=20220531T091625=. The =DATE= serves as the unique identifier of each -note and, as such, is also known as the file's ID or identifier. - -File names can include a string of alphanumeric characters in the -=SIGNATURE= field. Signatures have no clearly defined purpose and are up -to the user to define. One use-case is to use them to establish -sequential relations between files (e.g. 1, 1a, 1b, 1b1, 1b2, ...). - -Signatures are an optional extension to Denote's file-naming scheme. -They can be added to newly created files on demand, with the command -~denote-signature~, or by modifying the value of the user option -~denote-prompts~. - -The =TITLE= field is the title of the note, as provided by the user. -It automatically gets downcased by default and is also hyphenated -([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]). An entry about "Economics -in the Euro Area" produces an =economics-in-the-euro-area= string for -the =TITLE= of the file name. - -The =KEYWORDS= field consists of one or more entries demarcated by an -underscore (the separator is inserted automatically). Each keyword is -a string provided by the user at the relevant prompt which broadly -describes the contents of the entry. - -Each of the keywords is a single word, with multiple keywords providing -the multi-dimensionality needed for advanced searches through Denote -files. Users who need to compose a keyword out of multiple words such -as camelCase/CamelCase and are encouraged to use the -~denote-file-name-slug-functions~ user option accordingly -([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]). - -#+vindex: denote-file-type -The =EXTENSION= is the file type. By default, it is =.org= (~org-mode~) -though the user option ~denote-file-type~ provides support for Markdown -with YAML or TOML variants (=.md= which runs ~markdown-mode~) and plain -text (=.txt= via ~text-mode~). Consult its doc string for the minutiae. -While files end in the =.org= extension by default, the Denote code base -does not actually depend on org.el and/or its accoutrements. - -Examples: - -: 20220610T043241--initial-thoughts-on-the-zettelkasten-method__notetaking.org -: 20220610T062201--define-custom-org-hyperlink-type__denote_emacs_package.md -: 20220610T162327--on-hierarchy-and-taxis__notetaking_philosophy.txt - -The different field separators, namely =--= and =__= introduce an -efficient way to anchor searches (such as with Emacs commands like -~isearch~ or from the command-line with ~find~ and related). A query -for =_word= always matches a keyword, while a regexp in the form of, -say, ="\\([0-9T]+?\\)--\\(.*?\\)_"= captures the date in group =\1= and -the title in =\2= (test any regular expression in the current buffer by -invoking =M-x re-builder=). - -[[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]. - -The ~denote-prompts~ can be configured in such ways to yield the -following file name permutations: - -: DATE.EXT -: DATE--TITLE.EXT -: DATE__KEYWORDS.EXT -: DATE==SIGNATURE.EXT -: DATE==SIGNATURE--TITLE.EXT -: DATE==SIGNATURE--TITLE__KEYWORDS.EXT -: DATE==SIGNATURE__KEYWORDS.EXT - -When in doubt, stick to the default design. - -While Denote is an Emacs package, notes should work long-term and not -depend on the functionality of a specific program. The file-naming -scheme we apply guarantees that a listing is readable in a variety of -contexts. The Denote file-naming scheme is, in essence, an effective, -low-tech invention. - -** Sluggification of file name components -:PROPERTIES: -:CUSTOM_ID: h:ae8b19a1-7f67-4258-96b3-370a72c43f4e -:END: - -Files names can contain any character that the file system -permits. Denote imposes a few additional restrictions: - -+ The tokens "==", =__= and =--= are interpreted by Denote and should - appear only once. - -+ The dot character is not allowed in a note's file name, except to - indicate the file type extension. Denote recognises two extensions - for encrypted files, like =.txt.gpg=. - -By default, Denote enforces other rules to file names through the user -option ~denote-file-name-slug-functions~. These rules are applied to -file names by default: - -+ What we count as "illegal characters" are removed. The constant - ~denote-excluded-punctuation-regexp~ holds the relevant value. - -+ Input for a file title is hyphenated. The original value is - preserved in the note's contents ([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]). - -+ Spaces or other delimiters are removed from keywords, meaning that - =hello-world= becomes =helloworld=. This is because hyphens in - keywords do not work everywhere, such as in Org. Plus, hyphens are - word separators in the title and we want to keep distinct separators - for each component to make search easier and semantic - ([[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]). - -+ Signatures are like the above, but use the equals sign instead of - hyphens as a word separator. - -+ All file name components are downcased. Further down we document how - to deviate from these rules, such as to accept input of the form - =helloWorld= or =HelloWorld= verbatim. - -Denote imposes these restrictions to enforce uniformity, which is -helpful long-term as it keeps all files with the same predictable -pattern. Too many permutations make searches more difficult to express -accurately and be confident that the matches cover all files. -Nevertheless, one of the principles of Denote is its flexibility or -hackability and so users can deviate from the aforementioned -([[#h:d375c6d2-92c7-425f-9d9d-219ff47ed2a3][User-defined sluggification of file name components]]). - -** User-defined sluggification of file name components -:PROPERTIES: -:CUSTOM_ID: h:d375c6d2-92c7-425f-9d9d-219ff47ed2a3 -:END: - -#+vindex: denote-file-name-slug-functions -The user option ~denote-file-name-slug-functions~ controls the -sluggification of file name components ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]). -The default method is outlined above and in the previous section -([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). - -The value of this user option is an alist where each element is a cons -cell of the form =(COMPONENT . METHOD)=. For example, here is the -default value: - -#+begin_example emacs-lisp -'((title . denote-sluggify-title) - (signature . denote-sluggify-signature) - (keyword . denote-sluggify-keyword)) -#+end_example - -- The =COMPONENT= is an unquoted symbol among =title=, =signature=, - =keyword=, which refers to the corresponding component of the file - name. - -- The =METHOD= is a function to format the given component. This - function must take a string as its parameter and return the string - formatted for the file name. Note that even in the case of the - =keyword= component, the function receives one string representing a - single keyword and returns it formatted for the file name. Joining - the keywords together is handled internally by Denote. - -One commonly requested deviation from the sluggification rules is to -not sluggify individual keywords, such that the user's input is taken -as-is. This can be done as follows: - -#+begin_src emacs-lisp -(setq denote-file-name-slug-functions - '((title . denote-sluggify-title) - (keyword . identity) - (signature . denote-sluggify-signature))) -#+end_src - -The ~identity~ function simply returns the string it receives, thus -not altering it in any way. - -Another approach is to keep the sluggification but not downcase the -string. We can do this by modifying the original functions used by -Denote. For example, we have this: - -#+begin_src emacs-lisp -;; The original function for reference -(defun denote-sluggify-title (str) - "Make STR an appropriate slug for title." - (downcase (denote--slug-hyphenate (denote--slug-no-punct str)))) - -;; Our variant of the above, which does the same thing except from -;; downcasing the string. -(defun my-denote-sluggify-title (str) - "Make STR an appropriate slug for title." - (denote--slug-hyphenate (denote--slug-no-punct str))) - -;; Now we use our function to sluggify titles without affecting their -;; letter casing. -(setq denote-file-name-slug-functions - '((title . my-denote-sluggify-title) ; our function here - (signature . denote-sluggify-signature) - (keyword . denote-sluggify-keyword))) -#+end_src - -Follow this principle for all the sluggification functions. - -To access the source code, use either of the following built-in -methods: - -1. Call the command ~find-library~ and search for ~denote~. Then - navigate to the symbol you are searching for. - -2. Invoke the command ~describe-symbol~, search for the symbol you are - interested in, and from the resulting Help buffer either click on - the first link or do =M-x help-view-source= (bound to =s= in Help - buffers, by default). - -Remember that deviating from the default file-naming scheme of Denote -will make things harder to use in the future, as files can/will have -permutations that create uncertainty. The sluggification scheme and -concomitant restrictions we impose by default are there for a very -good reason: they are the distillation of years of experience. Here we -give you what you wish, but bear in mind it may not be what you need. -You have been warned. - -** Features of the file-naming scheme for searching or filtering -:PROPERTIES: -:CUSTOM_ID: h:1a953736-86c2-420b-b566-fb22c97df197 -:END: - -By default, file names have three fields and two sets of field -delimiters between them: - -: DATE--TITLE__KEYWORDS.EXTENSION - -When a signature is present, this becomes: - -: DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION - -Field delimiters practically serve as anchors for easier searching. -Consider this example: - -: 20220621T062327==1a2--introduction-to-denote__denote_emacs.txt - -You will notice that there are two matches for the word =denote=: one -in the title field and another in the keywords' field. Because of the -distinct field delimiters, if we search for =-denote= we only match -the first instance while =_denote= targets the second one. When -sorting through your notes, this kind of specificity is -invaluable---and you get it for free from the file names alone! -Similarly, a search for ==1= will show all notes that are related to -each other by virtue of their signature. - -Users can get a lot of value out of this simple yet effective -arrangement, even if they have no knowledge of regular expressions. -One thing to consider, for maximum effect, is to avoid using -multi-word keywords as those can get hyphenated like the title and -will thus interfere with the above: either set the user option -~denote-allow-multi-word-keywords~ to nil or simply insert single -words at the relevant prompts. - -* Front matter -:PROPERTIES: -:CUSTOM_ID: h:13218826-56a5-482a-9b91-5b6de4f14261 -:END: - -Notes have their own "front matter". This is a block of data at the top -of the file, with no empty lines between the entries, which is -automatically generated at the creation of a new note. The front matter -includes the title and keywords (aka "tags" or "filetags", depending on -the file type) which the user specified at the relevant prompt, as well -as the date and unique identifier, which are derived automatically. - -This is how it looks for Org mode (when ~denote-file-type~ is nil or the -=org= symbol): - -#+begin_example -#+title: This is a sample note -#+date: [2022-06-30 Thu 16:09] -#+filetags: :denote:testing: -#+identifier: 20220630T160934 -#+end_example - -For Markdown with YAML (~denote-file-type~ has the =markdown-yaml= -value), the front matter looks like this: - -#+begin_example ---- -title: "This is a sample note" -date: 2022-06-30T16:09:58+03:00 -tags: ["denote", "testing"] -identifier: "20220630T160958" ---- -#+end_example - -For Markdown with TOML (~denote-file-type~ has the =markdown-toml= -value), it is: - -#+begin_example -+++ -title = "This is a sample note" -date = 2022-06-30T16:10:13+03:00 -tags = ["denote", "testing"] -identifier = "20220630T161013" -+++ -#+end_example - -And for plain text (~denote-file-type~ has the =text= value), we have -the following: - -#+begin_example -title: This is a sample note -date: 2022-06-30 -tags: denote testing -identifier: 20220630T161028 ---------------------------- -#+end_example - -#+vindex: denote-date-format -The format of the date in the front matter is controlled by the user -option ~denote-date-format~. When nil, Denote uses a file-type-specific -format: - -- For Org, an inactive timestamp is used, such as - =[2022-06-30 Wed 15:31]=. - -- For Markdown, the RFC3339 standard is applied: - =2022-06-30T15:48:00+03:00=. - -- For plain text, the format is that of ISO 8601: =2022-06-30=. - -If the value is a string, ignore the above and use it instead. The -string must include format specifiers for the date. These are described -in the doc string of ~format-time-string~.. - -** Change the front matter format -:PROPERTIES: -:CUSTOM_ID: h:7f918854-5ed4-4139-821f-8ee9ba06ad15 -:END: - -Per Denote's design principles, the code is hackable. All front matter -is stored in variables that are intended for public use. We do not -declare those as "user options" because (i) they expect the user to have -some degree of knowledge in Emacs Lisp and (ii) implement custom code. - -[ NOTE for tinkerers: code intended for internal use includes double - hyphens in its symbol. "Internal use" means that it can be changed - without warning and with no further reference in the change log. Do - not use any of it without understanding the consequences. ] - -The variables which hold the front matter format are: - -#+vindex: denote-org-front-matter -- ~denote-org-front-matter~ - -#+vindex: denote-text-front-matter -- ~denote-text-front-matter~ - -#+vindex: denote-toml-front-matter -- ~denote-toml-front-matter~ - -#+vindex: denote-yaml-front-matter -- ~denote-yaml-front-matter~ - -These variables have a string value with specifiers that are used by the -~format~ function. The formatting operation passes four arguments which -include the values of the given entries. If you are an advanced user -who wants to edit this variable to affect how front matter is produced, -consider using something like =%2$s= to control where the Nth argument -is placed. - -When editing the value, make sure to: - -1. Not use empty lines inside the front matter block. - -2. Insert at least one empty line after the front matter block and do - not use any empty line before it. - -These help with consistency and might prove useful if we ever need to -operate on the front matter as a whole. - -With those granted, below are some examples. The approach is the same -for all variables. - -#+begin_src emacs-lisp -;; Like the default, but upcase the entries -(setq denote-org-front-matter - "#+TITLE: %s -#+DATE: %s -#+FILETAGS: %s -#+IDENTIFIER: %s -\n") - -;; Change the order (notice the %N$s notation) -(setq denote-org-front-matter - "#+title: %1$s -#+filetags: %3$s -#+date: %2$s -#+identifier: %4$s -\n") - -;; Remove the date -(setq denote-org-front-matter - "#+title: %1$s -#+filetags: %3$s -#+identifier: %4$s -\n") - -;; Remove the date and the identifier -(setq denote-org-front-matter - "#+title: %1$s -#+filetags: %3$s -\n") -#+end_src - -Note that ~setq~ has a global effect: it affects the creation of all new -notes. Depending on the workflow, it may be preferrable to have a -custom command which ~let~ binds the different format. We shall not -provide examples at this point as this is a more advanced feature and we -are not yet sure what the user's needs are. Please provide feedback and -we shall act accordingly. - -** Regenerate front matter -:PROPERTIES: -:CUSTOM_ID: h:54b48277-e0e5-4188-ad54-ef3db3b7e772 -:END: - -#+findex: denote-add-front-matter -Sometimes the user needs to produce new front matter for an existing -note. Perhaps because they accidentally deleted a line and could not -undo the operation. The command ~denote-add-front-matter~ can be used -for this very purpose. - -In interactive use, ~denote-add-front-matter~ must be invoked from a -buffer that visits a Denote note. It prompts for a title and then for -keywords. These are the standard prompts we already use for note -creation, so the keywords' prompt allows minibuffer completion and the -input of multiple entries, each separated by a comma ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). - -The newly created front matter is added to the top of the file. - -This command does not rename the file (e.g. to update the keywords). To -rename a file by reading its front matter as input, the user can rely on -~denote-rename-file-using-front-matter~ ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]). - -Note that ~denote-add-front-matter~ is useful only for existing Denote -notes. If the user needs to convert a generic text file to a Denote -note, they can use one of the command which first rename the file to -make it comply with our file-naming scheme and then add the relevant -front matter. - -* Linking notes -:PROPERTIES: -:CUSTOM_ID: h:fc913d54-26c8-4c41-be86-999839e8ad31 -:END: - -Denote offers several commands for linking between notes. - -All links target files which are Denote files. This means that they -have our file-naming scheme. Files need to be inside the -~denote-directory~ or one of its subdirectories. No other file is -recognised. - -The following sections delve into the details. - -** Adding a single link -:PROPERTIES: -:CUSTOM_ID: h:5e5e3370-12ab-454f-ba09-88ff44214324 -:END: - -#+findex: denote-link -The ~denote-link~ command inserts a link at point to a file specified -at the minibuffer prompt ([[#h:d99de1fb-b1b7-4a74-8667-575636a4d6a4][The ~denote-org-store-link-to-heading~ user option]]). -Links are formatted depending on the file type of the current note. In -Org and plain text buffers, links are formatted thus: -=[[denote:IDENTIFIER][DESCRIPTION]]=. While in Markdown they are expressed -as =[DESCRIPTION](denote:IDENTIFIER)=. - -When ~denote-link~ is called with a prefix argument (=C-u= by -default), it formats links like =[[denote:IDENTIFIER]]=. The user -might prefer its simplicity. - -By default, the description of the link is taken from the signature of -the file, if present, and the target file's front matter's title or, if -that is not available, from the file name. If the region is active, its -text is used as the link's description instead. If the active region -has no text, the inserted link uses just the identifier, as with the -=C-u= prefix mentioned above. - -Inserted links are automatically buttonized and remain active for as -long as the buffer is available. In Org this is handled by the major -mode: the =denote:= hyperlink type works exactly like the standard -=file:=. In Markdown and plain text, Denote performs the buttonization -of those links. To buttonize links in existing files while visiting -them, the user must add this snippet to their setup (it already excludes -Org): - -#+findex: denote-link-buttonize-buffer -#+begin_src emacs-lisp -(add-hook 'find-file-hook #'denote-link-buttonize-buffer) -#+end_src - -The ~denote-link-buttonize-buffer~ is also an interactive function in -case the user needs it. - -Links are created only for files which qualify as a "note" for our -purposes ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]). - -#+vindex: denote-faces-link -Links are styled with the ~denote-faces-link~ face, which looks exactly -like an ordinary link by default. This is just a convenience for the -user/theme in case they want =denote:= links to remain distinct from -other links. - -#+findex: denote-link-markdown-follow -In files whose major mode is ~markdown-mode~, the default key binding -=C-c C-o= (which calls the command ~markdown-follow-thing-at-point~) -correctly resolves =denote:= links. This method works in addition to -the =RET= key, which is made available by the aforementioned -buttonization. Interested users can refer to the function -~denote-link-markdown-follow~ for the implementation details. - -** The ~denote-org-store-link-to-heading~ user option -:PROPERTIES: -:CUSTOM_ID: h:d99de1fb-b1b7-4a74-8667-575636a4d6a4 -:END: - -#+vindex: denote-org-store-link-to-heading -The user option ~denote-org-store-link-to-heading~ determines whether -~org-store-link~ links to the current Org heading (such links are -merely "stored" and need to be inserted afterwards with the command -~org-insert-link~). Note that the ~org-capture~ command uses the -~org-link~ internally if it has to store a link. - -When its value is non-nil, ~org-store-link~ stores a link to the -current Org heading inside the Denote Org file. If the heading does -not have a =CUSTOM_ID=, it creates it and includes it in the heading's -=PROPERTIES= drawer. If a =CUSTOM_ID= exists, ~org-store-link~ use it -as-is. - -This makes the resulting link a combination of the =denote:= link type, -pointing to the identifier of the current file, plus the value of the -heading's =CUSTOM_ID=, such as: - -- =[[denote:20240118T060608][Some test]]= -- =[[denote:20240118T060608::#h:eed0fb8e-4cc7-478f-acb6-f0aa1a8bffcd][Some test::Heading text]]= - -Both lead to the same Denote file, but the latter jumps to the heading -with the given =CUSTOM_ID=. Notice that the link to the heading also -has a different description, which includes the heading text. - -The value of the =CUSTOM_ID= is determined by the Org user option -~org-id-method~. The sample shown above uses the default UUID -infrastructure. - -If ~denote-org-store-link-to-heading~ is set to a nil value, the -command ~org-store-link~ only stores links to the Denote file (using -its identifier), but not to the given heading. This is what Denote was -doing in versions prior to =2.3.0=. - -Note that the optional extension =denote-org-extras.el= defines the command -~denote-org-extras-link-to-heading~, which always links to a file+heading -regardless of the aforementioned user option ([[#h:fc1ad245-ec08-41be-8d1e-7153d99daf02][Insert link to an Org file with a further pointer to a heading]]). - -[ This feature only works in Org mode files, as other file types do - not have a linking mechanism that handles unique identifiers for - headings or other patterns to jump to. If ~org-store-link~ is - invoked in one such file, it captures only the Denote identifier of - the file, even if this user option is set to a non-nil value. ] - -** Insert link to an Org file with a further pointer to a heading -:PROPERTIES: -:CUSTOM_ID: h:fc1ad245-ec08-41be-8d1e-7153d99daf02 -:END: - -#+findex: denote-org-extras-link-to-heading -As part of the optional =denote-org-extras.el= extension, the command -~denote-org-extras-link-to-heading~ prompts for a link to an Org file -and then asks for a heading therein, using minibuffer completion. Once -the user provides input at the two prompts, the command inserts a link -at point which has the following pattern: =[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading text]]=. - -Because only Org files can have links to individual headings, the -command ~denote-org-extras-link-to-heading~ prompts only for Org files -(i.e. files which include the =.org= extension). Remember that Denote -works with many file types ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). - -This feature is similar to the concept of the user option ~denote-org-store-link-to-heading~ -([[#h:d99de1fb-b1b7-4a74-8667-575636a4d6a4][The ~denote-org-store-link-to-heading~ user option]]). It is, however, -interactive and differs in the directionality of the action. With that -user option, the command ~org-store-link~ will generate a =CUSTOM_ID= -for the current heading (or capture the value of one as-is), giving -the user the option to then call ~org-insert-link~ wherever they see -fit. By contrast, the command ~denote-org-extras-link-to-heading~ -prompts for a file, then a heading, and inserts the link at point. - -** Insert links matching a regexp -:PROPERTIES: -:CUSTOM_ID: h:9bec2c83-36ca-4951-aefc-7187c5463f90 -:END: - -#+findex: denote-add-links -The command ~denote-add-links~ adds links at point matching a -regular expression or plain string. The links are inserted as a -typographic list, such as: - -#+begin_example -- link1 -- link2 -- link3 -#+end_example - -Each link is formatted according to the file type of the current note, -as explained further above about the ~denote-link~ command. The current -note is excluded from the matching entries (adding a link to itself is -pointless). - -When called with a prefix argument (=C-u=) ~denote-add-links~ will -format all links as =[[denote:IDENTIFIER]]=, hence a typographic list: - -#+begin_example -- [[denote:IDENTIFIER-1]] -- [[denote:IDENTIFIER-2]] -- [[denote:IDENTIFIER-3]] -#+end_example - -Same examples of a regular expression that can be used with this -command: - -- =journal= match all files which include =journal= anywhere in their - name. - -- =_journal= match all files which include =journal= as a keyword. - -- =^2022.*_journal= match all file names starting with =2022= and - including the keyword =journal=. - -- =\.txt= match all files including =.txt=. In practical terms, this - only applies to the file extension, as Denote automatically removes - dots (and other characters) from the base file name. - -If files are created with ~denote-sort-keywords~ as non-nil (the -default), then it is easy to write a regexp that includes multiple -keywords in alphabetic order: - -- =_denote.*_package= match all files that include both the =denote= and - =package= keywords, in this order. - -- =\(.*denote.*package.*\)\|\(.*package.*denote.*\)= is the same as - above, but out-of-order. - -Remember that regexp constructs only need to be escaped once (like =\|=) -when done interactively but twice when called from Lisp. What we show -above is for interactive usage. - -Links are created only for files which qualify as a "note" for our -purposes ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]). - -** Insert link to file with signature -:PROPERTIES: -:CUSTOM_ID: h:066e5221-9844-474b-8858-398398646f86 -:END: - -#+findex: denote-link-with-signature -The command ~denote-link-with-signature~ prompts for a file among -those that contain a ===SIGNATURE= and inserts a link to it. The -description of the link includes the text of the signature and that of -the file's title, if any. For example, a link to the following file: - -: 20230925T144303==abc--my-first-signature-note__denote_testing.txt - -will get this link: =[[denote:20230925T144303][abc My first signature note]]=. - -For more advanced uses, refer to the doc string of the ~denote-link~ -function. - -** Insert links from marked files in Dired -:PROPERTIES: -:CUSTOM_ID: h:9cbb692e-5d8a-44a6-9193-899a07872a07 -:END: - -#+findex: denote-link-dired-marked-notes -The command ~denote-link-dired-marked-notes~ is similar to -~denote-add-links~ in that it inserts in the buffer a typographic list -of links to Denote notes ([[#h:9bec2c83-36ca-4951-aefc-7187c5463f90][Insert links matching a regexp]]). Though -instead of reading a regular expression, it lets the user mark files -in Dired and link to them. This should be easier for users of all -skill levels, instead of having to write a potentially complex regular -expression. - -If there are multiple buffers that visit a Denote note, this command -will ask to select one among them, using minibuffer completion. If -there is only one buffer, it will operate in it outright. If there are -no buffers, it will produce an error. - -With optional =ID-ONLY= as a prefix argument (=C-u= by default), the -command inserts links with just the identifier, which is the same -principle as with ~denote-link~ and others ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Adding a single link]]). - -The command ~denote-link-dired-marked-notes~ is meant to be used from a -Dired buffer. - -As always, links are created only for files which qualify as a "note" -for our purposes ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]). - -** Link to an existing note or create a new one -:PROPERTIES: -:CUSTOM_ID: h:b6056e6b-93df-4e6b-a778-eebd105bac46 -:END: - -In one's note-taking workflow, there may come a point where they are -expounding on a certain topic but have an idea about another subject -they would like to link to ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]). The user can always rely on -the other linking facilities we have covered herein to target files that -already exist. Though they may not know whether they already have notes -covering the subject or whether they would need to write new ones. To -this end, Denote provides two convenience commands: - -#+findex: denote-link-after-creating -+ ~denote-link-after-creating~ :: Create new note in the background and - link to it directly. - - Use ~denote~ interactively to produce the new note. Its doc string or - this manual explains which prompts will be used and under what - conditions ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). - - With optional =ID-ONLY= as a prefix argument (this is the =C-u= key, - by default) create a link that consists of just the identifier. Else - try to also include the file's title. This has the same meaning as in - ~denote-link~ ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Adding a single link]]). - - IMPORTANT NOTE: Normally, ~denote~ does not save the buffer it - produces for the new note ([[#h:bf80f4cd-6f56-4f7c-a991-8573161e4511][The ~denote-save-buffer-after-creation~ option]]). - This is a safety precaution to not write to disk unless the user - wants it (e.g. the user may choose to kill the buffer, thus - cancelling the creation of the note). However, for this command the - creation of the note happens in the background and the user may miss - the step of saving their buffer. We thus have to save the buffer in - order to (i) establish valid links, and (ii) retrieve whatever front - matter from the target file. - -#+findex: denote-link-after-creating-with-command -+ ~denote-link-after-creating-with-command~ :: This command is like - ~denote-link-after-creating~ except it prompts for a note-creating - command ([[*Points of entry][Points of entry]]). Use this to, for example, call - ~denote-signature~ so that the newly created note has a signature as - part of its file name. Optional =ID-ONLY= has the same meaning as - in the command ~denote-link-after-creating~. - -#+findex: denote-link-or-create -+ ~denote-link-or-create~ :: Use ~denote-link~ on =TARGET= file, - creating it if necessary. - - If =TARGET= file does not exist, call ~denote-link-after-creating~ - which runs the ~denote~ command interactively to create the file. The - established link will then be targeting that new file. - - If =TARGET= file does not exist, add the user input that was used to - search for it to the history of the ~denote-file-prompt~. The user - can then retrieve and possibly further edit their last input, using - it as the newly created note's actual title. At the ~denote-file-prompt~ - type =M-p= with the default key bindings, which calls ~previous-history-element~. - - With optional =ID-ONLY= as a prefix argument create a link with just - the file's identifier. This has the same meaning as in ~denote-link~. - - This command has the alias ~denote-link-to-existing-or-new-note~, - which helps with discoverability. - -#+findex: denote-link-or-create-with-command -+ ~denote-link-or-create-with-command~ :: This is like the above, - except when it is about to create the new note it first prompts for - the specific file-creating command to use ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). For - example, the user may want to specify a signature for this new file, - so they can select the ~denote-signature~ command. - -In all of the above, an optional prefix argument (=C-u= by default) -creates a link that consists of just the identifier. This has the -same meaning as in the regular ~denote-link~ command. - -Denote provides similar functionality for opening an existing note or -creating a new one ([[#h:ad91ca39-cf10-4e16-b224-fdf78f093883][Open an existing note or create it if missing]]). - -** The backlinks' buffer -:PROPERTIES: -:CUSTOM_ID: h:c73f1f68-e214-49d5-b369-e694f6a5d708 -:END: - -#+findex: denote-backlinks -The command ~denote-backlinks~ produces a bespoke buffer which -displays backlinks to the current note. A "backlink" is a link back -to the present entry. - -By default, the backlinks' buffer is designed to display the file name -of the note linking to the current entry. Each file name is presented -on its own line, like this: - -#+begin_example -Backlinks to "On being honest" (20220614T130812) ------------------------------------------------- - -20220614T145606--let-this-glance-become-a-stare__journal.txt -20220616T182958--feeling-butterflies-in-your-stomach__journal.txt -#+end_example - -#+vindex: denote-backlinks-show-context -When the user option ~denote-backlinks-show-context~ is non-nil, the -backlinks' buffer displays the line on which a link to the current -note occurs. It also shows multiple occurrences, if present. It looks -like this (and has the appropriate fontification): - -#+begin_example -Backlinks to "On being honest" (20220614T130812) ------------------------------------------------- - -20220614T145606--let-this-glance-become-a-stare__journal.txt -37: growing into it: [[denote:20220614T130812][On being honest]]. -64: As I said in [[denote:20220614T130812][On being honest]] I have never -20220616T182958--feeling-butterflies-in-your-stomach__journal.txt -62: indifference. In [[denote:20220614T130812][On being honest]] I alluded -#+end_example - -Note that the width of the lines in the context depends on the -underlying file. In the above example, the lines are split at the -~fill-column~. Long lines will show up just fine. Also note that the -built-in user option ~xref-truncation-width~ can truncate long lines -to a given maximum number of characters. - -[[#h:893eec49-d7be-4603-bcff-fcc247244011][Speed up backlinks' buffer creation?]] - -#+findex: denote-backlinks-mode -#+vindex: denote-backlinks-mode-map -The backlinks' buffer runs the major-mode ~denote-backlinks-mode~. It -binds keys to move between links with =n= (next) and =p= (previous). -These are stored in the ~denote-backlinks-mode-map~ (use =M-x -describe-mode= (=C-h m=) in an unfamiliar buffer to learn more about -it). When the user option ~denote-backlinks-show-context~ is non-nil, -all relevant Xref key bindings are fully functional: again, check -~describe-mode~. - -The backlinking facility uses Emacs' built-in Xref infrastructure. On -some operating systems, the user may need to add certain executables -to the relevant environment variable. - -[[#h:42f6b07e-5956-469a-8294-17f9cf62eb2b][Why do I get "Search failed with status 1" when I search for backlinks?]] - -Backlinks to the current file can also be visited by using the -minibuffer completion interface with the ~denote-find-backlink~ -command ([[#h:1bc2adad-dca3-4878-b9f0-b105d5dec6f4][Visiting linked files via the minibuffer]]). - -#+vindex: denote-link-backlinks-display-buffer-action -The placement of the backlinks' buffer is subject to the user option -~denote-link-backlinks-display-buffer-action~. Due to the nature of the -underlying ~display-buffer~ mechanism, this inevitably is a relatively -advanced feature. By default, the backlinks' buffer is displayed below -the current window. The doc string of our user option includes a sample -configuration that places the buffer in a left side window instead. -Reproducing it here for the sake of convenience: - -#+begin_src emacs-lisp -(setq denote-link-backlinks-display-buffer-action - '((display-buffer-reuse-window - display-buffer-in-side-window) - (side . left) - (slot . 99) - (window-width . 0.3))) -#+end_src - -** Writing metanotes -:PROPERTIES: -:CUSTOM_ID: h:6060a7e6-f179-4d42-a9de-a9968aaebecc -:END: - -A "metanote" is an entry that describes other entries who have something -in common. Writing metanotes can be part of a workflow where the user -periodically reviews their work in search of patterns and deeper -insights. For example, you might want to read your journal entries from -the past year to reflect on your experiences, evolution as a person, and -the like. - -The commands ~denote-add-links~, ~denote-link-dired-marked-notes~ are -suited for this task. - -[[#h:9bec2c83-36ca-4951-aefc-7187c5463f90][Insert links matching a regexp]]. - -[[#h:9cbb692e-5d8a-44a6-9193-899a07872a07][Insert links from marked files in Dired]]. - -You will create your metanote the way you use Denote ordinarily -(metanotes may have the =metanote= keyword, among others), write an -introduction or however you want to go about it, invoke the command -which inserts multiple links at once (see the above-cited nodes), and -continue writing. - -Metanotes can serve as entry points to groupings of individual notes. -They are not the same as a filtered list of files, i.e. what you would -do in Dired or the minibuffer where you narrow the list of notes to a -given query. Metanotes contain the filtered list plus your thoughts -about it. The act of purposefully grouping notes together and -contemplating on their shared patterns is what adds value. - -Your future self will appreciate metanotes for the function they serve -in encapsulating knowledge, while current you will be equipped with the -knowledge derived from the deliberate self-reflection. - -** Visiting linked files via the minibuffer -:PROPERTIES: -:CUSTOM_ID: h:1bc2adad-dca3-4878-b9f0-b105d5dec6f4 -:END: - -#+findex: denote-find-link -Denote has a major-mode-agnostic mechanism to collect all linked file -references in the current buffer and return them as an appropriately -formatted list. This list can then be used in interactive commands. -The ~denote-find-link~ is such a command. It uses minibuffer -completion to visit a file that is linked to from the current note. -The candidates have the correct metadata, which is ideal for -integration with other standards-compliant tools ([[#h:8ed2bb6f-b5be-4711-82e9-8bee5bb06ece][Extending Denote]]). -For instance, a package such as =marginalia= will display accurate -annotations, while the =embark= package will be able to work its magic -such as in exporting the list into a filtered Dired buffer (i.e. a -familiar Dired listing with only the files of the current minibuffer -session). - -#+findex: denote-find-backlink -To visit backlinks to the current note via the minibuffer, use -~denote-find-backlink~. This is an alternative to placing backlinks -in a dedicated buffer ([[#h:c73f1f68-e214-49d5-b369-e694f6a5d708][The backlinks' buffer]]). - -** Convert =denote:= links to =file:= links -:PROPERTIES: -:CUSTOM_ID: h:ed220cac-7dcb-4bb7-9243-1bb85e452e5f -:END: - -Sometimes the user needs to translate all =denote:= link types to -their =file:= equivalent. This may be because some other tool does not -recognise =denote:= links (or other custom links types---which are a -standard feature of Org, by the way). The user thus needs to (i) -either make a copy of their Denote note or edit the existing one, and -(ii) convert all links to the generic =file:= link type that -external/other programs understand. - -The optional extension =denote-org-extras.el= contains two commands -that are relevant for this use-case: - -#+findex: denote-org-extras-convert-links-to-file-type -+ Convert =denote:= links to =file:= links :: The command ~denote-org-extras-convert-links-to-file-type~ - goes through the buffer to find all =denote:= links. It gets the - identifier of the link and resolves it to the actual file system - path. It then replaces the match so that the link is written with - the =file:= type and then the file system path. The optional search - terms and/or link description are preserved ([[#h:fc1ad245-ec08-41be-8d1e-7153d99daf02][Insert link to an Org file with a further pointer to a heading]]). - -#+findex: denote-org-extras-convert-links-to-denote-type -+ Convert =file:= links to =denote:= links :: The command ~denote-org-extras-convert-links-to-denote-type~ - behaves like the one above. The difference is that it finds the file - system path and converts it into its identifier. - -** Miscellaneous information about links -:PROPERTIES: -:CUSTOM_ID: h:dd8f2231-8d77-49b9-acc4-af525c68b271 -:END: - -*** Aliases for the linking commands -:PROPERTIES: -:CUSTOM_ID: h:078856d9-f608-43a8-be84-f2cad4c27d0e -:END: - -#+findex: denote-insert-link -#+findex: denote-show-backlinks-buffer -#+findex: denote-link-insert-links-matching-regexp -For convenience, the ~denote-link~ command has an alias called -~denote-insert-link~. The ~denote-backlinks~ can also be used as -~denote-show-backlinks-buffer~. While ~denote-add-links~ is -aliased ~denote-link-insert-links-matching-regexp~. The purpose of -these aliases is to offer alternative, more descriptive names of -select commands. - -*** The ~denote-link-description-function~ to format links -:PROPERTIES: -:CUSTOM_ID: h:f634427c-b451-40e2-993e-e00ac627af68 -:END: - -#+vindex: denote-link-description-function -The user option ~denote-link-description-function~ takes as its value -the symbol of a function. This is used to format the text of the link. -The default function inserts the title. If the file has a signature, -it includes that as well, prepending it to the title. - -The function specified accepts a single =FILE= argument and returns -the description as a string. - -* Choose which commands to prompt for -:PROPERTIES: -:CUSTOM_ID: h:98c732ac-da0e-4ebd-a0e3-5c47f9075e51 -:END: - -#+vindex: denote-commands-for-new-notes -The user option ~denote-commands-for-new-notes~ specifies a list of -commands that are available at the ~denote-command-prompt~. This -prompt is used by Denote commands that ask the user how to create a -new note, as described elsewhere in this manual: - -- [[#h:ad91ca39-cf10-4e16-b224-fdf78f093883][Open an existing note or create it if missing]] -- [[#h:b6056e6b-93df-4e6b-a778-eebd105bac46][Link to a note or create it if missing]] - -The default value includes all the basic file-creating commands -([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). Users may customise this value if (i) they only -want to see fewer options and/or (ii) wish to include their own custom -command in the list ([[#h:11946562-7eb0-4925-a3b5-92d75f1f5895][Write your own convenience commands]]). - -* Fontification in Dired -:PROPERTIES: -:CUSTOM_ID: h:337f9cf0-9f66-45af-b73f-f6370472fb51 -:END: - -#+findex: denote-dired-mode -One of the upsides of Denote's file-naming scheme is the predictable -pattern it establishes, which appears as a near-tabular presentation in -a listing of notes (i.e. in Dired). The ~denote-dired-mode~ can help -enhance this impression, by fontifying the components of the file name -to make the date (identifier) and keywords stand out. - -There are two ways to set the mode. Either use it for all directories, -which probably is not needed: - -#+begin_src emacs-lisp -(add-hook 'dired-mode-hook #'denote-dired-mode) -#+end_src - -#+vindex: denote-dired-directories -#+findex: denote-dired-mode-in-directories -Or configure the user option ~denote-dired-directories~ and then set up -the function ~denote-dired-mode-in-directories~: - -#+begin_src emacs-lisp -;; We use different ways to specify a path for demo purposes. -(setq denote-dired-directories - (list denote-directory - (thread-last denote-directory (expand-file-name "attachments")) - (expand-file-name "~/Documents/vlog"))) - -(add-hook 'dired-mode-hook #'denote-dired-mode-in-directories) -#+end_src - -#+vindex: denote-dired-directories-include-subdirectories -The user option ~denote-dired-directories-include-subdirectories~ -specifies whether the ~denote-dired-directories~ also cover their -subdirectories. By default they do not. Set this option to ~t~ to -include subdirectories as well. - -The faces we define for this purpose are: - -#+vindex: denote-faces-date -#+vindex: denote-faces-delimiter -#+vindex: denote-faces-extension -#+vindex: denote-faces-keywords -#+vindex: denote-faces-signature -#+vindex: denote-faces-subdirectory -#+vindex: denote-faces-time -#+vindex: denote-faces-title -+ ~denote-faces-date~ -+ ~denote-faces-delimiter~ -+ ~denote-faces-extension~ -+ ~denote-faces-keywords~ -- ~denote-faces-signature~ -+ ~denote-faces-subdirectory~ -+ ~denote-faces-time~ -+ ~denote-faces-title~ - -For more control, we also provide these: - -#+vindex denote-faces-year -#+vindex denote-faces-month -#+vindex denote-faces-day -#+vindex denote-faces-hour -#+vindex denote-faces-minute -#+vindex denote-faces-second -+ ~denote-faces-year~ -+ ~denote-faces-month~ -+ ~denote-faces-day~ -+ ~denote-faces-hour~ -+ ~denote-faces-minute~ -+ ~denote-faces-second~ - -For the time being, the =diredfl= package is not compatible with this -facility. - -The ~denote-dired-mode~ does not only fontify note files that were -created by Denote: it covers every file name that follows our naming -conventions ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). This is particularly useful for -scenaria where, say, one wants to organise their collection of PDFs and -multimedia in a systematic way (and, perhaps, use them as attachments -for the notes Denote produces if you are writing Org notes and are using -its standand attachments' facility). - -* Automatically rename Denote buffers -:PROPERTIES: -:CUSTOM_ID: h:3ca4db16-8f26-4d7d-b748-bac48ae32d69 -:END: - -#+findex: denote-rename-buffer-mode -The minor mode ~denote-rename-buffer-mode~ provides the means to -automatically rename the buffer of a Denote file upon visiting the -file. This applies both to existing Denote files as well as new ones -([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). Enable the mode thus: - -#+begin_src emacs-lisp -(denote-rename-buffer-mode 1) -#+end_src - -#+vindex: denote-rename-buffer-function -#+findex: denote-rename-buffer -#+vindex: denote-rename-buffer-format -Buffers are named by applying the function specified in the user -option ~denote-rename-buffer-function~. The default function is -~denote-rename-buffer~: it renames the buffer based on the template -set in the user option ~denote-rename-buffer-format~. By default, the -formatting template targets only the =TITLE= component of the file -name ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). Other fields are explained elsewhere in -this manual ([[#h:35507c18-35b1-41b9-9d80-52f54fcef3cb][The denote-rename-buffer-format]]). - -Note that renaming a buffer is not the same as renaming a file -([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]). The former is just for convenience inside of Emacs. -Whereas the latter is for writing changes to disk, making them -available to all programs. - -** The ~denote-rename-buffer-format~ option -:PROPERTIES: -:CUSTOM_ID: h:35507c18-35b1-41b9-9d80-52f54fcef3cb -:END: - -The user option ~denote-rename-buffer-format~ controls how the -function ~denote-rename-buffer~ chooses the name of the -buffer-to-be-renamed. - -The value of this user option is a string. The following specifiers -are placeholders for Denote file name components ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]): - -- The =%t= is the Denote =TITLE= of the file. -- The =%i= is the Denote =IDENTIFIER= of the file. -- The =%d= is the same as =%i= (=DATE= mnemonic). -- The =%s= is the Denote =SIGNATURE= of the file. -- The =%k= is the Denote =KEYWORDS= of the file. -- The =%%= is a literal percent sign. - -In addition, the following flags are available for each of the specifiers: - -- =0= :: Pad to the width, if given, with zeros instead of spaces. -- =-= :: Pad to the width, if given, on the right instead of the left. -- =<= :: Truncate to the width and precision, if given, on the left. -- =>= :: Truncate to the width and precision, if given, on the right. -- =^= :: Convert to upper case. -- =_= :: Convert to lower case. - -When combined all together, the above are written thus: - -: %SPECIFIER-CHARACTER - -Any other string it taken as-is. Users may want, for example, to -include some text that makes Denote buffers stand out, such as -a =[D]= prefix. Examples: - -#+begin_src emacs-lisp -;; Use the title (default) -(setq denote-rename-buffer-format "%t") - -;; Use the title and keywords with some emoji in between -(setq denote-rename-buffer-format "%t 🤨 %k") - -;; Use the title with a literal "[D]" before it -(setq denote-rename-buffer-format "[D] %t") -#+end_src - -Users who need yet more flexibility are best served by writing their -own function and assigning it to the ~denote-rename-buffer-function~. - -* Use Org dynamic blocks -:PROPERTIES: -:CUSTOM_ID: h:8b542c50-dcc9-4bca-8037-a36599b22779 -:END: - -[ As part of version 2.3.0, all dynamic blocks are defined in the file - =denote-org-extras.el=. The file which was once called - =denote-org-dblock.el= contains aliases for the new function names - and dipslays a warning about its deprecation. There is no need to - ~require~ the ~denote-org-extras~ feature because all of Denote's - Org dynamic blocks are autoloaded (meaning that they work as soon as - they are used). For backward compatibility, all dynamic blocks - retain their original names as an alias for the newer one. ] - -Denote can optionally integrate with Org mode's "dynamic blocks" -facility. This means that it can use special blocks that are evaluated -with =C-c C-x C-u= (~org-dblock-update~) to generate their contents. -The following subsections describe the types of Org dynamic blocks -provided by Denote. - -- [[#h:50160fae-6515-4d7d-9737-995ad925e64b][Org dynamic blocks to insert links or backlinks]] -- [[#h:f15fa143-5036-416f-9bff-1bcabbb03456][Org dynamic block to insert file contents]] - -A dynamic block gets its contents by evaluating a function that -corresponds to the type of block. The block type and its parameters -are stated in the opening =#+BEGIN= line. Typing =C-c C-x C-u= -(~org-dblock-update~) with point on that line runs (or re-runs) the -associated function with the given parameters and populates the -block's contents accordingly. - -Dynamic blocks are particularly useful for metanote entries that -reflect on the status of earlier notes ([[#h:6060a7e6-f179-4d42-a9de-a9968aaebecc][Writing metanotes]]). - -The Org manual describes the technicalities of Dynamic Blocks. -Evaluate: - -#+begin_src emacs-lisp -(info "(org) Dynamic Blocks") -#+end_src - -** Org dynamic blocks to insert links or backlinks -:PROPERTIES: -:CUSTOM_ID: h:50160fae-6515-4d7d-9737-995ad925e64b -:END: - -[ As part of version 2.3.0, all dynamic blocks are defined in the file - =denote-org-extras.el=. The file which was once called - =denote-org-dblock.el= contains aliases for the new function names - and dipslays a warning about its deprecation. There is no need to - ~require~ the ~denote-org-extras~ feature because all of Denote's - Org dynamic blocks are autoloaded (meaning that they work as soon as - they are used). For backward compatibility, all dynamic blocks - retain their original names as an alias for the newer one. ] - -#+findex: denote-org-extras-dblock-insert-links -The =denote-links= block can be inserted at point with the command -~denote-org-extras-dblock-insert-links~ or by manually including the -following in an Org file: - -: #+BEGIN: denote-links :regexp "YOUR REGEXP HERE" :sort-by-component nil :reverse-sort nil :id-only nil -: -: #+END: - -The =denote-links= block is also registered as an option for the -command ~org-dynamic-block-insert-dblock~. - -Type =C-c C-x C-u= (~org-dblock-update~) with point on the =#+BEGIN= -line to update the block. - -- The =:regexp= parameter is mandatory. Its value is a string and its - behaviour is the same as that of the ~denote-add-links~ command - ([[#h:9bec2c83-36ca-4951-aefc-7187c5463f90][Insert links matching a regexp]]). Concretely, it produces a - typographic list of links to files matching the giving regular - expression. The value of the =:regexp= parameter may also be of the - form read by the ~rx~ macro (Lisp notation instead of a string), as - explained in the Emacs Lisp Reference Manual (evaluate this code to - read the documentation: =(info "(elisp) Rx Notation")=). Note that - you do not need to write an actual regular expression to get - meaningful results: even something like =_journal= will work to - include all files that have a =journal= keyword. - -- The =:sort-by-component= parameter is optional. It sorts the files - by the given Denote file name component. The value it accepts is an - unquoted symbol among =title=, =keywords=, =signature=, =identifier=. - When using the command ~denote-org-extras-dblock-insert-files~, this - parameter is automatically inserted together with the (=:regexp= - parameter) and the user is prompted for a file name component. - -- The =:reverse-sort= parameter is optional. It reverses the order in - which files appear in. This is meaningful even without the presence - of the parameter =:sort-by-component=, though it also combines with - it. - -- The =:id-only= parameter is optional. It accepts a ~t~ value, in - which case links are inserted without a description text but only - with the identifier of the given file. This has the same meaning as - with the ~denote-link~ command and related facilities ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]). - -- An optional =:block-name= parameter can be specified with a string - value to add a =#+name= to the results. This is useful for further - processing using Org facilities (a feature that is outside Denote's - purview). - -#+findex: denote-org-extras-dblock-insert-backlinks -The same as above except for the =:regexp= parameter are true for the -=denote-backlinks= block. The block can be inserted at point with the -command ~denote-org-extras-dblock-insert-backlinks~ or by manually writing -this in an Org file: - -: #+BEGIN: denote-backlinks :sort-by-component nil :reverse-sort nil :id-only nil -: -: #+END: - -#+findex: denote-org-extras-dblock-insert-missing-links -Finally, the =denote-missing-links= block is available with the -command ~denote-org-extras-dblock-insert-missing-links~. It is like -the aforementioned =denote-links= block, except it only lists links to -files that are not present in the current buffer. The parameters are -otherwise the same: - -: #+BEGIN: denote-missing-links :regexp "YOUR REGEXP HERE" :sort-by-component nil :reverse-sort nil :id-only nil -: -: #+END: - -Remember to type =C-c C-x C-u= (~org-dblock-update~) with point on the -=#+BEGIN= line to update the block. - -** Org dynamic block to insert file contents -:PROPERTIES: -:CUSTOM_ID: h:f15fa143-5036-416f-9bff-1bcabbb03456 -:END: - -[ As part of version 2.3.0, all dynamic blocks are defined in the file - =denote-org-extras.el=. The file which was once called - =denote-org-dblock.el= contains aliases for the new function names - and dipslays a warning about its deprecation. There is no need to - ~require~ the ~denote-org-extras~ feature because all of Denote's - Org dynamic blocks are autoloaded (meaning that they work as soon as - they are used). For backward compatibility, all dynamic blocks - retain their original names as an alias for the newer one. ] - -Denote can optionally use Org's dynamic blocks facility to produce a -section that lists entire file contents ([[#h:8b542c50-dcc9-4bca-8037-a36599b22779][Use Org dynamic blocks]]). -This works by instructing Org to match a regular expression of Denote -files, the same way we do with Denote links ([[#h:9bec2c83-36ca-4951-aefc-7187c5463f90][Insert links matching a regexp]]). - -This is useful to, for example, compile a dynamically concatenated -list of scattered thoughts on a given topic, like =^2023.*_emacs= for -a long entry that incorporates all the notes written in 2023 with the -keyword =emacs=. - -#+findex: denote-org-extras-dblock-insert-files -To produce such a block, call the command ~denote-org-extras-dblock-insert-files~ -or manually write the following block in an Org file and then type - =C-c C-x C-u= (~org-dblock-update~) on the =#+BEGIN= line to run it -(do it again to recalculate the block): - -: #+BEGIN: denote-files :regexp "YOUR REGEXP HERE" -: -: #+END: - -To fully control the output, include these additional optional -parameters, which are described further below: - -: #+BEGIN: denote-files :regexp "YOUR REGEXP HERE" :sort-by-component nil :reverse-sort nil :no-front-matter nil :file-separator nil :add-links nil -: -: #+END: - -- The =:regexp= parameter is mandatory. Its value is a string, - representing a regular expression to match Denote file names. Its - value may also be an ~rx~ expression instead of a string, as noted - in the previous section ([[#h:50160fae-6515-4d7d-9737-995ad925e64b][Org dynamic blocks to insert links or backlinks]]). - Note that you do not need to write an actual regular expression to - get meaningful results: even something like =_journal= will work to - include all files that have a =journal= keyword. - -- The =:sort-by-component= parameter is optional. It sorts the files - by the given Denote file name component. The value it accepts is an - unquoted symbol among =title=, =keywords=, =signature=, =identifier=. - When using the command ~denote-org-extras-dblock-insert-files~, this - parameter is automatically inserted together with the (=:regexp= - parameter) and the user is prompted for a file name component. - -- The =:reverse-sort= parameter is optional. It reverses the order in - which files appear in. This is meaningful even without the presence - of the parameter =:sort-by-component=, though it also combines with - it. - -#+vindex: denote-org-extras-dblock-file-contents-separator -- The =:file-separator= parameter is optional. If it is omitted, then - Denote will use no separator between the files it inserts. If the - value is ~t~ the ~denote-org-extras-dblock-file-contents-separator~ is - applied at the end of each file: it introduces some empty lines and - a horizontal rule between them to visually distinguish individual - files. If the =:file-separator= value is a string, it is used as the - file separator (e.g. use ="\n"= to insert just one empty new line). - -- The =:no-front-matter= parameter is optional. When set to a ~t~ - value, Denote tries to remove front matter from the files it is - inserting in the dynamic block. The technique used to perform this - operation is by removing all lines from the top of the file until - the first empty line. This works with the default front matter that - Denote adds, but is not 100% reliable with all sorts of user-level - modifications and edits to the file. When the =:no-front-matter= is - set to a natural number, Denote will omit that many lines from the - top of the file. - -- The =:add-links= parameter is optional. When it is set to a ~t~ - value, all files are inserted as a typographic list and are indented - accordingly. The first line in each list item is a link to the file - whose contents are inserted in the following lines. When the value - is =id-only=, then links are inserted without a description text but - only with the identifier of the given file. This has the same - meaning as with the ~denote-link~ command and related facilities - ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]). Remember that Org can fold the items in a - typographic list the same way it does with headings. So even long - files can be presented in this format without much trouble. - -- An optional =:block-name= parameter can be specified with a string - value to add a =#+name= to the results. This is useful for further - processing using Org facilities (a feature that is outside Denote's - purview). - -* Sort files by component -:PROPERTIES: -:CUSTOM_ID: h:9fe01e63-f34f-4479-8713-f162a5ca865e -:END: - -The =denote-sort.el= file is an optional extension to the core -functionality of Denote, which empowers users to sort files by the -given file name component ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). - -#+findex: denote-sort-dired -The command ~denote-sort-dired~ produces a Dired file listing with a -flat, filtered, and sorted set of files from the ~denote-directory~. -It does so by means of three minibuffer prompts: - -1. It first asks for a regular expression with which to match Denote - files. Remember that due to Denote's efficient file-naming scheme, - you do not need to write some complex regular expression. Even - something like =_journal= will match only files with a =journal= - keyword. -2. Once the regular expression is provided, the command asks for a - Denote file name component to sort files by. This is a symbol among - =title=, =keywords=, =signature=, and =identifier=. -3. Finally, it asks a "yes or no" on whether to reverse the sort - order. - -The resulting Dired listing is a regular Dired buffer, unlike that of -~dired-virtual-mode~ ([[#h:d35d8d41-f51b-4139-af8f-9c8cc508e35b][Use ~dired-virtual-mode~ for arbitrary file listings]]). - -The dynamic Org blocks that Denote defines to insert file contents -also use this feature ([[#h:f15fa143-5036-416f-9bff-1bcabbb03456][Org dynamic block to insert file contents]]). - -DEVELOPMENT NOTE as of 2023-11-30 07:24 +0200: The sort mechanism will -be incorporated in more functions on a case-by-case basis, subject to -user feedback. For the time being, I am not documenting the functions -that are only accessed from Lisp. Do not hesitate to contact me -(Protesilaos) in person or on one of the development sources (mailing -list or GitHub/GitLab mirror) if you have a use-case where sorting -seems useful. I am happy to help but do not want to roll this feature -everywhere before eliciting relevant feedback: once we add it, we are -not going back. - -* Keep a journal or diary -:PROPERTIES: -:CUSTOM_ID: h:4a6d92dd-19eb-4fcc-a7b5-05ce04da3a92 -:END: - -Denote provides a general-purpose mechanism to create new files that -broadly count as "notes" ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). Such files can be daily -entries in a journal. While it is possible to use the generic -~denote~ command to maintain a journal, we provide an optional set of -convenience options and commands as part of =denote-journal-extras.el=. -To use those, add the following the Denote configuration: - -#+begin_src emacs-lisp -(require 'denote-journal-extras) -#+end_src - -#+findex: denote-journal-extras-new-entry -#+vindex: denote-journal-extras-keyword -#+vindex: denote-journal-extras-directory -The command ~denote-journal-extras-new-entry~ creates a new entry in -the journal. Such a file has the ~denote-journal-extras-keyword~, -which is =journal= by default ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). The user can -set this keyword to an arbitrary string (single word is preferred). -New journal entries can be stored in the ~denote-directory~ or -subdirectory thereof. To make it easier for the user, the new journal -entry will be placed in ~denote-journal-extras-directory~, which -defaults to a subdirectory of ~denote-directory~ called =journal=. - -If ~denote-journal-extras-directory~ is nil, the ~denote-directory~ is -used. Journal entries will thus be in a flat listing together with -all other notes. They can still be retrieved easily by searching for -the ~denote-journal-extras-keyword~ ([[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]). - -#+vindex: denote-journal-extras-title-format -Furthermore, the command ~denote-journal-extras-new-entry~ will use -the current date as the title of the new entry. The exact format is -controlled by the user option ~denote-journal-extras-title-format~. -Acceptable values for ~denote-journal-extras-title-format~ and their -corresponding styles are: - -| Symbol | Style | -|-------------------------+-----------------------------------| -| day | Monday | -| day-date-month-year | Monday 19 September 2023 | -| day-date-month-year-24h | Monday 19 September 2023 20:49 | -| day-date-month-year-12h | Monday 19 September 2023 08:49 PM | - -For example: - -#+begin_src emacs-lisp -(setq denote-journal-extras-title-format 'day-date-month-year) -#+end_src - -If the value of this user option is ~nil~, then -~denote-journal-extras-new-entry~ will prompt for a title. - -The ~denote-journal-extras-new-entry~ command also accepts an optional -=DATE= argument. When called internactively, this is a universal -prefix (e.g. =C-u= with the default key bindings). With =DATE=, it -prompts for a date to create a new journal entry for. The date prompt -can optionally use the Org date+calendar selection interface -([[#h:e7ef08d6-af1b-4ab3-bb00-494a653e6d63][The ~denote-date-prompt-use-org-read-date~ option]]). - -In terms of workflow, using the current date as the title is better -for maintaining a daily journal. A prompt for an arbitrary title is -more suitable for those who like to keep a record of something like a -thought or event (though this can also be achieved by the regular -~denote~ command or maybe ~denote-subdirectory~). - -#+vindex: denote-journal-extras-hook -The ~denote-journal-extras-new-entry~ command calls the normal hook -~denote-journal-extras-hook~ after it is done. The user can leverage -this to produce consequences therefrom, such as to set a timer with -the ~tmr~ package from GNU ELPA ([[#h:4af1f81e-e93a-43cc-b344-960032a16d42][Journaling with a timer]]). - -#+findex: denote-journal-extras-new-or-existing-entry -The command ~denote-journal-extras-new-or-existing-entry~ locates an -existing journal entry or creates a new one. A journal entry is one -that has ~denote-journal-extras-keyword~ as part of its file name. If -there are multiple journal entries for the current date, it prompts -for one among them using minibuffer completion. If there is only one, -it visits it outright. If there is no journal entry, it creates one -by calling ~denote-journal-extra-new-entry~ (as described above). - -#+findex: denote-journal-extras-link-or-create-entry -The command ~denote-journal-extras-link-or-create-entry~ links to the -journal entry for today or creates it in the background, if missing, -and then links to it from the current file. If there are multiple -journal entries for the same day, it prompts to select one among them -and then links to it. When called with an optional prefix argument -(such as =C-u= with default key bindings), the command prompts for a -date and then performs the aforementioned. With a double prefix -argument (=C-u C-u=), it also produces a link whose description -includes just the file's identifier. - -** Journaling with a timer -:PROPERTIES: -:CUSTOM_ID: h:4af1f81e-e93a-43cc-b344-960032a16d42 -:END: - -[ Revised as part of version 2.1.0 to conform with how we - now tend to the needs of users who use Denote for journaling - purposes ([[#h:4a6d92dd-19eb-4fcc-a7b5-05ce04da3a92][Keep a journal or diary]]). ] - -Sometimes journaling is done with the intent to hone one's writing -skills. Perhaps you are learning a new language or wish to communicate -your ideas with greater clarity and precision. As with everything that -requires a degree of sophistication, you have to work for it---write, -write, write! - -One way to test your progress is to set a timer. It helps you gauge -your output and its quality. To use a timer with Emacs, consider the -~tmr~ package. A new timer can be set with something like this: - -#+begin_src emacs-lisp -;; Set 10 minute timer with the given description -(tmr "10" "Practice writing in my journal") -#+end_src - -To make this timer start as soon as a new journal entry is created -with the command ~denote-journal-extras-new-entry~, add a function to -the ~denote-journal-extras-hook~. For example: - -#+begin_src emacs-lisp -;; Add an anonymous function, which is more difficult to modify after -;; the fact: -(add-hook 'denote-journal-extras-hook (lambda () - (tmr "10" "Practice writing in my journal"))) - -;; Or write a small function that you can then modify without -;; revaluating the hook: -(defun my-denote-tmr () - (tmr "10" "Practice writing in my journal")) - -(add-hook 'denote-journal-extras-hook 'my-denote-tmr) - -;; Or to make it fully featured, define variables for the duration and -;; the description and set it up so that you only need to modify -;; those: -(defvar my-denote-tmr-duration "10") - -(defvar my-denote-tmr-description "Practice writing in my journal") - -(defun my-denote-tmr () - (tmr my-denote-tmr-duration my-denote-tmr-description)) - -(add-hook 'denote-journal-extras-hook 'my-denote-tmr) -#+end_src - -Once the timer elapses, stop writing and review your performance. -Practice makes perfect! - -Sources for ~tmr~: - -+ Package name (GNU ELPA): ~tmr~ -+ Official manual: -+ Change log: -+ Git repo on SourceHut: - - Mirrors: - + GitHub: - + GitLab: -+ Mailing list: - -* Minibuffer histories -:PROPERTIES: -:CUSTOM_ID: h:82dc1203-d689-44b2-9a6c-b37776209651 -:END: - -Denote has a dedicated minibuffer history for each one of its prompts. -This practically means that using =M-p= (~previous-history-element~) and -=M-n= (~next-history-element~) will only cycle through the relevant -record of inputs, such as your latest titles in the =TITLE= prompt, and -keywords in the =KEYWORDS= prompt. - -The built-in =savehist= library saves minibuffer histories. Sample -configuration: - -#+begin_src emacs-lisp -(require 'savehist) -(setq savehist-file (locate-user-emacs-file "savehist")) -(setq history-length 500) -(setq history-delete-duplicates t) -(setq savehist-save-minibuffer-history t) -(add-hook 'after-init-hook #'savehist-mode) -#+end_src - -* Extending Denote -:PROPERTIES: -:CUSTOM_ID: h:8ed2bb6f-b5be-4711-82e9-8bee5bb06ece -:END: - -Denote is a tool with a narrow scope: create notes and link between -them, based on the aforementioned file-naming scheme. For other common -operations the user is advised to rely on standard Emacs facilities or -specialised third-party packages. This section covers the details. - -** Create a new note in any directory -:PROPERTIES: -:CUSTOM_ID: h:c9d7c157-85a6-4bb7-bd21-d00bccf5e799 -:END: - -The commands that create new files are designed to write to the -~denote-directory~. The idea is that the linking mechanism can find -any file by its identifier if it is in the ~denote-directory~ -(searching the entire file system would be cumbersome). - -However, these are cases where the user needs to create a new note in -an arbitrary directory. The following command can do this. Put the -code in your configuration file and evaluate it. Then call the command -by its name with =M-x=. - -#+begin_src emacs-lisp -(defun my-denote-create-note-in-any-directory () - "Create new Denote note in any directory. -Prompt for the directory using minibuffer completion." - (declare (interactive-only t)) - (interactive) - (let ((denote-directory (read-directory-name "New note in: " nil nil :must-match))) - (call-interactively 'denote))) -#+end_src - -** Narrow the list of files in Dired -:PROPERTIES: -:CUSTOM_ID: h:ea173a01-69ef-4574-89a7-6e60ede02f13 -:END: - -Emacs' standard file manager (or directory editor) can read a regular -expression to mark the matching files. This is the command -~dired-mark-files-regexp~, which is bound to =% m= by default. For -example, =% m _denote= will match all files that have the =denote= -keyword ([[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]). - -Once the files are matched, the user has two options: (i) narrow the -list to the matching items or (ii) exclude the matching items from the -list. - -For the former, we want to toggle the marks by typing =t= (calls the -command ~dired-toggle-marks~ by default) and then hit the letter =k= -(for ~dired-do-kill-lines~). The remaining files are those that match -the regexp that was provided earlier. - -For the latter approach of filtering out the matching items, simply -involves the use of the =k= command (~dired-do-kill-lines~) to omit the -marked files from the list. - -These sequences can be combined to incrementally narrow the list. Note -that ~dired-do-kill-lines~ does not delete files: it simply hides them -from the current view. - -Revert to the original listing with =g= (~revert-buffer~). - -For a convenient wrapper, consider this example: - -#+begin_src emacs-lisp -(defvar prot-dired--limit-hist '() - "Minibuffer history for `prot-dired-limit-regexp'.") - -;;;###autoload -(defun prot-dired-limit-regexp (regexp omit) - "Limit Dired to keep files matching REGEXP. - -With optional OMIT argument as a prefix (\\[universal-argument]), -exclude files matching REGEXP. - -Restore the buffer with \\`\\[revert-buffer]'." - (interactive - (list - (read-regexp - (concat "Files " - (when current-prefix-arg - (propertize "NOT " 'face 'warning)) - "matching PATTERN: ") - nil 'prot-dired--limit-hist) - current-prefix-arg)) - (dired-mark-files-regexp regexp) - (unless omit (dired-toggle-marks)) - (dired-do-kill-lines)) -#+end_src - -** Use ~dired-virtual-mode~ for arbitrary file listings -:PROPERTIES: -:CUSTOM_ID: h:d35d8d41-f51b-4139-af8f-9c8cc508e35b -:END: - -Emacs' Dired is a powerful file manager that builds its functionality -on top of the Unix =ls= command. As noted elsewhere in this manual, -the user can update the =ls= flags that Dired uses to display its -contents ([[#h:a7fd5e0a-78f7-434e-aa2e-e150479c16e2][I want to sort by last modified, why won't Denote let me?]]). - -What Dired cannot do is parse the output of a result that is produced -by piped commands, such as =ls -l | sort -t _ -k2=. This specific -example targets the second underscore-separated field of the file -name, per our conventions ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). Conceretely, it -matches the "alpha" as the sorting key in something like this: - -#+begin_src emacs-lisp -20220929T200432--testing-file-one__alpha.txt -#+end_src - -Consider then, how Dired will sort those files by their identifier: - -#+begin_src emacs-lisp -20220929T200432--testing-file-one__alpha.txt -20220929T200532--testing-file-two__beta.txt -20220929T200632--testing-file-three__alpha.txt -20220929T200732--testing-file-four__beta.txt -#+end_src - -Whereas on the command line, we can get the following: - -#+begin_example -$ ls | sort -t _ -k 2 -20220929T200432--testing-file-one__alpha.txt -20220929T200632--testing-file-three__alpha.txt -20220929T200532--testing-file-two__beta.txt -20220929T200732--testing-file-four__beta.txt -#+end_example - -This is where ~dired-virtual-mode~ shows its utility. If we tweak our -command-line invocation to include =ls -l=, this mode can behave like -Dired on the listed files. (We omit the output of the =-l= flag from -this tutorial, as it is too verbose.) - -What we now need is to capture the output of =ls -l | sort -t _ -k 2= -in an Emacs buffer and then enable ~dired-virtual-mode~. To do that, -we can rely on either =M-x shell= or =M-x eshell= and then manually -copy the relevant contents. - -For the user's convenience, I share what I have for Eshell to quickly -capture the last command's output in a dedicated buffer: - -#+begin_src emacs-lisp -(defcustom prot-eshell-output-buffer "*Exported Eshell output*" - "Name of buffer with the last output of Eshell command. -Used by `prot-eshell-export'." - :type 'string - :group 'prot-eshell) - -(defcustom prot-eshell-output-delimiter "* * *" - "Delimiter for successive `prot-eshell-export' outputs. -This is formatted internally to have newline characters before -and after it." - :type 'string - :group 'prot-eshell) - -(defun prot-eshell--command-prompt-output () - "Capture last command prompt and its output." - (let ((beg (save-excursion - (goto-char (eshell-beginning-of-input)) - (goto-char (point-at-bol))))) - (when (derived-mode-p 'eshell-mode) - (buffer-substring-no-properties beg (eshell-end-of-output))))) - -;;;###autoload -(defun prot-eshell-export () - "Produce a buffer with output of the last Eshell command. -If `prot-eshell-output-buffer' does not exist, create it. Else -append to it, while separating multiple outputs with -`prot-eshell-output-delimiter'." - (interactive) - (let ((eshell-output (prot-eshell--command-prompt-output))) - (with-current-buffer (get-buffer-create prot-eshell-output-buffer) - (let ((inhibit-read-only t)) - (goto-char (point-max)) - (unless (eq (point-min) (point-max)) - (insert (format "\n%s\n\n" prot-eshell-output-delimiter))) - (goto-char (point-at-bol)) - (insert eshell-output) - (switch-to-buffer-other-window (current-buffer)))))) -#+end_src - -Bind ~prot-eshell-export~ to a key in the ~eshell-mode-map~ and give -it a try (I use =C-c C-e=). In the produced buffer, activate the -~dired-virtual-mode~. - -** Use Embark to collect minibuffer candidates -:PROPERTIES: -:CUSTOM_ID: h:edf9b651-86eb-4d5f-bade-3c9e270082f0 -:END: - -=embark= is a remarkable package that lets you perform relevant, -context-dependent actions using a prefix key (simplifying in the -interest of brevity). - -For our purposes, Embark can be used to produce a Dired listing -directly from the minibuffer. Suppose the current note has links to -three other notes. You might use the ~denote-find-link~ command to -pick one via the minibuffer. But why not turn those three links into -their own Dired listing? While in the minibuffer, invoke ~embark-act~ -which you may have already bound to =C-.= and then follow it up with -=E= (for the ~embark-export~ command). - -This pattern can be repeated with any list of candidates, meaning that -you can narrow the list by providing some input before eventually -exporting the results with Embark. - -Overall, this is very powerful and you might prefer it over doing the -same thing directly in Dired, since you also benefit from all the power -of the minibuffer ([[#h:ea173a01-69ef-4574-89a7-6e60ede02f13][Narrow the list of files in Dired]]). - -** Search file contents -:PROPERTIES: -:CUSTOM_ID: h:76198fab-d6d2-4c67-9ccb-7a08cc883952 -:END: - -Emacs provides built-in commands which are wrappers of standard Unix -tools: =M-x grep= lets the user input the flags of a ~grep~ call and -pass a regular expression to the =-e= flag. - -The author of Denote uses this thin wrapper instead: - -#+begin_src emacs-lisp -(defvar prot-search--grep-hist '() - "Input history of grep searches.") - -;;;###autoload -(defun prot-search-grep (regexp &optional recursive) - "Run grep for REGEXP. - -Search in the current directory using `lgrep'. With optional -prefix argument (\\[universal-argument]) for RECURSIVE, run a -search starting from the current directory with `rgrep'." - (interactive - (list - (read-from-minibuffer (concat (if current-prefix-arg - (propertize "Recursive" 'face 'warning) - "Local") - " grep for PATTERN: ") - nil nil nil 'prot-search--grep-hist) - current-prefix-arg)) - (unless grep-command - (grep-compute-defaults)) - (if recursive - (rgrep regexp "*" default-directory) - (lgrep regexp "*" default-directory))) -#+end_src - -Rather than maintain custom code, consider using the excellent =consult= -package: it provides commands such as ~consult-grep~ and ~consult-find~ -which provide live results and are generally easier to use than the -built-in commands. - -** Bookmark the directory with the notes -:PROPERTIES: -:CUSTOM_ID: h:1bba4c1e-6812-4749-948f-57df4fd49b36 -:END: - -Part of the reason Denote does not reinvent existing functionality is to -encourage you to learn more about Emacs. You do not need a bespoke -"jump to my notes" directory because such commands do not scale well. -Will you have a "jump to my downloads" then another for multimedia and -so on? No. - -Emacs has a built-in framework for recording persistent markers to -locations. Visit the ~denote-directory~ (or any dir/file for that -matter) and invoke the ~bookmark-set~ command (bound to =C-x r m= by -default). It lets you create a bookmark. - -The list of bookmarks can be reviewed with the ~bookmark-bmenu-list~ -command (bound to =C-x r l= by default). A minibuffer interface is -available with ~bookmark-jump~ (=C-x r b=). - -If you use the =consult= package, its default ~consult-buffer~ command -has the means to group together buffers, recent files, and bookmarks. -Each of those types can be narrowed to with a prefix key. The package -=consult-dir= is an extension to =consult= which provides useful extras -for working with directories, including bookmarks. - -** Use the ~denote-explore~ package to explore your notes -:PROPERTIES: -:CUSTOM_ID: h:110ae3a4-5fc6-45da-b9bb-0e294bd12981 -:END: - -Peter Prevos has developed the ~denote-explore~ package which provides -four groups of Emacs commands to explore your Denote files: - -- Summary statistics :: Count notes, attachments and keywords. -- Random walks :: Generate new ideas using serendipity. -- Janitor :: Manage your denote collection. -- Visualisations :: Visualise your Denote network. - -The package's documentation covers the details: -. - -** Use the ~citar-denote~ package for bibliography notes -:PROPERTIES: -:CUSTOM_ID: h:226d66e4-b7de-4617-87e2-a7f2d6f007dd -:END: - -Peter Prevos has produced the ~citar-denote~ package which makes it -possible to write notes on BibTeX entries with the help of the ~citar~ -package. These notes have the citation's unique key associated with -them in the file's front matter. They also get a configurable keyword -in their file name, making it easy to find them in Dired and/or -retrieve them with the various Denote methods. - -With ~citar-denote~, the user leverages standard minibuffer completion -mechanisms (e.g. with the help of the ~vertico~ and ~embark~ packages) -to manage bibliographic notes and access those notes with ease. The -package's documentation covers the details: . - -** Use the ~consult-notes~ package -:PROPERTIES: -:CUSTOM_ID: h:8907f4bc-992a-45bc-a60e-267ed1ce9c2d -:END: - -If you are using Daniel Mendler's ~consult~ (which is a brilliant -package), you will most probably like its ~consult-notes~ extension, -developed by Colin McLear. It uses the familiar mechanisms of Consult -to preview the currently selected entry and to filter searches via a -prefix key. For example: - -#+begin_src emacs-lisp -(setq consult-notes-file-dir-sources - `(("Denote Notes" ?d ,(denote-directory)) - ("Books" ?b "~/Documents/books/"))) -#+end_src - -With the above, =M-x consult-notes= will list the files in those two -directories. If you type =d= and space, it narrows the list to just -the notes, while =b= does the same for books. - -The other approach is to enable the ~consult-notes-denote-mode~. It -takes care to add the ~denote-directory~ to the sources that -~consult-notes~ reads from. Denote notes are then filtered by the =d= -prefix followed by a space. - -The minor mode has the extra feature of reformatting the title of -notes shown in the minibuffer. It isolates the =TITLE= component of -each note and shows it without hyphens, while presenting keywords in -their own column. The user option ~consult-notes-denote-display-id~ -can be set to ~nil~ to hide the identifier. Depending on how one -searches through their notes, this refashioned presentation may be the -best option ([[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]). - -** Use the ~denote-menu~ package -:PROPERTIES: -:CUSTOM_ID: h:472db709-27de-4a1f-a171-c3fe0a7a9be8 -:END: - -Denote's file-naming scheme is designed to be efficient and to provide -valueable meta information about the file. The cost, however, is that -it is terse and harder to read, depending on how the user chooses to -filter and process their notes. - -To this end, [[https://github.com/namilus/denote-menu][the ~denote-menu~ package by Mohamed Suliman]] provides the -convenience of a nice tabular interface for all notes. ~denote-menu~ -removes the delimiters that are found in Denote file names and -presents the information in a human-readable format. Furthermore, the -package provides commands to interact with the list of notes, such as -to filter them and to transition from the tabular list to Dired. Its -documentation expands on the technicalities. - -** Treat your notes as a project -:PROPERTIES: -:CUSTOM_ID: h:fad3eb08-ddc7-43e4-ba28-210d89668037 -:END: - -Emacs has a built-in library for treating a directory tree as a -"project". This means that the contents of this tree are seen as part -of the same set, so commands like ~project-switch-to-buffer~ (=C-x p b= -by default) will only consider buffers in the current project -(e.g. three notes that are currently being visited). - -Normally, a "project" is a directory tree whose root is under version -control. For our purposes, all you need is to navigate to the -~denote-directory~ (for the shell or via Dired) and use the command-line -to run this (requires the =git= executable): - -: git init - -From Dired, you can type =M-!= which invokes ~dired-smart-shell-command~ -and then run the git call there. - -The project can then be registered by invoking any project-related -command inside of it, such as ~project-find-file~ (=C-x p f=). - -It is a good idea to keep your notes under version control, as that -gives you a history of changes for each file. We shall not delve into -the technicalities here, though suffice to note that Emacs' built-in -version control framework or the exceptionally well-crafted =magit= -package will get the job done (VC can work with other backends besides -Git). - -** Use the tree-based file prompt for select commands -:PROPERTIES: -:CUSTOM_ID: h:8f9e0971-8b30-4e7b-af79-8fed257dbcfa -:END: - -Older versions of Denote had a file prompt that resembled that of the -standard ~find-file~ command (bound to =C-x C-f= by default). This -means that it used a tree-based method of navigating the filesystem by -selecting the specific directory and then the given file. - -Currently, Denote flattens the file prompt so that every file in the -~denote-directory~ and its subdirectories can be matched from anywhere -using the power of Emacs' minibuffer completion (such as with the help -of the ~orderless~ package in addition to built-in options). - -Users who need the old behaviour on a per-command basis can define -their own wrapper functions as shown in the following code block. - -#+begin_src emacs-lisp -;; This is the old `denote-file-prompt' that we renamed to -;; `denote-file-prompt-original' for clarity. -(defun denote-file-prompt-original (&optional initial-text) - "Prompt for file with identifier in variable `denote-directory'. -With optional INITIAL-TEXT, use it to prepopulate the minibuffer." - (read-file-name "Select note: " (denote-directory) nil nil initial-text - (lambda (f) - (or (denote-file-has-identifier-p f) - (file-directory-p f))))) - -;; Our wrapper command that changes the current `denote-file-prompt' -;; to the functionality of `denote-file-prompt-original' only when -;; this command is used. -(defun my-denote-link () - "Call `denote-link' but use Denote's original file prompt. -See `denote-file-prompt-original'." - (interactive) - (cl-letf (((symbol-function 'denote-file-prompt) #'denote-file-prompt-original)) - (call-interactively #'denote-link))) -#+end_src - -** Rename files with Denote in the Image Dired thumbnails buffer -:PROPERTIES: -:CUSTOM_ID: h:e666ced6-da75-4bdb-9be3-82c2f4455ee9 -:END: - -[[#h:2d5ee9bf-e8f2-426c-8bf7-bf78bc88d1ee][Rename files with Denote using ~dired-preview~]] - -Just as with the ~denote-dired-rename-marked-files-with-keywords~, -we can use Denote in the Image Dired buffer ([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files at once]]). -Here is the custom code: - -#+begin_src emacs-lisp -(autoload 'image-dired--with-marked "image-dired") -(autoload 'image-dired-original-file-name "image-dired-util") - -(defun my-denote-image-dired-rename-marked-files (keywords) - "Like `denote-dired-rename-marked-files-with-keywords' but for Image Dired. -Prompt for KEYWORDS and rename all marked files in the Image -Dired buffer to have a Denote-style file name with the given -KEYWORDS. - -IMPORTANT NOTE: if there are marked files in the corresponding -Dired buffers, those will be targeted as well. This is not the -fault of Denote: it is how Dired and Image Dired work in tandem. -To only rename the marked thumbnails, start by unmarking -everything in Dired. Then mark the items in Image Dired and -invoke this command." - (interactive (list (denote-keywords-prompt)) image-dired-thumbnail-mode) - (image-dired--with-marked - (when-let* ((file (image-dired-original-file-name)) - (dir (file-name-directory file)) - (id (or (denote-retrieve-filename-identifier file) "")) - (file-type (denote-filetype-heuristics file)) - (title (denote--retrieve-title-or-filename file file-type)) - (signature (or (denote-retrieve-filename-signature file) "") - (extension (file-name-extension file t)) - (new-name (denote-format-file-name dir id keywords title extension signature)) - (default-directory dir)) - (denote-rename-file-and-buffer file new-name)))) -#+end_src - -While the ~my-denote-image-dired-rename-marked-files~ renames files in -the helpful Denote-compliant way, users may still need to not prepend -a unique identifier and not sluggify (hyphenate and downcase) the -image's existing file name. To this end, the following custom command -can be used instead: - -#+begin_src emacs-lisp -(defun my-image-dired-rename-marked-files (keywords) - "Like `denote-dired-rename-marked-files-with-keywords' but for Image Dired. -Prompt for keywords and rename all marked files in the Image -Dired buffer to have Denote-style keywords, but none of the other -conventions of Denote's file-naming scheme." - (interactive (list (denote-keywords-prompt)) image-dired-thumbnail-mode) - (image-dired--with-marked - (when-let* ((file (image-dired-original-file-name)) - (dir (file-name-directory file)) - (file-type (denote-filetype-heuristics file)) - (title (denote--retrieve-title-or-filename file file-type)) - (extension (file-name-extension file t)) - (kws (denote--keywords-combine keywords)) - (new-name (concat dir title "__" kws extension)) - (default-directory dir)) - (denote-rename-file-and-buffer file new-name)))) -#+end_src - -** Rename files with Denote using ~dired-preview~ -:PROPERTIES: -:CUSTOM_ID: h:2d5ee9bf-e8f2-426c-8bf7-bf78bc88d1ee -:END: - -The ~dired-preview~ package (by me/Protesilaos) automatically displays -a preview of the file at point in Dired. This can be helpful in -tandem with Denote when we want to rename multiple files by taking a -quick look at their contents. - -The command ~denote-dired-rename-marked-files-with-keywords~ -will generate Denote-style file names based on the keywords it prompts -for. Identifiers are derived from each file's modification date -([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files at once]]). There is no need for any custom code -in this scenario. - -As noted in the section about Image Dired, the user may sometimes not -need a fully fledged Denote-style file name but only append Denote-like -keywords to each file name (e.g. =Original Name__denote_test.jpg= -instead of =20230710T195843--original-name__denote_test.jpg=). - -[[#h:e666ced6-da75-4bdb-9be3-82c2f4455ee9][Rename files with Denote in the Image Dired thumbnails buffer]] - -In such a workflow, it is unlikely to be dealing with ordinary text -files where front matter can be helpful. A custom command does not -need to behave like what Denote provides out-of-the-box, but can -instead append keywords to file names without conducting any further -actions. We thus have: - -#+begin_src emacs-lisp -(defun my-denote-dired-rename-marked-files-keywords-only () - "Like `denote-dired-rename-marked-files-with-keywords' but only for keywords in file names. - -Prompt for keywords and rename all marked files in the Dired -buffer to only have Denote-style keywords, but none of the other -conventions of Denote's file-naming scheme." - (interactive nil dired-mode) - (if-let ((marks (dired-get-marked-files))) - (let ((keywords (denote-keywords-prompt))) - (dolist (file marks) - (let* ((dir (file-name-directory file)) - (file-type (denote-filetype-heuristics file)) - (title (denote--retrieve-title-or-filename file file-type)) - (extension (file-name-extension file t)) - (kws (denote--keywords-combine keywords)) - (new-name (concat dir title "__" kws extension))) - (denote-rename-file-and-buffer file new-name))) - (revert-buffer)) - (user-error "No marked files; aborting"))) -#+end_src - -** Avoid duplicate identifiers when exporting Denote notes -:PROPERTIES: -:CUSTOM_ID: h:4a8c4546-26b3-4195-8b2c-b08a519986a4 -:END: - -When exporting Denote notes to, for example, an HTML or PDF file, -there is a high probability that the same file name is used with a new -extension. This is problematic because it creates files with -duplicate identifiers. The =20230515T085612--example__keyword.org= -produces a =20230515T085612--example__keyword.pdf=. Any link to the -=20230515T085612= will thus break: it does not honor Denote's -expectation of finding unique identifiers. This is not the fault of -Denote: exporting is done by the user without Denote's involvement. - -Org Mode and Markdown use different approaches to exporting files. No -recommended method is available for plain text files as there is no -standardised export functionality for this format (the user can always -create a new note using the file type they want on a case-by-case -basis: [[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]]). - -*** Export Denote notes with Org Mode -:PROPERTIES: -:CUSTOM_ID: h:67669d9d-17c3-45bd-8227-da57d8bc3b73 -:END: - -Org Mode has a built-in configurable export engine. You can prevent -duplicate identifiers when exporting manually for each exported file -or by advising the Org export function. - -Denote also provides commands to convert =denote:= links to their -=file:= equivalent, in case this is a required pre-processing step for -export purposes ([[#h:ed220cac-7dcb-4bb7-9243-1bb85e452e5f][Convert =denote:= links to =file:= links]]). - -**** Manually configure Org export -:PROPERTIES: -:CUSTOM_ID: h:bf791e28-73e5-4ed8-88bc-e4e9b3ebaedb -:END: - -Insert =#+export_file_name: FILENAME= in the front matter before -exporting to force a filename called whatever the value of =FILENAME= -is. The =FILENAME= does not specify the file type extension, such as -=.pdf=. This is up to the export engine. For example, a Denote note -with a complete file name of =20230515T085612--example__keyword.org= -and a front matter entry of =#+export_file_name: hello= will be -exported as =hello.pdf=. - -The advantage of this manual method is that it gives the user full -control over the resulting file name. The disadvantage is that it -depends on the user's behaviour. Forgetting to add a new name can -lead to duplicate identifiers, as already noted in the introduction to -this section ([[#h:4a8c4546-26b3-4195-8b2c-b08a519986a4][Export Denote notes]]). - -**** Automatically store Org exports in another folder -:PROPERTIES: -:CUSTOM_ID: h:7a61a370-78e5-42a1-9650-98fee140723f -:END: - -It is possible to automatically place all exports in another folder by -making Org's function ~org-export-output-file-name~ create the target -directory if needed and move the exported file there. Remember that -advising Elisp code must be handled with care, as it might break the -original function in subtle ways. - -#+begin_src emacs-lisp -(defvar my-org-export-output-directory-prefix "./export_" - "Prefix of directory used for org-mode export. - -The single dot means that the directory is created on the same -level as the one where the Org file that performs the exporting -is. Use two dots to place the directory on a level above the -current one. - -If this directory is part of `denote-directory', make sure it is -not read by Denote. See `denote-excluded-directories-regexp'. -This way there will be no known duplicate Denote identifiers -produced by the Org export mechanism.") - -(defun my-org-export-create-directory (fn extension &rest args) - "Move Org export file to its appropriate directory. - -Append the file type EXTENSION of the exported file to -`my-org-export-output-directory-prefix' and, if absent, create a -directory named accordingly. - -Install this as advice around `org-export-output-file-name'. The -EXTENSION is supplied by that function. ARGS are its remaining -arguments." - (let ((export-dir (format "%s%s" my-org-export-output-directory-prefix extension))) - (unless (file-directory-p export-dir) - (make-directory export-dir))) - (apply fn extension args)) - -(advice-add #'org-export-output-file-name :around #'my-org-export-create-directory) -#+end_src - -The target export directory should not be a subdirectory of -~denote-directory~, as that will result in duplicate identifiers. -Exclude it with the ~denote-excluded-directories-regexp~ user option -([[#h:8458f716-f9c2-4888-824b-2bf01cc5850a][Exclude certain directories from all operations]]). - -[ NOTE: I (Protesilaos) am not a LaTeX user and cannot test the - following. ] - -Using a different directory will require some additional configuration -when exporting using LaTeX. The export folder cannot be inside the -path of the ~denote-directory~ to prevent Denote from recognising it -as an attachment: -. - -**** Org Mode Publishing -:PROPERTIES: -:CUSTOM_ID: h:2f3451ed-2fc4-4f36-bcf2-112939963e20 -:END: - -Org Mode also has a publishing tool for exporting a collection of -files. Some user might apply this approach to convert their note -collection to a public or private website. - -The ~org-publish-project-alist~ variable drives the publishing -process, including the publishing directory. - -The publishing directory should not be a subdirectory of -~denote-directory~, as that will result in duplicate identifiers. -Exclude it with the ~denote-excluded-directories-regexp~ user option -([[#h:8458f716-f9c2-4888-824b-2bf01cc5850a][Exclude certain directories from all operations]]). - -*** Export Denote notes with Markdown -:PROPERTIES: -:CUSTOM_ID: h:44c6a34a-e9ad-4f43-a24f-12f2c5a8467e -:END: - -Exporting from Markdown requires an external processor (e.g., -Markdown.pl, Pandoc, or MultiMarkdown). The ~markdown-command~ -variable defines the command line used in export, for example: - -#+begin_src emacs-lisp -(setq markdown-command "multimarkdown") -#+end_src - -The export process thus occurs outside of Emacs. Users need to read -the documentation of their preferred processor to prevent the creation -of duplicate Denote identifiers. - -* Installation -:PROPERTIES: -:CUSTOM_ID: h:f3bdac2c-4704-4a51-948c-a789a2589790 -:END: -#+cindex: Installation instructions - -** GNU ELPA package -:PROPERTIES: -:CUSTOM_ID: h:42953f87-82bd-43ec-ab99-22b1e22955e7 -:END: - -The package is available as =denote=. Simply do: - -: M-x package-refresh-contents -: M-x package-install - -And search for it. - -GNU ELPA provides the latest stable release. Those who prefer to follow -the development process in order to report bugs or suggest changes, can -use the version of the package from the GNU-devel ELPA archive. Read: -https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/. - -** Manual installation -:PROPERTIES: -:CUSTOM_ID: h:d397712c-c8c0-4cfa-ad1a-ef28cf78d1f0 -:END: - -Assuming your Emacs files are found in =~/.emacs.d/=, execute the -following commands in a shell prompt: - -#+begin_src sh -cd ~/.emacs.d - -# Create a directory for manually-installed packages -mkdir manual-packages - -# Go to the new directory -cd manual-packages - -# Clone this repo, naming it "denote" -git clone https://git.sr.ht/~protesilaos/denote denote -#+end_src - -Finally, in your =init.el= (or equivalent) evaluate this: - -#+begin_src emacs-lisp -;; Make Elisp files in that directory available to the user. -(add-to-list 'load-path "~/.emacs.d/manual-packages/denote") -#+end_src - -Everything is in place to set up the package. - -* Sample configuration -:PROPERTIES: -:CUSTOM_ID: h:5d16932d-4f7b-493d-8e6a-e5c396b15fd6 -:END: -#+cindex: Package configuration - -#+begin_src emacs-lisp -(require 'denote) - -;; Remember to check the doc strings of those variables. -(setq denote-directory (expand-file-name "~/Documents/notes/")) -(setq denote-save-buffer-after-creation nil) -(setq denote-known-keywords '("emacs" "philosophy" "politics" "economics")) -(setq denote-infer-keywords t) -(setq denote-sort-keywords t) -(setq denote-file-type nil) ; Org is the default, set others here -(setq denote-prompts '(title keywords)) -(setq denote-excluded-directories-regexp nil) -(setq denote-excluded-keywords-regexp nil) -(setq denote-rename-no-confirm nil) ; Set to t if you are familiar with `denote-rename-file' - -;; Pick dates, where relevant, with Org's advanced interface: -(setq denote-date-prompt-use-org-read-date t) - - -;; Read this manual for how to specify `denote-templates'. We do not -;; include an example here to avoid potential confusion. - - -(setq denote-date-format nil) ; read doc string - -;; By default, we do not show the context of links. We just display -;; file names. This provides a more informative view. -(setq denote-backlinks-show-context t) - -;; Also see `denote-link-backlinks-display-buffer-action' which is a bit -;; advanced. - -;; If you use Markdown or plain text files (Org renders links as buttons -;; right away) -(add-hook 'find-file-hook #'denote-link-buttonize-buffer) - -;; We use different ways to specify a path for demo purposes. -(setq denote-dired-directories - (list denote-directory - (thread-last denote-directory (expand-file-name "attachments")) - (expand-file-name "~/Documents/books"))) - -;; Generic (great if you rename files Denote-style in lots of places): -;; (add-hook 'dired-mode-hook #'denote-dired-mode) -;; -;; OR if only want it in `denote-dired-directories': -(add-hook 'dired-mode-hook #'denote-dired-mode-in-directories) - - -;; Automatically rename Denote buffers using the `denote-rename-buffer-format'. -(denote-rename-buffer-mode 1) - -;; Denote DOES NOT define any key bindings. This is for the user to -;; decide. For example: -(let ((map global-map)) - (define-key map (kbd "C-c n n") #'denote) - (define-key map (kbd "C-c n c") #'denote-region) ; "contents" mnemonic - (define-key map (kbd "C-c n N") #'denote-type) - (define-key map (kbd "C-c n d") #'denote-date) - (define-key map (kbd "C-c n z") #'denote-signature) ; "zettelkasten" mnemonic - (define-key map (kbd "C-c n s") #'denote-subdirectory) - (define-key map (kbd "C-c n t") #'denote-template) - ;; If you intend to use Denote with a variety of file types, it is - ;; easier to bind the link-related commands to the `global-map', as - ;; shown here. Otherwise follow the same pattern for `org-mode-map', - ;; `markdown-mode-map', and/or `text-mode-map'. - (define-key map (kbd "C-c n i") #'denote-link) ; "insert" mnemonic - (define-key map (kbd "C-c n I") #'denote-add-links) - (define-key map (kbd "C-c n b") #'denote-backlinks) - (define-key map (kbd "C-c n f f") #'denote-find-link) - (define-key map (kbd "C-c n f b") #'denote-find-backlink) - ;; Note that `denote-rename-file' can work from any context, not just - ;; Dired bufffers. That is why we bind it here to the `global-map'. - (define-key map (kbd "C-c n r") #'denote-rename-file) - (define-key map (kbd "C-c n R") #'denote-rename-file-using-front-matter)) - -;; Key bindings specifically for Dired. -(let ((map dired-mode-map)) - (define-key map (kbd "C-c C-d C-i") #'denote-link-dired-marked-notes) - (define-key map (kbd "C-c C-d C-r") #'denote-dired-rename-files) - (define-key map (kbd "C-c C-d C-k") #'denote-dired-rename-marked-files-with-keywords) - (define-key map (kbd "C-c C-d C-R") #'denote-dired-rename-marked-files-using-front-matter)) - -(with-eval-after-load 'org-capture - (setq denote-org-capture-specifiers "%l\n%i\n%?") - (add-to-list 'org-capture-templates - '("n" "New note (with denote.el)" plain - (file denote-last-path) - #'denote-org-capture - :no-save t - :immediate-finish nil - :kill-buffer t - :jump-to-captured t))) - -;; Also check the commands `denote-link-after-creating', -;; `denote-link-or-create'. You may want to bind them to keys as well. - - -;; If you want to have Denote commands available via a right click -;; context menu, use the following and then enable -;; `context-menu-mode'. -(add-hook 'context-menu-functions #'denote-context-menu) -#+end_src - -* For developers or advanced users -:PROPERTIES: -:CUSTOM_ID: h:c916d8c5-540a-409f-b780-6ccbd90e088e -:END: - -Denote is in a stable state and can be relied upon as the basis for -custom extensions. Further below is a list with the functions or -variables we provide for public usage. Those are in addition to all -user options and commands that are already documented in the various -sections of this manual. - -In this context "public" is any form with single hyphens in its symbol, -such as ~denote-directory-files~. We expressly support those, meaning -that we consider them reliable and commit to documenting any changes in -their particularities (such as through ~make-obsolete~, a record in the -change log, a blog post on the maintainer's website, and the like). - -By contradistinction, a "private" form is declared with two hyphens in -its symbol such as ~denote--file-extension~. Do not use those as we -might change them without further notice. - -#+vindex: denote-id-format -+ Variable ~denote-id-format~ :: Format of ID prefix of a note's - filename. The note's ID is derived from the date and time of its - creation ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). - -#+vindex: denote-id-regexp -+ Variable ~denote-id-regexp~ :: Regular expression to match - ~denote-id-format~. - -#+vindex: denote-signature-regexp -+ Variable ~denote-signature-regexp~ :: Regular expression to match - the =SIGNATURE= field in a file name. - -#+vindex: denote-title-regexp -+ Variable ~denote-title-regexp~ :: Regular expression to match the - =TITLE= field in a file name ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). - -#+vindex: denote-keywords-regexp -+ Variable ~denote-keywords-regexp~ :: Regular expression to match the - =KEYWORDS= field in a file name ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). - -#+vindex: denote-excluded-punctuation-regexp -+ Variable ~denote-excluded-punctuation-regexp~ :: Punctionation that - is removed from file names. We consider those characters illegal - for our purposes. - -#+vindex: denote-excluded-punctuation-extra-regexp -+ Variable ~denote-excluded-punctuation-extra-regexp~ :: Additional - punctuation that is removed from file names. This variable is for - advanced users who need to extend the ~denote-excluded-punctuation-regexp~. - Once we have a better understanding of what we should be omitting, - we will update things accordingly. - -#+findex: denote-file-is-note-p -+ Function ~denote-file-is-note-p~ :: Return non-nil if =FILE= is an - actual Denote note. For our purposes, a note must not be a - directory, must satisfy ~file-regular-p~, its path must be part of - the variable ~denote-directory~, it must have a Denote identifier in - its name, and use one of the extensions implied by - ~denote-file-type~. - -#+findex: denote-file-has-identifier-p -+ Function ~denote-file-has-identifier-p~ :: Return non-nil if =FILE= - has a Denote identifier. - -#+findex: denote-file-has-signature-p -+ Function ~denote-file-has-signature-p~ :: Return non-nil if =FILE= - has a signature. - -#+findex: denote-file-has-supported-extension-p -+ Function ~denote-file-has-supported-extension-p~ :: Return non-nil - if =FILE= has supported extension. Also account for the possibility - of an added =.gpg= suffix. Supported extensions are those implied by - ~denote-file-type~. - -#+findex: denote-file-is-writable-and-supported-p -+ Function ~denote-file-is-writable-and-supported-p~ :: Return non-nil - if =FILE= is writable and has supported extension. - -#+findex: denote-file-type-extensions -+ Function ~denote-file-type-extensions~ :: Return all file type - extensions in ~denote-file-types~. - -#+vindex: denote-encryption-file-extensions -+ Variable ~denote-encryption-file-extensions~ :: List of strings - specifying file extensions for encryption. - -#+findex: denote-file-type-extensions-with-encryption -+ Function ~denote-file-type-extensions-with-encryption~ :: Derive - ~denote-file-type-extensions~ plus ~denote-encryption-file-extensions~. - -#+findex: denote-get-file-extension -+ Function ~denote-get-file-extension~ :: Return extension of =FILE= - with dot included. Account for ~denote-encryption-file-extensions~. - In other words, return something like =.org.gpg= if it is part of - the file, else return =.org=. - -#+findex: denote-get-file-extension-sans-encryption -+ Function ~denote-get-file-extension-sans-encryption~ :: Return - extension of =FILE= with dot included and without the encryption - part. Build on top of ~denote-get-file-extension~ though always - return something like =.org= even if the actual file extension is - =.org.gpg=. - -#+findex: denote-keywords -+ Function ~denote-keywords~ :: Return appropriate list of keyword - candidates. If ~denote-infer-keywords~ is non-nil, infer keywords - from existing notes and combine them into a list with - ~denote-known-keywords~. Else use only the latter set of keywords - ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). - -#+findex: denote-convert-file-name-keywords-to-crm -+ Function ~denote-convert-file-name-keywords-to-crm~ :: Make =STRING= - with keywords readable by ~completing-read-multiple~. =STRING= - consists of underscore-separated words, as those appear in the - keywords component of a Denote file name. =STRING= is the same as - the return value of ~denote-retrieve-filename-keywords~. - -#+findex: denote-keywords-sort -+ Function ~denote-keywords-sort~ :: Sort =KEYWORDS= if - ~denote-sort-keywords~ is non-nil. =KEYWORDS= is a list of strings, - per ~denote-keywords-prompt~. - -#+findex: denote-keywords-combine -+ Function ~denote-keywords-combine~ :: Combine =KEYWORDS= list of - strings into a single string. Keywords are separated by the - underscore character, per the Denote file-naming scheme. - -#+findex: denote-valid-date-p -+ Function ~denote-valid-date-p~ :: Return =DATE= as a valid date. A - valid =DATE= is a value that can be parsed by either ~decode-time~ - or ~date-to-time~ .Those functions signal an error if =DATE= is a - value they do not recognise. If =DATE= is nil, return nil. - -#+findex: denote-parse-date -+ Function ~denote-parse-date~ :: Return =DATE= as an appropriate - value for the ~denote~ command. Pass =DATE= through - ~denote-valid-date-p~ and use its return value. If either that or - =DATE= is nil, return ~current-time~. - -#+findex: denote-directory -+ Function ~denote-directory~ :: Return path of the variable - ~denote-directory~ as a proper directory, also because it accepts a - directory-local value for what we internally refer to as "silos" - ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directories for notes]]). Custom Lisp code can - ~let~ bind the value of the variable ~denote-directory~ - to override what this function returns. - -#+findex: denote-directory-files -+ Function ~denote-directory-files~ :: Return list of absolute file - paths in variable ~denote-directory~. Files only need to have an - identifier. The return value may thus include file types that are - not implied by ~denote-file-type~. Remember that the variable - ~denote-directory~ accepts a dir-local value, as explained in its - doc string ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directories for notes]]). With optional - =FILES-MATCHING-REGEXP=, restrict files to those matching the given - regular expression. With optional =OMIT-CURRENT= as a non-nil value, - do not include the current Denote file in the returned list. With - optional =TEXT-ONLY= as a non-nil value, limit the results to text - files that satisfy ~denote-file-is-note-p~. - -#+findex: denote-directory-subdirectories -+ Function ~denote-directory-subdirectories~ :: Return list of - subdirectories in variable ~denote-directory~. Omit dotfiles (such - as .git) unconditionally. Also exclude whatever matches - ~denote-excluded-directories-regexp~. Note that the - ~denote-directory~ accepts a directory-local value for what we call - "silos" ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directories for notes]]). - -#+findex: denote-file-name-relative-to-denote-directory -+ Function ~denote-file-name-relative-to-denote-directory~ :: Return - name of =FILE= relative to the variable ~denote-directory~. =FILE= - must be an absolute path. - -#+findex: denote-get-path-by-id -+ Function ~denote-get-path-by-id~ :: Return absolute path of =ID= - string in ~denote-directory-files~. - -#+findex: denote-barf-duplicate-id -+ Function ~denote-barf-duplicate-id~ :: Throw a ~user-error~ if - =IDENTIFIER= already exists. - -#+findex: denote-sluggify -+ Function ~denote-sluggify~ :: Make =STR= an appropriate slug for - file names and related ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]). - -#+findex: denote-sluggify-keyword -+ Function ~denote-sluggify-keyword~ :: Sluggify =STR= while joining - separate words. - -#+findex: denote-sluggify-signature -+ Function ~denote-sluggify-signature~ :: Make =STR= an appropriate - slug for signatures ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]). - -#+findex: denote-sluggify-keywords -+ Function ~denote-sluggify-keywords~ :: Sluggify =KEYWORDS=, which is - a list of strings ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]). - -#+findex: denote-filetype-heuristics -+ Function ~denote-filetype-heuristics~ :: Return likely file type of - =FILE=. If in the process of ~org-capture~, consider the file type to - be that of Org. Otherwise, use the file extension to detect the file - type of =FILE=. - - If more than one file type correspond to this file extension, use - the first file type for which the :title-key-regexp in - ~denote-file-types~ matches in the file. - - If no file type in ~denote-file-types~ has the file extension, the - file type is assumed to be the first one in ~denote-file-types~. - -#+findex: denote-format-file-name -+ Function ~denote-format-file-name~ :: Format file name. =DIR-PATH=, - =ID=, =KEYWORDS=, =TITLE=, =EXTENSION= and =SIGNATURE= are expected to - be supplied by ~denote~ or equivalent command. - - =DIR-PATH= is a string pointing to a directory. It ends with a - forward slash (the function ~denote-directory~ makes sure this is - the case when returning the value of the variable ~denote-directory~). - =DIR-PATH= cannot be nil or an empty string. - - =ID= is a string holding the identifier of the note. It cannot be - nil or an empty string and must match ~denote-id-regexp~. - - =DIR-PATH= and =ID= form the base file name. - - =KEYWORDS= is a list of strings that is reduced to a single string - by ~denote-keywords-combine~. =KEYWORDS= can be an empty list or a - nil value, in which case the relevant file name component is not - added to the base file name. - - =TITLE= and =SIGNATURE= are strings. They can be an empty string, in - which case their respective file name component is not added to the - base file name. - - =EXTENSION= is a string that contains a dot followed by the file - type extension. It can be an empty string or a nil value, in which - case it is not added to the base file name. - -#+findex: denote-extract-keywords-from-path -+ Function ~denote-extract-keywords-from-path~ :: Extract keywords - from =PATH= and return them as a list of strings. =PATH= must be a - Denote-style file name where keywords are prefixed with an - underscore. If =PATH= has no such keywords, which is possible, - return nil ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). - -#+findex: denote-extract-id-from-string -+ Function ~denote-extract-id-from-string~ :: Return existing Denote - identifier in =STRING=, else nil. - -#+findex: denote-retrieve-filename-identifier -+ Function ~denote-retrieve-filename-identifier~ :: Extract identifier - from =FILE= name, if present, else return nil. To create a new one, refer to the - ~denote-create-unique-file-identifier~ function. - -#+findex: denote-retrieve-filename-title -+ Function ~denote-retrieve-filename-title~ :: Extract Denote title - component from =FILE= name, if present, else return nil. - -#+findex: denote-retrieve-filename-keywords -+ Function ~denote-retrieve-filename-keywords~ :: Extract keywords - from =FILE= name, if present, else return nil. Return - matched keywords as a single string. - -#+findex: denote-retrieve-filename-signature -+ Function ~denote-retrieve-filename-signature~ :: Extract signature - from =FILE= name, if present, else return nil. - -#+findex: denote-retrieve-title-or-filename -+ Function ~denote-retrieve-title-or-filename~ :: Return appropriate - title for =FILE= given its =TYPE=. Try to find the value of the - title in the front matter of FILE, otherwise use its file name. This - is a wrapper for ~denote-retrieve-front-matter-title-value~ and - =denote-retrieve-filename-title=. - -#+findex: denote-get-identifier -+ Function ~denote-get-identifier~ :: Convert =DATE= into a Denote - identifier using ~denote-id-format~. =DATE= is parsed by - ~denote-valid-date-p~. If =DATE= is nil, use the current time. - -#+findex: denote-create-unique-file-identifier -+ Function ~denote-create-unique-file-identifier~ :: Create a new unique - =FILE= identifier. Test that the identifier is unique among - =USED-IDS=. The conditions are as follows: - - - If =DATE= is non-nil, invoke ~denote-prompt-for-date-return-id~. - - - If =DATE= is nil, use the file attributes to determine the last - modified date and format it as an identifier. - - - As a fallback, derive an identifier from the current time. - - With optional =USED-IDS= as nil, test that the identifier is unique - among all files and buffers in variable ~denote-directory~. - - To only return an existing identifier, refer to the function - ~denote-retrieve-filename-identifier~. - -#+findex: denote-retrieve-front-matter-title-value -+ Function ~denote-retrieve-front-matter-title-value~ :: Return title value from - =FILE= front matter per =FILE-TYPE=. - -#+findex: denote-retrieve-front-matter-title-line -+ Function ~denote-retrieve-front-matter-title-line~ :: Return title line from - =FILE= front matter per =FILE-TYPE=. - -#+findex: denote-retrieve-front-matter-keywords-value -+ Function ~denote-retrieve-front-matter-keywords-value~ :: Return keywords value - from =FILE= front matter per =FILE-TYPE=. The return value is a list - of strings. To get a combined string the way it would appear in a - Denote file name, use ~denote-retrieve-front-matter-keywords-value-as-string~. - -#+findex: denote-retrieve-front-matter-keywords-value-as-string -+ Function ~denote-retrieve-front-matter-keywords-value-as-string~ :: Return - keywords value from =FILE= front matter per =FILE-TYPE=. The return - value is a string, with the underscrore as a separator between - individual keywords. To get a list of strings instead, use - ~denote-retrieve-front-matter-keywords-value~ (the current function uses that - internally). - -#+findex: denote-retrieve-front-matter-keywords-line -+ Function ~denote-retrieve-front-matter-keywords-line~ :: Return keywords line - from =FILE= front matter per =FILE-TYPE=. - -#+findex: denote-signature-prompt -+ Function ~denote-signature-prompt~ :: Prompt for signature string. - With optional =INITIAL-SIGNATURE= use it as the initial minibuffer - text. With optional =PROMPT-TEXT= use it in the minibuffer instead - of the default prompt. Previous inputs at this prompt are available - for minibuffer completion if the user option ~denote-history-completion-in-prompts~ - is set to a non-nil value ([[#h:403422a7-7578-494b-8f33-813874c12da3][The ~denote-history-completion-in-prompts~ option]]). - -#+findex: denote-file-prompt -+ Function ~denote-file-prompt~ :: Prompt for file with identifier in - variable ~denote-directory~. With optional =FILES-MATCHING-REGEXP=, - filter the candidates per the given regular expression. With - optional =PROMPT-TEXT=, use it instead of the default "Select NOTE". - -#+findex: denote-keywords-prompt -+ Function ~denote-keywords-prompt~ :: Prompt for one or more keywords. - Read entries as separate when they are demarcated by the - ~crm-separator~, which typically is a comma. With optional - =PROMPT-TEXT=, use it to prompt the user for keywords. Else use a - generic prompt. With optional =INITIAL-KEYWORDS= use them as the - initial minibuffer text. - -#+findex: denote-title-prompt -+ Function ~denote-title-prompt~ :: Prompt for title string. With - optional =INITIAL-TITLE= use it as the initial minibuffer text. With - optional =PROMPT-TEXT= use it in the minibuffer instead of the - default prompt. Previous inputs at this prompt are available - for minibuffer completion if the user option ~denote-history-completion-in-prompts~ - is set to a non-nil value ([[#h:403422a7-7578-494b-8f33-813874c12da3][The ~denote-history-completion-in-prompts~ option]]). - -#+vindex: denote-title-prompt-current-default -+ Variable ~denote-title-prompt-current-default~ :: Currently bound - default title for ~denote-title-prompt~. Set the value of this - variable within the lexical scope of a command that needs to supply - a default title before calling ~denote-title-prompt~ and use - ~unwind-protect~ to set its value back to nil. - -#+findex: denote-file-type-prompt -+ Function ~denote-file-type-prompt~ :: Prompt for ~denote-file-type~. - Note that a non-nil value other than ~text~, ~markdown-yaml~, and - ~markdown-toml~ falls back to an Org file type. We use ~org~ here - for clarity. - -#+findex: denote-date-prompt -+ Function ~denote-date-prompt~ :: Prompt for date, expecting - =YYYY-MM-DD= or that plus =HH:MM= (or even =HH:MM:SS=). Use Org's - more advanced date selection utility if the user option - ~denote-date-prompt-use-org-read-date~ is non-nil. It requires Org - ([[#h:e7ef08d6-af1b-4ab3-bb00-494a653e6d63][The denote-date-prompt-use-org-read-date option]]). - -#+findex: denote-command-prompt -+ Function ~denote-command-prompt~ :: Prompt for command among - ~denote-commands-for-new-notes~ ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). - -#+vindex: denote-prompts-with-history-as-completion -+ Variable ~denote-prompts-with-history-as-completion~ :: Prompts that - conditionally perform completion against their history. These are - minibuffer prompts that ordinarily accept a free form string input, - as opposed to matching against a predefined set. These prompts can - optionally perform completion against their own minibuffer history - when the user option ~denote-history-completion-in-prompts~ is set - to a non-nil value ([[#h:403422a7-7578-494b-8f33-813874c12da3][The ~denote-history-completion-in-prompts~ option]]). - -#+findex: denote-files-matching-regexp-prompt -+ Function ~denote-files-matching-regexp-prompt~ :: Prompt for - =REGEXP= to filter Denote files by. With optional =PROMPT-TEXT= use - it instead of a generic prompt. - -#+findex: denote-prompt-for-date-return-id -+ Function ~denote-prompt-for-date-return-id~ :: Use - ~denote-date-prompt~ and return it as ~denote-id-format~. - -#+findex: denote-template-prompt -+ Function ~denote-template-prompt~ :: Prompt for template key in - ~denote-templates~ and return its value. - -#+findex: denote-subdirectory-prompt -+ Function ~denote-subdirectory-prompt~ :: Prompt for subdirectory of - the variable ~denote-directory~. The table uses the ~file~ - completion category (so it works with packages such as ~marginalia~ - and ~embark~). - -#+findex: denote-rename-file-prompt -+ Function ~denote-rename-file-prompt~ :: Prompt to rename file named - =OLD-NAME= to =NEW-NAME=. - -#+findex: denote-rename-file-and-buffer -+ Function ~denote-rename-file-and-buffer~ :: Rename file named - =OLD-NAME= to =NEW-NAME=, updating buffer name. - -#+findex: denote-rewrite-front-matter -+ Function ~denote-rewrite-front-matter~ :: Rewrite front matter of - note after ~denote-rename-file~ (or related) The =FILE=, =TITLE=, - =KEYWORDS=, and =FILE-TYPE= arguments are given by the renaming - command and are used to construct new front matter values if - appropriate. With optional =NO-CONFIRM=, do not prompt to confirm - the rewriting of the front matter. Otherwise produce a ~y-or-n-p~ - prompt to that effect. With optional =NO-CONFIRM=, save the buffer - after performing the rewrite. Otherwise leave it unsaved for - furthter review by the user. - -#+findex: denote-rewrite-keywords -+ Function ~denote-rewrite-keywords~ :: Rewrite =KEYWORDS= in =FILE= - outright according to =FILE-TYPE=. Do the same as - ~denote-rewrite-front-matter~ for keywords, but do not ask for - confirmation. With optional =SAVE-BUFFER=, save the buffer - corresponding to =FILE=. This function is for use in the commands - ~denote-keywords-add~, ~denote-keywords-remove~, - ~denote-dired-rename-files~, or related. - -#+findex: denote-update-dired-buffers -+ Function ~denote-update-dired-buffers~ :: Update Dired buffers of - variable ~denote-directory~. Also revert the current Dired buffer - even if it is not inside the ~denote-directory~. Note that the - ~denote-directory~ accepts a directory-local value for what we - internally refer to as "silos" ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directories for notes]]). - -#+vindex: denote-file-types -+ Variable ~denote-file-types~ :: Alist of ~denote-file-type~ and - their format properties. - - Each element is of the form =(SYMBOL PROPERTY-LIST)=. =SYMBOL= is - one of those specified in ~denote-file-type~ or an arbitrary symbol - that defines a new file type. - - =PROPERTY-LIST= is a plist that consists of the following elements: - - 1. =:extension= is a string with the file extension including the - period. - - 2. =:date-function= is a function that can format a date. See the - functions ~denote--date-iso-8601~, ~denote--date-rfc3339~, and - ~denote--date-org-timestamp~. - - 3. =:front-matter= is either a string passed to ~format~ or a - variable holding such a string. The ~format~ function accepts - four arguments, which come from ~denote~ in this order: =TITLE=, - =DATE=, =KEYWORDS=, =IDENTIFIER=. Read the doc string of - ~format~ on how to reorder arguments. - - 4. =:title-key-regexp= is a regular expression that is used to - retrieve the title line in a file. The first line matching this - regexp is considered the title line. - - 5. =:title-value-function= is the function used to format the raw - title string for inclusion in the front matter (e.g. to surround - it with quotes). Use the ~identity~ function if no further - processing is required. - - 6. =:title-value-reverse-function= is the function used to retrieve - the raw title string from the front matter. It performs the - reverse of =:title-value-function=. - - 7. =:keywords-key-regexp= is a regular expression used to retrieve - the keywords' line in the file. The first line matching this - regexp is considered the keywords' line. - - 8. =:keywords-value-function= is the function used to format the - keywords' list of strings as a single string, with appropriate - delimiters, for inclusion in the front matter. - - 9. =:keywords-value-reverse-function= is the function used to retrieve - the keywords' value from the front matter. It performs the reverse - of the =:keywords-value-function=. - - 10. =:link= is a string, or variable holding a string, that - specifies the format of a link. See the variables - ~denote-org-link-format~, ~denote-md-link-format~. - - 11. =:link-in-context-regexp= is a regular expression that is used - to match the aforementioned link format. See the variables - ~denote-org-link-in-context-regexp~, ~denote-md-link-in-context-regexp~. - - If ~denote-file-type~ is nil, use the first element of this list for - new note creation. The default is ~org~. - -#+vindex: denote-org-front-matter -+ Variable ~denote-org-front-matter~ :: Specifies the Org front - matter. It is passed to ~format~ with arguments =TITLE=, =DATE=, - =KEYWORDS=, =ID= ([[#h:7f918854-5ed4-4139-821f-8ee9ba06ad15][Change the front matter format]]) - -#+vindex: denote-yaml-front-matter -+ Variable ~denote-yaml-front-matter~ :: Specifies the YAML (Markdown) - front matter. It is passed to ~format~ with arguments =TITLE=, - =DATE=, =KEYWORDS=, =ID= ([[#h:7f918854-5ed4-4139-821f-8ee9ba06ad15][Change the front matter format]]) - -#+vindex: denote-toml-front-matter -+ Variable ~denote-toml-front-matter~ :: Specifies the TOML (Markdown) - front matter. It is passed to ~format~ with arguments =TITLE=, - =DATE=, =KEYWORDS=, =ID= ([[#h:7f918854-5ed4-4139-821f-8ee9ba06ad15][Change the front matter format]]) - -#+vindex: denote-text-front-matter -+ Variable ~denote-text-front-matter~ :: Specifies the plain text - front matter. It is passed to ~format~ with arguments =TITLE=, - =DATE=, =KEYWORDS=, =ID= ([[#h:7f918854-5ed4-4139-821f-8ee9ba06ad15][Change the front matter format]]) - -#+vindex: denote-org-link-format -+ Variable ~denote-org-link-format~ :: Format of Org link to note. - The value is passed to ~format~ with =IDENTIFIER= and =TITLE= - arguments, in this order. Also see ~denote-org-link-in-context-regexp~. - -#+vindex: denote-md-link-format -+ Variable ~denote-md-link-format~ :: Format of Markdown link to note. - The =%N$s= notation used in the default value is for ~format~ as the - supplied arguments are =IDENTIFIER= and =TITLE=, in this order. - Also see ~denote-md-link-in-context-regexp~. - -#+vindex: denote-id-only-link-format -+ Variable ~denote-id-only-link-format~ :: Format of identifier-only - link to note. The value is passed to ~format~ with =IDENTIFIER= as - its sole argument. Also see ~denote-id-only-link-in-context-regexp~. - -#+vindex: denote-org-link-in-context-regexp -+ Variable ~denote-org-link-in-context-regexp~ :: Regexp to match an - Org link in its context. The format of such links is ~denote-org-link-format~. - -#+vindex: denote-md-link-in-context-regexp -+ Variable ~denote-md-link-in-context-regexp~ :: Regexp to match an - Markdown link in its context. The format of such links is ~denote-md-link-format~. - -#+vindex: denote-id-only-link-in-context-regexp -+ Variable ~denote-id-only-link-in-context-regexp~ :: Regexp to match - an identifier-only link in its context. The format of such links is - ~denote-id-only-link-format~. - -#+findex: denote-date-org-timestamp -+ Function ~denote-date-org-timestamp~ :: Format =DATE= using the Org - inactive timestamp notation. - -#+findex: denote-date-rfc3339 -+ Function ~denote-date-rfc3339~ :: Format =DATE= using the RFC3339 - specification. - -#+findex: denote-date-iso-8601 -+ Function ~denote-date-iso-8601~ :: Format =DATE= according to ISO - 8601 standard. - -#+findex: denote-trim-whitespace -+ Function ~denote-trim-whitespace~ :: Trim whitespace around string - =S=. This can be used in ~denote-file-types~ to format front - mattter. - -#+findex: denote-trim-whitespace-then-quotes -+ Function ~denote-trim-whitespace-then-quotes~ :: Trim whitespace - then quotes around string =S=. This can be used in - ~denote-file-types~ to format front mattter. - -#+findex: denote-format-string-for-org-front-matter -+ Function ~denote-format-string-for-org-front-matter~ :: Return - string =S= as-is for Org or plain text front matter. If =S= is not a - string, return an empty string. - -#+findex: denote-format-string-for-md-front-matter -+ Function ~denote-format-string-for-md-front-matter~ :: Surround - string =S= with quotes. If =S= is not a string, return a literal - emptry string. This can be used in ~denote-file-types~ to format - front mattter. - -#+findex: denote-format-keywords-for-md-front-matter -+ Function ~denote-format-keywords-for-md-front-matter~ :: Format - front matter =KEYWORDS= for markdown file type. =KEYWORDS= is a - list of strings. Consult the ~denote-file-types~ for how this is - used. - -#+findex: denote-format-keywords-for-text-front-matter -+ Function ~denote-format-keywords-for-text-front-matter~ :: Format - front matter =KEYWORDS= for text file type. =KEYWORDS= is a list of - strings. Consult the ~denote-file-types~ for how this is used. - -#+findex: denote-format-keywords-for-org-front-matter -+ Function ~denote-format-keywords-for-org-front-matter~ :: Format - front matter =KEYWORDS= for org file type. =KEYWORDS= is a list of - strings. Consult the ~denote-file-types~ for how this is used. - -#+findex: denote-extract-keywords-from-front-matter -+ Function ~denote-extract-keywords-from-front-matter~ :: Format front - matter =KEYWORDS= for org file type. =KEYWORDS= is a list of - strings. Consult the ~denote-file-types~ for how this is used. - -#+findex: denote-link-return-links -+ Function ~denote-link-return-links~ :: Return list of links in - current or optional =FILE=. Also see ~denote-link-return-backlinks~. - -#+findex: denote-link-return-backlinks -+ Function ~denote-link-return-backlinks~ :: Return list of backlinks - in current or optional =FILE=. Also see ~denote-link-return-links~. - -#+vindex: denote-link-signature-format -+ Variable ~denote-link-signature-format~ :: Format of link - description for ~denote-link-with-signature~. - -#+findex: denote-link-description-with-signature-and-title -+ Function ~denote-link-description-with-signature-and-title~ :: Return - link description for =FILE=. Produce a description as follows: - -- If the region is active, use it as the description. - -- If =FILE= as a signature, then use the - ~denote-link-signature-format~. By default, this looks like - "signature title". - -- If =FILE= does not have a signature, then use its title as the - description. - -#+vindex: denote-link-description-function -+ Variable ~denote-link-description-function~ :: Function to use to - create the description of links. The function specified should take - a =FILE= argument and should return the description as a string. By - default, the title of the file is returned as the description. - -* Troubleshoot Denote in a pristine environment -:PROPERTIES: -:CUSTOM_ID: h:9c4467d5-6480-4681-80fb-cd9717bf8b3b -:END: - -Sometimes we get reports on bugs that may not be actually caused by -some error in the Denote code base. To help gain insight into what -the problem is, we need to be able to reproduce the issue in a minimum -viable system. Below is one way to achieve this. - -1. Find where your =denote.el= file is stored on your filesystem. - -2. Assuming you have already installed the package, one way to do this - is to invoke =M-x find-library= and search for ~denote~. It will - take you to the source file. There do =M-x eval-expression=, which - will bring up a minibuffer prompt. At the prompt evaluate: - -#+begin_example emacs-lisp -(kill-new (expand-file-name (buffer-file-name))) -#+end_example - -3. The above will save the full file system path to your kill ring. - -4. In a terminal emulator or an =M-x shell= buffer execute: - -#+begin_example -emacs -Q -#+end_example - -5. This will open a new instance of Emacs in a pristine environment. - Only the default settings are loaded. - -6. In the =*scratch*= buffer of =emacs -Q=, add your configurations - like the following and try to reproduce the issue: - -#+begin_example emacs-lisp -(require 'denote "/full/path/to/what/you/got/denote.el") - -;; Your configurations here -#+end_example - -Then try to see if your problem still occurs. If it does, then the -fault is with Denote. Otherwise there is something external to it -that we need to account for. Whatever the case, this exercise helps -us get a better sense of the specifics. - -* Contributing -:PROPERTIES: -:CUSTOM_ID: h:1ebe4865-c001-4747-a6f2-0fe45aad71cd -:END: - -Denote is a GNU ELPA package. As such, any significant change to the -code requires copyright assignment to the Free Software Foundation -(more below). - -You do not need to be a programmer to contribute to this package. -Sharing an idea or describing a workflow is equally helpful, as it -teaches us something we may not know and might be able to cover either -by extending Denote or expanding this manual. If you prefer to write a -blog post, make sure you share it with us: we can add a section herein -referencing all such articles. Everyone gets acknowledged -([[#h:f8126820-3b59-49fa-bcc2-73bd60132bb9][Acknowledgements]]). There is no such thing as an "insignificant -contribution"---they all matter. - -+ Package name (GNU ELPA): ~denote~ -+ Official manual: -+ Change log: -+ Git repositories: - + GitHub: - + GitLab: - -If our public media are not suitable, you are welcome to contact me -(Protesilaos) in private: . - -Copyright assignment is a prerequisite to sharing code. It is a simple -process. Check the request form below (please adapt it accordingly). -You must write an email to the address mentioned in the form and then -wait for the FSF to send you a legal agreement. Sign the document and -file it back to them. This could all happen via email and take about a -week. You are encouraged to go through this process. You only need to -do it once. It will allow you to make contributions to Emacs in -general. - -#+begin_example text -Please email the following information to assign@gnu.org, and we -will send you the assignment form for your past and future changes. - -Please use your full legal name (in ASCII characters) as the subject -line of the message. - -REQUEST: SEND FORM FOR PAST AND FUTURE CHANGES - -[What is the name of the program or package you're contributing to?] - -GNU Emacs - -[Did you copy any files or text written by someone else in these changes? -Even if that material is free software, we need to know about it.] - -Copied a few snippets from the same files I edited. Their author, -Protesilaos Stavrou, has already assigned copyright to the Free Software -Foundation. - -[Do you have an employer who might have a basis to claim to own -your changes? Do you attend a school which might make such a claim?] - - -[For the copyright registration, what country are you a citizen of?] - - -[What year were you born?] - - -[Please write your email address here.] - - -[Please write your postal address here.] - - - - - -[Which files have you changed so far, and which new files have you written -so far?] - -#+end_example - -** Wishlist of what we can do to extend Denote -:PROPERTIES: -:CUSTOM_ID: h:044a6a0f-e382-4013-8279-8bf4e64e73c0 -:END: - -These are various ideas to extend Denote. Whether they should be in -the core package or a separate extension is something we can discuss. -I, Protesilaos, am happy to help anyone who wants to do any of this. - -- denote-consult.el :: This can be a separate package that enhances or - replaces the various prompts we have for files (and maybe more) by - using the ~consult~ package. Consult provides the preview mechanism - and can probably be used for more things, such as to define a source - for Denote-only buffers in the ~consult-buffer~ command. If we need - to tweak things in =denote.el=, I am happy to do it. For example, we - could have a ~denote-file-prompt-function~ variable, which would - default to ~denote-file-prompt~ (what we currently have) and would - also such a hypothetical package to easily plug into what we have. - -- denote-embark.el :: Provide integration with the ~embark~ package. - This can be for doing something with the identifier/link at point. - For example, it could provide an action to produce backlinks for the - identifier/file we are linking to, not just the current one. - -- denote-transient.el :: The ~transient~ package is built into Emacs - 29 (Denote supports Emacs 28 though). We can use it to define an - alternative to what we have for the menu bar. Perhaps this interface - can used to toggle various options, such as to call ~denote~ with a - different set of prompts. - -- A ~denote-directories~ user option :: This can be either an - extension of the ~denote-directory~ (accept a list of file paths - value) or a new variable. The idea is to let the user define - separate Denote directories which do know about the presence of each - other (unlike silos). This way, a user can have an entry in - =~/Documents/notes/= link to something =~/Git/projects/= and - everything work as if the ~denote-directory~ is set to the =~/= - (with the status quo as of 2024-02-18 08:27 +0200). - -- Signatures before identifiers :: This is probably going to increase - the complixity of =denote.el= and may not be worth pursuing. But - just to explore the idea: we could have an option to rearrange file - names such that the signature appears before the identifier. If we - can do this in a smart way, we can probably extend the principle for - all file name components. Again though, this may be too complex and - not worth doing. - -- Encode the day in the identifier :: The idea is to use some coded - reference for Monday, Tuesday, etc. instead of having the generic - =T= in the identifier. For example, Monday is =A= so the identifier - for it is something like =20240219A101522= instead of what we now - have as =20240219T101522=. The old method should still be supported. - Apart from changing a few regular expressions, this does not seem - too complex to me. We would need a user option to opt in to such a - feature. Then tweak the relevant parts. The tricky issue is to - define a mapping of day names to letters/symbols that works for - everyone. Do all countries have a seven-day week, for example? We - need something universally applicable here. - -Anything else? You are welcome to discuss these and/or add to the -list. - -* Publications about Denote -:PROPERTIES: -:CUSTOM_ID: h:ca0c38f9-fa3e-4901-947e-1b589335781d -:END: - -The Emacs community is putting Denote to great use. This section -includes publications that show how people configure their note-taking -setup. If you have a blog post, video, or configuration file about -Denote, feel welcome to tell us about it ([[#h:1ebe4865-c001-4747-a6f2-0fe45aad71cd][Contributing]]). - -+ David Wilson (SystemCrafters): /Generating a Blog Site from Denote - Entries/, 2022-09-09, - -+ David Wilson (SystemCrafters): /Trying Out Prot's Denote, an Org - Roam Alternative?/, 2022-07-15, - -+ Jack Baty: /Keeping my Org Agenda updated based on Denote keywords/, - 2022-11-30, - -+ Jeremy Friesen: /Denote Emacs Configuration/, 2022-10-02, - - -+ Jeremy Friesen: /Exploring the Denote Emacs Package/, 2022-10-01, - - -+ Jeremy Friesen: /Migration Plan for Org-Roam Notes to Denote/, - 2022-10-02, - -+ Jeremy Friesen: /Project Dispatch Menu with Org Mode Metadata, - Denote, and Transient/, 2022-11-19, - - -+ Mohamed Suliman: /Managing a bibliography of BiBTeX entries with - Denote/, 2022-12-20, - -+ Peter Prevos: /Simulating Text Files with R to Test the Emacs Denote - Package/, 2022-07-28, - -+ Peter Prevos: /Emacs Writing Studio/, 2023-10-19. A configuration for authors, using Denote for taking notes, literature reviews and manage collections of images: - - - - - - - - - -+ Stefan Thesing: /Denote as a Zettelkasten/, 2023-03-02, - - -+ Summer Emacs: /An explanation of how I use Emacs/, 2023-05-04, - - -* Alternatives to Denote -:PROPERTIES: -:CUSTOM_ID: h:dbb51a1b-90b8-48e8-953c-e2fb3e36981e -:END: - -What follows is a list of Emacs packages for note-taking. I -(Protesilaos) have not used any of them, as I was manually applying my -file-naming scheme beforehand and by the time those packages were -available I was already hacking on the predecessor of Denote as a means -of learning Emacs Lisp (a package which I called "Unassuming Sidenotes -of Little Significance", aka "USLS" which is pronounced as "U-S-L-S" or -"useless"). As such, I cannot comment at length on the differences -between Denote and each of those packages, beside what I gather from -their documentation. - -+ [[https://github.com/org-roam/org-roam][org-roam]] :: The de facto standard in the Emacs milieu---and rightly - so! It has a massive community, is featureful, and should be an - excellent companion to anyone who is invested in the Org ecosystem - and/or knows what "Roam" is (I don't). It has been explained to me - that Org Roam uses a database to store a cache about your notes. It - otherwise uses standard Org files. The cache helps refer to the same - node through aliases which can provide lots of options. Personally, I - follow a single-topic-per-note approach, so anything beyond that is - overkill. If the database is only for a cache, then maybe that has no - downside, though I am careful with any kind of specialised program as - it creates a dependency. If you ask me about database software in - particular, I have no idea how to use one, let alone debug it or - retrieve data from it if something goes awry (I could learn, but that - is beside the point). - -+ [[https://github.com/localauthor/zk][zk (or zk.el)]] :: Reading its documentation makes me think that this is - Denote's sibling---the two projects have a lot of things in common, - including the preference to rely on plain files and standard tools. - The core difference is that Denote has a strict file-naming scheme. - Other differences in available features are, in principle, matters of - style or circumstance: both packages can have them. As its initials - imply, ZK enables a zettelkasten-like workflow. It does not enforce - it though, letting the user adapt the method to their needs and - requirements. - -+ [[https://github.com/ymherklotz/emacs-zettelkasten][zettelkasten]] :: This is another one of Denote's relatives, at least - insofar as the goal of simplicity is concerned. The major difference - is that according to its documentation "the name of the file that is - created is just a unique ID". This is not consistent with our - file-naming scheme which is all about making sense of your files by - their name alone and being able to visually parse a listing of them - without any kind of specialised tool (e.g. =ls -l= or =ls -C= on the - command-line from inside the ~denote-directory~ give you a - human-readable set of files names, while =find * -maxdepth 0 -type f= - is another approach). - -+ [[https://github.com/EFLS/zetteldeft][zetteldeft]] :: This is a zettelkasten note-taking system built on top - of the =deft= package. Deft provides a search interface to a - directory, in this case the one holding the user's =zetteldeft= notes. - Denote has no such dependency and is not opinionated about how the - user prefers to search/access their notes: use Dired, Grep, the - =consult= package, or whatever else you already have set up for all - things Emacs, not just your notes. - -Searching through =M-x list-packages= for "zettel" brings up more -matches. =zetteldesk= is an extension to Org Roam and, as such, I -cannot possibly know what Org Roam truly misses and what the added-value -of this package is. =neuron-mode= builds on top of an external program -called =neuron=, which I have never used. - -Searching for "note" gives us a few more results. =notes-mode= has -precious little documentation and I cannot tell what it actually does -(as I said in my presentation for LibrePlanet 2022, inadequate docs are -a bug). =side-notes= differs from what we try to do with Denote, as it -basically gives you the means to record your thoughts about some other -project you are working on and keep them on the side: so it and Denote -should not be mutually exclusive. - -If I missed something, please let me know. - -** Alternative implementations and further reading -:PROPERTIES: -:CUSTOM_ID: h:188c0986-f2fa-444f-b493-5429356e75cf -:END: - -This section covers blog posts and implementations from the Emacs -community about the topic of note-taking and file organization. They -may refer to some of the packages covered in the previous section or -provide their custom code ([[#h:dbb51a1b-90b8-48e8-953c-e2fb3e36981e][Alternatives to Denote]]). The list is -unsorted. - -+ José Antonio Ortega Ruiz (aka "jao") explains a note-taking method - that is simple like Denote but differs in other ways. An interesting - approach overall: https://jao.io/blog/simple-note-taking.html. - -+ Jethro Kuan (the main =org-roam= developer) explains their note-taking - techniques: https://jethrokuan.github.io/org-roam-guide/. Good ideas - all round, regardless of the package/code you choose to use. - -+ Karl Voit's tools [[https://github.com/novoid/date2name][date2name]], [[https://github.com/novoid/filetags/][filetags]], [[https://github.com/novoid/appendfilename/][appendfilename]], and - [[https://github.com/novoid/move2archive][move2archive]] provide a Python-based implementation to organize - individual files which do not require Emacs. His approach ([[https://karl-voit.at/managing-digital-photographs/][blog - post]] and his [[https://www.youtube.com/watch?v=rckSVmYCH90][presentation at GLT18]]) has been complemented by [[https://github.com/novoid/memacs][memacs]] - to process e.g., the date of creation of photographs, or the log of - a phone call in a format compatible to org. - -[ Development note: help expand this list. ] - -* Frequently Asked Questions -:PROPERTIES: -:CUSTOM_ID: h:da2944c6-cde6-4c65-8f2d-579305a159bb -:END: - -I (Protesilaos) answer some questions I have received or might get. It -is assumed that you have read the rest of this manual: I will not go -into the specifics of how Denote works. - -** Why develop Denote when PACKAGE already exists? -:PROPERTIES: -:CUSTOM_ID: h:b875450a-ae22-4899-ac23-c10fa9c279bb -:END: - -I wrote Denote because I was using a variant of Denote's file-naming -scheme before I was even an Emacs user (I switched to Emacs from -Tmux+Vim+CLI in the summer of 2019). I was originally inspired by -Jekyll, the static site generator, which I started using for my website -in 2016 (was on WordPress before). Jekyll's files follow the -=YYYY-MM-DD-TITLE.md= pattern. I liked its efficiency relative to the -unstructured mess I had before. Eventually, I started using that scheme -outside the confines of my website's source code. Over time I refined -it and here we are. - -Note-taking is something I take very seriously, as I am a prolific -writer (just check my website, which only reveals the tip of the -iceberg). As such, I need a program that does exactly what I want and -which I know how to extend. I originally tried to use Org capture -templates to create new files with a Denote-style file-naming scheme but -never managed to achieve it. Maybe because ~org-capture~ has some -hard-coded assumptions or I simply am not competent enough to hack on -core Org facilities. Whatever the case, an alternative was in order. - -The existence of PACKAGE is never a good reason for me not to conduct my -own experiments for recreational, educational, or practical purposes. -When the question arises of "why not contribute to PACKAGE instead?" the -answer is that without me experimenting in the first place, I would lack -the skills for such a task. Furthermore, contributing to another -package does not guarantee I get what I want in terms of workflow. - -Whether you should use Denote or not is another matter altogether: -choose whatever you want. - -** Why not rely exclusively on Org? -:PROPERTIES: -:CUSTOM_ID: h:b9831849-5c71-484e-b444-bac19cc13151 -:END: - -I think Org is one of Emacs' killer apps. I also believe it is not the -right tool for every job. When I write notes, I want to focus on -writing. Nothing more. I thus have no need for stuff like org-babel, -scheduling to-do items, clocking time, and so on. The more "mental -dependencies" you add to your workflow, the heavier the burden you carry -and the less focused you are on the task at hand: there is always that -temptation to tweak the markup, tinker with some syntactic construct, -obsess about what ought to be irrelevant to writing as such. - -In technical terms, I also am not fond of Org's code base (I understand -why it is the way it is---just commenting on the fact). Ever tried to -read it? You will routinely find functions that are tens-to-hundreds of -lines long and have all sorts of special casing. As I am not a -programmer and only learnt to write Elisp through trial and error, I -have no confidence in my ability to make Org do what I want at that -level, hence =denote= instead of =org-denote= or something. - -Perhaps the master programmer is one who can deal with complexity and -keep adding to it. I am of the opposite view, as language---code -included---is at its communicative best when it is clear and accessible. - -Make no mistake: I use Org for the agenda and also to write technical -documentation that needs to be exported to various formats, including -this very manual. - -** Why care about Unix tools when you use Emacs? -:PROPERTIES: -:CUSTOM_ID: h:da1e2469-8f04-450b-a379-a854efa80a36 -:END: - -My notes form part of my longer-term storage. I do not want to have to -rely on a special program to be able to read them or filter them. Unix -is universal, at least as far as I am concerned. - -Denote streamlines some tasks and makes things easier in general, which -is consistent with how Emacs provides a layer of interactivity on top of -Unix. Still, Denote's utilities can, in principle, be implemented as -POSIX shell scripts (minus the Emacs-specific parts like fontification -in Dired or the buttonization of links). - -Portability matters. For example, in the future I might own a -smartphone, so I prefer not to require Emacs, Org, or some other -executable to access my files on the go. - -Furthermore, I might want to share those files with someone. If I make -Emacs a requirement, I am limiting my circle to a handful of relatively -advanced users. - -Please don't misinterpret this: I am using Emacs full-time for my -computing and maintain a growing list of packages for it. This is just -me thinking long-term. - -** Why many small files instead of few large ones? -:PROPERTIES: -:CUSTOM_ID: h:7d2e7b8a-d484-4c1d-8688-17f70f242ad7 -:END: - -I have read that Org favours the latter method. If true, I strongly -disagree with it because of the implicit dependency it introduces and -the way it favours machine-friendliness over human-readability in terms -of accessing information. Notes are long-term storage. I might want to -access them on (i) some device with limited features, (ii) print on -paper, (iii) share with another person who is not a tech wizard. - -There are good arguments for few large files, but all either prioritize -machine-friendliness or presuppose the use of sophisticated tools like -Emacs+Org. - -Good luck using =less= on a generic TTY to read a file with a zillion -words, headings, sub-headings, sub-sub-headings, property drawers, and -other constructs! You will not get the otherwise wonderful folding of -headings the way you do in Emacs---do not take such features for -granted. - -My point is that notes should be atomic to help the user---and -potentially the user's family, friends, acquaintances---make sense of -them in a wide range of scenaria. The more program-agnostic your file -is, the better for you and/or everyone else you might share your -writings with. - -Human-readability means that we optimize for what matters to us. If (a) -you are the only one who will ever read your notes, (b) always have -access to good software like Emacs+Org, (c) do not care about printing -on paper, then Denote's model is not for you. Maybe you need to tweak -some ~org-capture~ template to append a new entry to one mega file (I do -that for my Org agenda, by the way, as I explained before about using -the right tool for the job). - -** Does Denote perform well at scale? -:PROPERTIES: -:CUSTOM_ID: h:863f812a-aac7-42ea-83b3-fbbdb58e08d7 -:END: - -Denote does not do anything fancy and has no special requirements: it -uses standard tools to accomplish ordinary tasks. If Emacs can cope -with lots of files, then that is all you need to know: Denote will work. - -To put this to the test, Peter Prevos is running simulations with R that -generate large volumes of notes. You can read the technicalities here: -. -Excerpt: - -#+begin_quote -Using this code I generated ten thousands notes and used this to test -the Denote package to see it if works at a large scale. This tests shows -that Prot's approach is perfectly capable of working with thousands of -notes. -#+end_quote - -Of course, we are always prepared to make refinements to the code, where -necessary, without compromising on the project's principles. - -** I add TODOs to my notes; will many files slow down the Org agenda? -:PROPERTIES: -:CUSTOM_ID: h:63c2f8d4-79ed-4c55-b3ef-e048a05802c0 -:END: - -Yes, many files will slow down the agenda due to how that works. Org -collects all files specified in the ~org-agenda-files~, searches through -their contents for timestamped entries, and then loops through all days -to determine where each entry belongs. The more days and more files, -the longer it takes to build the agenda. Doing this with potentially -hundreds of files will have a noticeable impact on performance. - -This is not a deficiency of Denote. It happens with generic Org files. -The way the agenda is built is heavily favoring the use of a single file -that holds all your timestamped entries (or at least a few such files). -Tens or hundreds of files are inefficient for this job. Plus doing so -has the side-effect of making Emacs open all those files, which you -probably do not need. - -If you want my opinion though, be more forceful with the separation of -concerns. Decouple your knowledge base from your ephemeral to-do list: -Denote (and others) can be used for the former, while you let standard -Org work splendidly for the latter---that is what I do, anyway. - -Org has a powerful linking facility, whether you use ~org-store-link~ or -do it via an ~org-capture~ template. If you want a certain note to be -associated with a task, just store the task in a single =tasks.org= (or -however you name it) and link to the relevant context. - -Do not mix your knowledge base with your to-do items. If you need help -figuring out the specifics of this workflow, you are welcome to ask for -help in our relevant channels ([[#h:1ebe4865-c001-4747-a6f2-0fe45aad71cd][Contributing]]). - -** I want to sort by last modified in Dired, why won't Denote let me? -:PROPERTIES: -:CUSTOM_ID: h:a7fd5e0a-78f7-434e-aa2e-e150479c16e2 -:END: - -Denote does not control how Dired sorts files. I encourage you to read -the manpage of the =ls= executable. It will help you in general, while -it applies to Emacs as well via Dired. The gist is that you can update -the =ls= flags that Dired uses on-the-fly: type =C-u M-x -dired-sort-toggle-or-edit= (=C-u s= by default) and append -=--sort=time= at the prompt. To reverse the order, add the =-r= flag. -The user option ~dired-listing-switches~ sets your default preference. - -For an on-demand sorted and filtered Dired listing of Denote files, -use the command ~denote-sort-dired~ ([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Sort files by component]]). - -** How do you handle the last modified case? -:PROPERTIES: -:CUSTOM_ID: h:764b5e87-cd22-4937-b5fc-af3892d6b3d8 -:END: - -Denote does not insert any meta data or heading pertaining to edits in -the file. I am of the view that these either do not scale well or are -not descriptive enough. Suppose you use a "lastmod" heading with a -timestamp: which lines where edited and what did the change amount to? - -This is where an external program can be helpful. Use a Version Control -System, such as Git, to keep track of all your notes. Every time you -add a new file, record the addition. Same for post-creation edits. -Your VCS will let you review the history of those changes. For -instance, Emacs' built-in version control framework has a command that -produces a log of changes for the current file: =M-x vc-print-log=, -bound to =C-x v l= by default. From there one can access the -corresponding diff output (use =M-x describe-mode= (=C-h m=) in an -unfamiliar buffer to learn more about it). With Git in particular, -Emacs users have the option of the all-round excellent =magit= package. - -In short: let Denote (or equivalent) create notes and link between them, -the file manager organise and provide access to files, search programs -deal with searching and narrowing, and version control software handle -the tracking of changes. - -** Speed up backlinks' buffer creation? -:PROPERTIES: -:CUSTOM_ID: h:893eec49-d7be-4603-bcff-fcc247244011 -:END: - -Denote leverages the built-in =xref= library to search for the -identifier of the current file and return any links to it. For users -of Emacs version 28 or higher, there exists a user option to specify -the program that performs this search: ~xref-search-program~. The -default is =grep=, which can be slow, though one may opt for =ugrep=, -=ripgrep=, or even specify something else (read the doc string of that -user option for the details). - -Try either for these for better results: - -#+begin_src emacs-lisp -(setq xref-search-program 'ripgrep) - -;; OR - -(setq xref-search-program 'ugrep) -#+end_src - -To use whatever executable is available on your system, use something -like this: - -#+begin_src emacs-lisp -;; Prefer ripgrep, then ugrep, and fall back to regular grep. -(setq xref-search-program - (cond - ((or (executable-find "ripgrep") - (executable-find "rg")) - 'ripgrep) - ((executable-find "ugrep") - 'ugrep) - (t - 'grep))) -#+end_src - -** Why do I get "Search failed with status 1" when I search for backlinks? -:PROPERTIES: -:CUSTOM_ID: h:42f6b07e-5956-469a-8294-17f9cf62eb2b -:END: - -Denote uses [[info:emacs#Xref][Emacs' Xref]] to find backlinks. Xref requires ~xargs~ and -one of ~grep~ or ~ripgrep~, depending on your configuration. - -This is usually not an issue on *nix systems, but the necessary -executables are not available on Windows Emacs distributions. Please -ensure that you have both ~xargs~ and either ~grep~ or ~ripgrep~ -available within your ~PATH~ environment variable. - -If you have ~git~ on Windows installed, then you may use the following -code (adjust the git's installation path if necessary): -#+begin_src emacs-lisp - (setenv "PATH" (concat (getenv "PATH") ";" "C:\\Program Files\\Git\\usr\\bin")) -#+end_src - -** Why do I get a double =#+title= in Doom Emacs? -:PROPERTIES: -:CUSTOM_ID: h:0f737b7d-40e6-46a7-b1db-117c0ffcbfef -:END: - -Doom Emacs provides a set of bespoke templates for Org. One of those -prefills any new Org file with a =#+title= field. So when Denote -creates a new Org file and inserts front matter to it, it inevitably -adds an extra title to the existing one. - -This is not a Denote problem. We can only expect a new file to be -empty by default. Check how to disable the relevant module in your -Doom Emacs configuration file. - -* Acknowledgements -:PROPERTIES: -:CUSTOM_ID: h:f8126820-3b59-49fa-bcc2-73bd60132bb9 -:END: -#+cindex: Contributors - -Denote is meant to be a collective effort. Every bit of help matters. - -+ Author/maintainer :: Protesilaos Stavrou. - -+ Contributions to code or the manual :: Abin Simon, Adam Růžička, - Alan Schmitt, Ashton Wiersdorf, Benjamin Kästner, Bruno Boal, - Charanjit Singh, Clemens Radermacher, Colin McLear, Damien Cassou, - Eduardo Grajeda, Elias Storms, Eshel Yaron, Florian, Glenna D., - Graham Marlow, Hilde Rhyne, Ivan Sokolov, Jack Baty, Jean-Charles - Bagneris, Jean-Philippe Gagné Guay, Joseph Turner, Jürgen Hötzel, - Kaushal Modi, Kai von Fintel, Kostas Andreadis, Kristoffer - Balintona, Kyle Meyer, Marc Fargas, Matthew Lemon, Noboru Ota - (nobiot), Norwid Behrnd, Peter Prevos, Philip Kaludercic, Quiliro - Ordóñez, Stephen R. Kifer, Stefan Monnier, Stefan Thesing, Thibaut - Benjamin, Tomasz Hołubowicz, Vedang Manerikar, Wesley Harvey, Zhenxu - Xu, arsaber101, ezchi, jarofromel, leinfink (Henrik), l-o-l-h - (Lincoln), mattyonweb, maxbrieiev, mentalisttraceur, pmenair, - relict007. - -+ Ideas and/or user feedback :: Abin Simon, Aditya Yadav, Alan - Schmitt, Aleksandr Vityazev, Alex Hirschfeld, Alfredo Borrás, Ashton - Wiersdorf, Benjamin Kästner, Claudiu Tănăselia, Colin McLear, Damien - Cassou, Elias Storms, Federico Stilman, Florian, Frédéric Willem - Frank Ehmsen, Glenna D., Guo Yong, Hanspeter Gisler, Jack Baty, Jay - Rajput, Jean-Charles Bagneris, Jens Östlund, Jeremy Friesen, - Jonathan Sahar, Johan Bolmsjö, Jousimies, Juanjo Presa, Kai von - Fintel, Kaushal Modi, M. Hadi Timachi, Mark Olson, Mirko Hernandez, - Niall Dooley, Paul van Gelder, Peter Prevos, Peter Smith, Suhail - Singh, Shreyas Ragavan, Stefan Thesing, Summer Emacs, Sven Seebeck, - Taoufik, TJ Stankus, Vick (VicZz), Viktor Haag, Wade Mealing, Yi - Liu, Ypot, atanasj, babusri, doolio, drcxd, fingerknight, hpgisler, - mentalisttraceur, pRot0ta1p, rbenit68, relict007, sienic, sundar bp. - -Special thanks to Peter Povinec who helped refine the file-naming -scheme, which is the cornerstone of this project. - -Special thanks to Jean-Philippe Gagné Guay for the numerous -contributions to the code base. - -* GNU Free Documentation License -:PROPERTIES: -:APPENDIX: t -:CUSTOM_ID: h:2d84e73e-c143-43b5-b388-a6765da974ea -:END: - -#+texinfo: @include doclicense.texi - -#+begin_export html -
-
-                GNU Free Documentation License
-                 Version 1.3, 3 November 2008
-
-
- Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
-     
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other
-functional and useful document "free" in the sense of freedom: to
-assure everyone the effective freedom to copy and redistribute it,
-with or without modifying it, either commercially or noncommercially.
-Secondarily, this License preserves for the author and publisher a way
-to get credit for their work, while not being considered responsible
-for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative
-works of the document must themselves be free in the same sense.  It
-complements the GNU General Public License, which is a copyleft
-license designed for free software.
-
-We have designed this License in order to use it for manuals for free
-software, because free software needs free documentation: a free
-program should come with manuals providing the same freedoms that the
-software does.  But this License is not limited to software manuals;
-it can be used for any textual work, regardless of subject matter or
-whether it is published as a printed book.  We recommend this License
-principally for works whose purpose is instruction or reference.
-
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that
-contains a notice placed by the copyright holder saying it can be
-distributed under the terms of this License.  Such a notice grants a
-world-wide, royalty-free license, unlimited in duration, to use that
-work under the conditions stated herein.  The "Document", below,
-refers to any such manual or work.  Any member of the public is a
-licensee, and is addressed as "you".  You accept the license if you
-copy, modify or distribute the work in a way requiring permission
-under copyright law.
-
-A "Modified Version" of the Document means any work containing the
-Document or a portion of it, either copied verbatim, or with
-modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of
-the Document that deals exclusively with the relationship of the
-publishers or authors of the Document to the Document's overall
-subject (or to related matters) and contains nothing that could fall
-directly within that overall subject.  (Thus, if the Document is in
-part a textbook of mathematics, a Secondary Section may not explain
-any mathematics.)  The relationship could be a matter of historical
-connection with the subject or with related matters, or of legal,
-commercial, philosophical, ethical or political position regarding
-them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles
-are designated, as being those of Invariant Sections, in the notice
-that says that the Document is released under this License.  If a
-section does not fit the above definition of Secondary then it is not
-allowed to be designated as Invariant.  The Document may contain zero
-Invariant Sections.  If the Document does not identify any Invariant
-Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed,
-as Front-Cover Texts or Back-Cover Texts, in the notice that says that
-the Document is released under this License.  A Front-Cover Text may
-be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy,
-represented in a format whose specification is available to the
-general public, that is suitable for revising the document
-straightforwardly with generic text editors or (for images composed of
-pixels) generic paint programs or (for drawings) some widely available
-drawing editor, and that is suitable for input to text formatters or
-for automatic translation to a variety of formats suitable for input
-to text formatters.  A copy made in an otherwise Transparent file
-format whose markup, or absence of markup, has been arranged to thwart
-or discourage subsequent modification by readers is not Transparent.
-An image format is not Transparent if used for any substantial amount
-of text.  A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain
-ASCII without markup, Texinfo input format, LaTeX input format, SGML
-or XML using a publicly available DTD, and standard-conforming simple
-HTML, PostScript or PDF designed for human modification.  Examples of
-transparent image formats include PNG, XCF and JPG.  Opaque formats
-include proprietary formats that can be read and edited only by
-proprietary word processors, SGML or XML for which the DTD and/or
-processing tools are not generally available, and the
-machine-generated HTML, PostScript or PDF produced by some word
-processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself,
-plus such following pages as are needed to hold, legibly, the material
-this License requires to appear in the title page.  For works in
-formats which do not have any title page as such, "Title Page" means
-the text near the most prominent appearance of the work's title,
-preceding the beginning of the body of the text.
-
-The "publisher" means any person or entity that distributes copies of
-the Document to the public.
-
-A section "Entitled XYZ" means a named subunit of the Document whose
-title either is precisely XYZ or contains XYZ in parentheses following
-text that translates XYZ in another language.  (Here XYZ stands for a
-specific section name mentioned below, such as "Acknowledgements",
-"Dedications", "Endorsements", or "History".)  To "Preserve the Title"
-of such a section when you modify the Document means that it remains a
-section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which
-states that this License applies to the Document.  These Warranty
-Disclaimers are considered to be included by reference in this
-License, but only as regards disclaiming warranties: any other
-implication that these Warranty Disclaimers may have is void and has
-no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either
-commercially or noncommercially, provided that this License, the
-copyright notices, and the license notice saying this License applies
-to the Document are reproduced in all copies, and that you add no
-other conditions whatsoever to those of this License.  You may not use
-technical measures to obstruct or control the reading or further
-copying of the copies you make or distribute.  However, you may accept
-compensation in exchange for copies.  If you distribute a large enough
-number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and
-you may publicly display copies.
-
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have
-printed covers) of the Document, numbering more than 100, and the
-Document's license notice requires Cover Texts, you must enclose the
-copies in covers that carry, clearly and legibly, all these Cover
-Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
-the back cover.  Both covers must also clearly and legibly identify
-you as the publisher of these copies.  The front cover must present
-the full title with all words of the title equally prominent and
-visible.  You may add other material on the covers in addition.
-Copying with changes limited to the covers, as long as they preserve
-the title of the Document and satisfy these conditions, can be treated
-as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit
-legibly, you should put the first ones listed (as many as fit
-reasonably) on the actual cover, and continue the rest onto adjacent
-pages.
-
-If you publish or distribute Opaque copies of the Document numbering
-more than 100, you must either include a machine-readable Transparent
-copy along with each Opaque copy, or state in or with each Opaque copy
-a computer-network location from which the general network-using
-public has access to download using public-standard network protocols
-a complete Transparent copy of the Document, free of added material.
-If you use the latter option, you must take reasonably prudent steps,
-when you begin distribution of Opaque copies in quantity, to ensure
-that this Transparent copy will remain thus accessible at the stated
-location until at least one year after the last time you distribute an
-Opaque copy (directly or through your agents or retailers) of that
-edition to the public.
-
-It is requested, but not required, that you contact the authors of the
-Document well before redistributing any large number of copies, to
-give them a chance to provide you with an updated version of the
-Document.
-
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under
-the conditions of sections 2 and 3 above, provided that you release
-the Modified Version under precisely this License, with the Modified
-Version filling the role of the Document, thus licensing distribution
-and modification of the Modified Version to whoever possesses a copy
-of it.  In addition, you must do these things in the Modified Version:
-
-A. Use in the Title Page (and on the covers, if any) a title distinct
-   from that of the Document, and from those of previous versions
-   (which should, if there were any, be listed in the History section
-   of the Document).  You may use the same title as a previous version
-   if the original publisher of that version gives permission.
-B. List on the Title Page, as authors, one or more persons or entities
-   responsible for authorship of the modifications in the Modified
-   Version, together with at least five of the principal authors of the
-   Document (all of its principal authors, if it has fewer than five),
-   unless they release you from this requirement.
-C. State on the Title page the name of the publisher of the
-   Modified Version, as the publisher.
-D. Preserve all the copyright notices of the Document.
-E. Add an appropriate copyright notice for your modifications
-   adjacent to the other copyright notices.
-F. Include, immediately after the copyright notices, a license notice
-   giving the public permission to use the Modified Version under the
-   terms of this License, in the form shown in the Addendum below.
-G. Preserve in that license notice the full lists of Invariant Sections
-   and required Cover Texts given in the Document's license notice.
-H. Include an unaltered copy of this License.
-I. Preserve the section Entitled "History", Preserve its Title, and add
-   to it an item stating at least the title, year, new authors, and
-   publisher of the Modified Version as given on the Title Page.  If
-   there is no section Entitled "History" in the Document, create one
-   stating the title, year, authors, and publisher of the Document as
-   given on its Title Page, then add an item describing the Modified
-   Version as stated in the previous sentence.
-J. Preserve the network location, if any, given in the Document for
-   public access to a Transparent copy of the Document, and likewise
-   the network locations given in the Document for previous versions
-   it was based on.  These may be placed in the "History" section.
-   You may omit a network location for a work that was published at
-   least four years before the Document itself, or if the original
-   publisher of the version it refers to gives permission.
-K. For any section Entitled "Acknowledgements" or "Dedications",
-   Preserve the Title of the section, and preserve in the section all
-   the substance and tone of each of the contributor acknowledgements
-   and/or dedications given therein.
-L. Preserve all the Invariant Sections of the Document,
-   unaltered in their text and in their titles.  Section numbers
-   or the equivalent are not considered part of the section titles.
-M. Delete any section Entitled "Endorsements".  Such a section
-   may not be included in the Modified Version.
-N. Do not retitle any existing section to be Entitled "Endorsements"
-   or to conflict in title with any Invariant Section.
-O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or
-appendices that qualify as Secondary Sections and contain no material
-copied from the Document, you may at your option designate some or all
-of these sections as invariant.  To do this, add their titles to the
-list of Invariant Sections in the Modified Version's license notice.
-These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains
-nothing but endorsements of your Modified Version by various
-parties--for example, statements of peer review or that the text has
-been approved by an organization as the authoritative definition of a
-standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a
-passage of up to 25 words as a Back-Cover Text, to the end of the list
-of Cover Texts in the Modified Version.  Only one passage of
-Front-Cover Text and one of Back-Cover Text may be added by (or
-through arrangements made by) any one entity.  If the Document already
-includes a cover text for the same cover, previously added by you or
-by arrangement made by the same entity you are acting on behalf of,
-you may not add another; but you may replace the old one, on explicit
-permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License
-give permission to use their names for publicity for or to assert or
-imply endorsement of any Modified Version.
-
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this
-License, under the terms defined in section 4 above for modified
-versions, provided that you include in the combination all of the
-Invariant Sections of all of the original documents, unmodified, and
-list them all as Invariant Sections of your combined work in its
-license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and
-multiple identical Invariant Sections may be replaced with a single
-copy.  If there are multiple Invariant Sections with the same name but
-different contents, make the title of each such section unique by
-adding at the end of it, in parentheses, the name of the original
-author or publisher of that section if known, or else a unique number.
-Make the same adjustment to the section titles in the list of
-Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History"
-in the various original documents, forming one section Entitled
-"History"; likewise combine any sections Entitled "Acknowledgements",
-and any sections Entitled "Dedications".  You must delete all sections
-Entitled "Endorsements".
-
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other
-documents released under this License, and replace the individual
-copies of this License in the various documents with a single copy
-that is included in the collection, provided that you follow the rules
-of this License for verbatim copying of each of the documents in all
-other respects.
-
-You may extract a single document from such a collection, and
-distribute it individually under this License, provided you insert a
-copy of this License into the extracted document, and follow this
-License in all other respects regarding verbatim copying of that
-document.
-
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate
-and independent documents or works, in or on a volume of a storage or
-distribution medium, is called an "aggregate" if the copyright
-resulting from the compilation is not used to limit the legal rights
-of the compilation's users beyond what the individual works permit.
-When the Document is included in an aggregate, this License does not
-apply to the other works in the aggregate which are not themselves
-derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these
-copies of the Document, then if the Document is less than one half of
-the entire aggregate, the Document's Cover Texts may be placed on
-covers that bracket the Document within the aggregate, or the
-electronic equivalent of covers if the Document is in electronic form.
-Otherwise they must appear on printed covers that bracket the whole
-aggregate.
-
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may
-distribute translations of the Document under the terms of section 4.
-Replacing Invariant Sections with translations requires special
-permission from their copyright holders, but you may include
-translations of some or all Invariant Sections in addition to the
-original versions of these Invariant Sections.  You may include a
-translation of this License, and all the license notices in the
-Document, and any Warranty Disclaimers, provided that you also include
-the original English version of this License and the original versions
-of those notices and disclaimers.  In case of a disagreement between
-the translation and the original version of this License or a notice
-or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements",
-"Dedications", or "History", the requirement (section 4) to Preserve
-its Title (section 1) will typically require changing the actual
-title.
-
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense, or distribute it is void, and
-will automatically terminate your rights under this License.
-
-However, if you cease all violation of this License, then your license
-from a particular copyright holder is reinstated (a) provisionally,
-unless and until the copyright holder explicitly and finally
-terminates your license, and (b) permanently, if the copyright holder
-fails to notify you of the violation by some reasonable means prior to
-60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, receipt of a copy of some or all of the same material does
-not give you any rights to use it.
-
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the
-GNU Free Documentation License from time to time.  Such new versions
-will be similar in spirit to the present version, but may differ in
-detail to address new problems or concerns.  See
-https://www.gnu.org/licenses/.
-
-Each version of the License is given a distinguishing version number.
-If the Document specifies that a particular numbered version of this
-License "or any later version" applies to it, you have the option of
-following the terms and conditions either of that specified version or
-of any later version that has been published (not as a draft) by the
-Free Software Foundation.  If the Document does not specify a version
-number of this License, you may choose any version ever published (not
-as a draft) by the Free Software Foundation.  If the Document
-specifies that a proxy can decide which future versions of this
-License can be used, that proxy's public statement of acceptance of a
-version permanently authorizes you to choose that version for the
-Document.
-
-11. RELICENSING
-
-"Massive Multiauthor Collaboration Site" (or "MMC Site") means any
-World Wide Web server that publishes copyrightable works and also
-provides prominent facilities for anybody to edit those works.  A
-public wiki that anybody can edit is an example of such a server.  A
-"Massive Multiauthor Collaboration" (or "MMC") contained in the site
-means any set of copyrightable works thus published on the MMC site.
-
-"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
-license published by Creative Commons Corporation, a not-for-profit
-corporation with a principal place of business in San Francisco,
-California, as well as future copyleft versions of that license
-published by that same organization.
-
-"Incorporate" means to publish or republish a Document, in whole or in
-part, as part of another Document.
-
-An MMC is "eligible for relicensing" if it is licensed under this
-License, and if all works that were first published under this License
-somewhere other than this MMC, and subsequently incorporated in whole or
-in part into the MMC, (1) had no cover texts or invariant sections, and
-(2) were thus incorporated prior to November 1, 2008.
-
-The operator of an MMC Site may republish an MMC contained in the site
-under CC-BY-SA on the same site at any time before August 1, 2009,
-provided the MMC is eligible for relicensing.
-
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of
-the License in the document and put the following copyright and
-license notices just after the title page:
-
-    Copyright (c)  YEAR  YOUR NAME.
-    Permission is granted to copy, distribute and/or modify this document
-    under the terms of the GNU Free Documentation License, Version 1.3
-    or any later version published by the Free Software Foundation;
-    with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
-    A copy of the license is included in the section entitled "GNU
-    Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
-replace the "with...Texts." line with this:
-
-    with the Invariant Sections being LIST THEIR TITLES, with the
-    Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other
-combination of the three, merge those two alternatives to suit the
-situation.
-
-If your document contains nontrivial examples of program code, we
-recommend releasing these examples in parallel under your choice of
-free software license, such as the GNU General Public License,
-to permit their use in free software.
-
-#+end_export - -#+html: blob - 1ea03ba9e7341ee40a2ddd780d8e5c9d81576952 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/denote-autoloads.el +++ /dev/null @@ -1,994 +0,0 @@ -;;; denote-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from denote.el - - (put 'denote-directory 'safe-local-variable (lambda (val) (or (stringp val) (eq val 'local) (eq val 'default-directory)))) - (put 'denote-known-keywords 'safe-local-variable #'listp) - (put 'denote-infer-keywords 'safe-local-variable (lambda (val) (or val (null val)))) -(autoload 'denote "denote" "\ -Create a new note with the appropriate metadata and file name. - -Run the `denote-after-new-note-hook' after creating the new note. - -When called interactively, the metadata and file name are prompted -according to the value of `denote-prompts'. - -When called from Lisp, all arguments are optional. - -- TITLE is a string or a function returning a string. - -- KEYWORDS is a list of strings. The list can be empty or the - value can be set to nil. - -- FILE-TYPE is a symbol among those described in `denote-file-type'. - -- SUBDIRECTORY is a string representing the path to either the - value of the variable `denote-directory' or a subdirectory - thereof. The subdirectory must exist: Denote will not create - it. If SUBDIRECTORY does not resolve to a valid path, the - variable `denote-directory' is used instead. - -- DATE is a string representing a date like 2022-06-30 or a date - and time like 2022-06-16 14:30. A nil value or an empty string - is interpreted as the `current-time'. - -- TEMPLATE is a symbol which represents the key of a cons cell in - the user option `denote-templates'. The value of that key is - inserted to the newly created buffer after the front matter. - -- SIGNATURE is a string or a function returning a string. - -(fn &optional TITLE KEYWORDS FILE-TYPE SUBDIRECTORY DATE TEMPLATE SIGNATURE)" t) -(autoload 'denote-type "denote" "\ -Create note while prompting for a file type. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `file-type' prompt appended to its existing prompts." t) -(function-put 'denote-type 'interactive-only 't) -(autoload 'denote-date "denote" "\ -Create note while prompting for a date. - -The date can be in YEAR-MONTH-DAY notation like 2022-06-30 or -that plus the time: 2022-06-16 14:30. When the user option -`denote-date-prompt-use-org-read-date' is non-nil, the date -prompt uses the more powerful Org+calendar system. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `date' prompt appended to its existing prompts." t) -(function-put 'denote-date 'interactive-only 't) -(autoload 'denote-subdirectory "denote" "\ -Create note while prompting for a subdirectory. - -Available candidates include the value of the variable -`denote-directory' and any subdirectory thereof. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `subdirectory' prompt appended to its existing prompts." t) -(function-put 'denote-subdirectory 'interactive-only 't) -(autoload 'denote-template "denote" "\ -Create note while prompting for a template. - -Available candidates include the keys in the `denote-templates' -alist. The value of the selected key is inserted in the newly -created note after the front matter. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `template' prompt appended to its existing prompts." t) -(function-put 'denote-template 'interactive-only 't) -(autoload 'denote-signature "denote" "\ -Create note while prompting for a file signature. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `signature' prompt appended to its existing prompts." t) -(function-put 'denote-signature 'interactive-only 't) -(autoload 'denote-region "denote" "\ -Call `denote' and insert therein the text of the active region." t) -(function-put 'denote-region 'interactive-only 't) -(autoload 'denote-open-or-create "denote" "\ -Visit TARGET file in variable `denote-directory'. -If file does not exist, invoke `denote' to create a file. - -If TARGET file does not exist, add the user input that was used -to search for it to the minibuffer history of the -`denote-file-prompt'. The user can then retrieve and possibly -further edit their last input, using it as the newly created -note's actual title. At the `denote-file-prompt' type -\\\\[previous-history-element]. - -(fn TARGET)" t) -(autoload 'denote-open-or-create-with-command "denote" "\ -Visit TARGET file in variable `denote-directory'. -If file does not exist, invoke `denote' to create a file. - -If TARGET file does not exist, add the user input that was used -to search for it to the minibuffer history of the -`denote-file-prompt'. The user can then retrieve and possibly -further edit their last input, using it as the newly created -note's actual title. At the `denote-file-prompt' type -\\\\[previous-history-element]." t) -(function-put 'denote-open-or-create-with-command 'interactive-only 't) -(autoload 'denote-rename-file "denote" "\ -Rename file and update existing front matter if appropriate. - -Always rename the file where it is located in the file system: -never move it to another directory. - -If in Dired, consider FILE to be the one at point, else prompt -with minibuffer completion for one. When called from Lisp, FILE -is a file system path represented as a string. - -If FILE has a Denote-compliant identifier, retain it while -updating components of the file name referenced by the user -option `denote-prompts'. By default, these are the TITLE and -KEYWORDS. The SIGNATURE is another one. When called from Lisp, -TITLE and SIGNATURE are strings, while KEYWORDS is a list of -strings. - -If there is no identifier, create an identifier based on the -following conditions: - -1. If the `denote-prompts' includes an entry for date prompts, - then prompt for DATE and take its input to produce a new - identifier. For use in Lisp, DATE must conform with - `denote-valid-date-p'. - -2. If DATE is nil (e.g. when `denote-prompts' does not include a - date entry), use the file attributes to determine the last - modified date of FILE and format it as an identifier. - -3. As a fallback, derive an identifier from the current date and - time. - -4. At any rate, if the resulting identifier is not unique among - the files in the variable `denote-directory', increment it - such that it becomes unique. - -In interactive use, and assuming `denote-prompts' includes a -title entry, make the TITLE prompt have prefilled text in the -minibuffer that consists of the current title of FILE. The -current title is either retrieved from the front matter (such as -the #+title in Org) or from the file name. - -Do the same for the SIGNATURE prompt, subject to `denote-prompts', -by prefilling the minibuffer with the current signature of FILE, -if any. - -Same principle for the KEYWORDS prompt: convert the keywords in -the file name into a comma-separated string and prefill the -minibuffer with it (the KEYWORDS prompt accepts more than one -keywords, each separated by a comma, else the `crm-separator'). - -For all prompts, interpret an empty input as an instruction to -remove that file name component. For example, if a TITLE prompt -is available and FILE is 20240211T093531--some-title__keyword1.org -then rename FILE to 20240211T093531__keyword1.org. - -If a file name component is present, but there is no entry for it in -`denote-prompts', keep it as-is. - -[ NOTE: Please check with your minibuffer user interface how to - provide an empty input. The Emacs default setup accepts the - empty minibuffer contents as they are, though popular packages - like `vertico' use the first available completion candidate - instead. For `vertico', the user must either move one up to - select the prompt and then type RET there with empty contents, - or use the command `vertico-exit-input' with empty contents. - That Vertico command is bound to M-RET as of this writing on - 2024-02-13 08:08 +0200. ] - -When renaming FILE, read its file type extension (like .org) and -preserve it through the renaming process. Files that have no -extension are left without one. - -As a final step, ask for confirmation, showing the difference -between old and new file names. Do not ask for confirmation if -the user option `denote-rename-no-confirm' is set to a non-nil -value. - -If FILE has front matter for TITLE and KEYWORDS, ask to rewrite -their values in order to reflect the new input, unless -`denote-rename-no-confirm' is non-nil. When the -`denote-rename-no-confirm' is nil (the default), do not save the -underlying buffer, thus giving the user the option to -double-check the result, such as by invokling the command -`diff-buffer-with-file'. The rewrite of the TITLE and KEYWORDS -in the front matter should not affect the rest of the front -matter. - -If the file does not have front matter but is among the supported -file types (per `denote-file-type'), add front matter to the top -of it and leave the buffer unsaved for further inspection. Save -the buffer if `denote-rename-no-confirm' is non-nil. - -For the front matter of each file type, refer to the variables: - -- `denote-org-front-matter' -- `denote-text-front-matter' -- `denote-toml-front-matter' -- `denote-yaml-front-matter' - -Run the `denote-after-rename-file-hook' after renaming FILE. - -This command is intended to (i) rename Denote files, (ii) convert -existing supported file types to Denote notes, and (ii) rename -non-note files (e.g. PDF) that can benefit from Denote's -file-naming scheme. - -For a version of this command that works with multiple files -one-by-one, use `denote-dired-rename-files'. - -(fn FILE &optional TITLE KEYWORDS SIGNATURE DATE)" t) -(autoload 'denote-dired-rename-files "denote" "\ -Rename Dired marked files same way as `denote-rename-file'. -Rename each file in sequence, making all the relevant prompts. -Unlike `denote-rename-file', do not prompt for confirmation of -the changes made to the file: perform them outright (same as -setting `denote-rename-no-confirm' to a non-nil value)." '(dired-mode)) -(function-put 'denote-dired-rename-files 'interactive-only 't) -(autoload 'denote-dired-rename-marked-files-with-keywords "denote" "\ -Rename marked files in Dired to a Denote file name by writing keywords. - -Specifically, do the following: - -- retain the file's existing name and make it the TITLE field, - per Denote's file-naming scheme; - -- sluggify the TITLE, according to our conventions (check the - user option `denote-file-name-slug-functions'); - -- prepend an identifier to the TITLE; - -- preserve the file's extension, if any; - -- prompt once for KEYWORDS and apply the user's input to the - corresponding field in the file name, rewriting any keywords - that may exist while removing keywords that do exist if - KEYWORDS is empty; - -- add or rewrite existing front matter to the underlying file, if - it is recognized as a Denote note (per `denote-file-type'), - such that it includes the new keywords. - -Run the `denote-after-rename-file-hook' after renaming is done. - -[ Note that the affected buffers are not saved, unless the user - option `denote-rename-no-confirm' is non-nil. Users can thus - check them to confirm that the new front matter does not cause - any problems (e.g. with the `diff-buffer-with-file' command). - Multiple buffers can be saved in one go with the command - `save-some-buffers' (read its doc string). ]" '(dired-mode)) -(function-put 'denote-dired-rename-marked-files-with-keywords 'interactive-only 't) -(autoload 'denote-rename-file-using-front-matter "denote" "\ -Rename FILE using its front matter as input. -When called interactively, FILE is the return value of the -function `buffer-file-name' which is subsequently inspected for -the requisite front matter. It is thus implied that the FILE has -a file type that is supported by Denote, per `denote-file-type'. - -Unless NO-CONFIRM is non-nil (such as with a prefix argument), -ask for confirmation, showing the difference between the old and -the new file names. - -Never modify the identifier of the FILE, if any, even if it is -edited in the front matter. Denote considers the file name to be -the source of truth in this case to avoid potential breakage with -typos and the like. - -If NO-CONFIRM is non-nil (such as with a prefix argument) do not -prompt for confirmation while renaming the file. Do it outright. - -If optional SAVE-BUFFER is non-nil (such as with a double prefix -argument), save the corresponding buffer. - -If the user option `denote-rename-no-confirm' is non-nil, -interpret it the same way as a combination of NO-CONFIRM and -SAVE-BUFFER. - -The identifier of the file, if any, is never modified even if it -is edited in the front matter: Denote considers the file name to -be the source of truth in this case, to avoid potential breakage -with typos and the like. - -(fn FILE &optional NO-CONFIRM SAVE-BUFFER)" t) -(autoload 'denote-dired-rename-marked-files-using-front-matter "denote" "\ -Call `denote-rename-file-using-front-matter' over the Dired marked files. -Refer to the documentation of that command for the technicalities. - -Marked files must count as notes for the purposes of Denote, -which means that they at least have an identifier in their file -name and use a supported file type, per `denote-file-type'. -Files that do not meet this criterion are ignored because Denote -cannot know if they have front matter and what that may be." '(dired-mode)) -(autoload 'denote-keywords-add "denote" "\ -Prompt for KEYWORDS to add to the current note's front matter. -When called from Lisp, KEYWORDS is a list of strings. - -Rename the file without further prompt so that its name reflects -the new front matter, per `denote-rename-file-using-front-matter'. - -With an optional SAVE-BUFFER (such as a prefix argument when -called interactively), save the buffer outright. Otherwise leave -the buffer unsaved for further review. - -If the user option `denote-rename-no-confirm' is non-nil, -interpret it the same way as SAVE-BUFFER, making SAVE-BUFFER -reduntant. - -Run `denote-after-rename-file-hook' as a final step. - -(fn KEYWORDS &optional SAVE-BUFFER)" t) -(autoload 'denote-keywords-remove "denote" "\ -Prompt for keywords in current note and remove them. -Keywords are retrieved from the file's front matter. - -Rename the file without further prompt so that its name reflects -the new front matter, per `denote-rename-file-using-front-matter'. - -With an optional SAVE-BUFFER as a prefix argument, save the -buffer outright. Otherwise leave the buffer unsaved for further -review. - -If the user option `denote-rename-no-confirm' is non-nil, -interpret it the same way as SAVE-BUFFER, making SAVE-BUFFER -reduntant. - -Run `denote-after-rename-file-hook' as a final step. - -(fn &optional SAVE-BUFFER)" t) -(function-put 'denote-keywords-remove 'interactive-only 't) -(autoload 'denote-rename-add-signature "denote" "\ -Add to FILE name the SIGNATURE. -In interactive use, prompt for FILE, defaulting either to the current -buffer's file or the one at point in a Dired buffer. Also prompt for -SIGNATURE, using the existing one, if any, as the initial value. - -When called from Lisp, FILE is a string pointing to a file system path -and SIGNATURE is a string. - -Ask for confirmation before renaming the file to include the new -signature. Do it unless the user option `denote-rename-no-confirm' is -set to a non-nil value. - -Once the operation is done, reload any Dired buffers and run the -`denote-after-rename-file-hook'. - -Also see `denote-rename-remove-signature'. - -(fn FILE SIGNATURE)" t) -(autoload 'denote-rename-remove-signature "denote" "\ -Remove the signature of FILE. -In interactive use, prompt for FILE, defaulting either to the current -buffer's file or the one at point in a Dired buffer. When called from -Lisp, FILE is a string pointing to a file system path. - -Ask for confirmation before renaming the file to remove its signature. -Do it unless the user option `denote-rename-no-confirm' is set to a -non-nil value. - -Once the operation is done, reload any Dired buffers and run the -`denote-after-rename-file-hook'. - -Also see `denote-rename-add-signature'. - -(fn FILE)" t) -(autoload 'denote-add-front-matter "denote" "\ -Insert front matter at the top of FILE. - -When called interactively, FILE is the return value of the -function `buffer-file-name'. FILE is checked to determine -whether it is a note for Denote's purposes. - -TITLE is a string. Interactively, it is the user input at the -minibuffer prompt. - -KEYWORDS is a list of strings. Interactively, it is the user -input at the minibuffer prompt. This one supports completion for -multiple entries, each separated by the `crm-separator' (normally -a comma). - -The purpose of this command is to help the user generate new -front matter for an existing note (perhaps because the user -deleted the previous one and could not undo the change). - -This command does not rename the file (e.g. to update the -keywords). To rename a file by reading its front matter as -input, use `denote-rename-file-using-front-matter'. - -Note that this command is useful only for existing Denote notes. -If the user needs to convert a generic text file to a Denote -note, they can use one of the command which first rename the file -to make it comply with our file-naming scheme and then add the -relevant front matter. - -[ NOTE: Please check with your minibuffer user interface how to - provide an empty input. The Emacs default setup accepts the - empty minibuffer contents as they are, though popular packages - like `vertico' use the first available completion candidate - instead. For `vertico', the user must either move one up to - select the prompt and then type RET there with empty contents, - or use the command `vertico-exit-input' with empty contents. - That Vertico command is bound to M-RET as of this writing on - 2024-02-29 09:24 +0200. ] - -(fn FILE TITLE KEYWORDS)" t) -(autoload 'denote-change-file-type-and-front-matter "denote" "\ -Change file type of FILE and add an appropriate front matter. - -If in Dired, consider FILE to be the one at point, else prompt -with minibuffer completion for one. - -Add a front matter in the format of the NEW-FILE-TYPE at the -beginning of the file. - -Retrieve the title of FILE from a line starting with a title -field in its front matter, depending on the previous file -type (e.g. #+title for Org). The same process applies for -keywords. - -As a final step, ask for confirmation, showing the difference -between old and new file names. - -Important note: No attempt is made to modify any other elements -of the file. This needs to be done manually. - -(fn FILE NEW-FILE-TYPE)" t) -(autoload 'denote-dired-mode "denote" "\ -Fontify all Denote-style file names. - -Add this or `denote-dired-mode-in-directories' to -`dired-mode-hook'. - -This is a minor mode. If called interactively, toggle the -`Denote-Dired mode' mode. If the prefix argument is positive, -enable the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `denote-dired-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(autoload 'denote-dired-mode-in-directories "denote" "\ -Enable `denote-dired-mode' in `denote-dired-directories'. -Add this function to `dired-mode-hook'. - -If `denote-dired-directories-include-subdirectories' is non-nil, -also enable it in all subdirectories.") -(autoload 'denote-link "denote" "\ -Create link to FILE note in variable `denote-directory' with DESCRIPTION. - -When called interactively, prompt for FILE using completion. In -this case, derive FILE-TYPE from the current buffer. - -The DESCRIPTION is returned by the function specified in variable -`denote-link-description-function'. If the region is active, its -content is deleted and can be used as the description of the -link. The default value of `denote-link-description-function' -returns the content of the active region, if any, else the title -of the linked file is used as the description. The title comes -either from the front matter or the file name. Note that if you -change the default value of `denote-link-description-function', -make sure to use the `region-text' parameter. Regardless of the -value of this user option, `denote-link' will always replace the -content of the active region. - -With optional ID-ONLY as a non-nil argument, such as with a -universal prefix (\\[universal-argument]), insert links with just -the identifier and no further description. In this case, the -link format is always [[denote:IDENTIFIER]]. If the DESCRIPTION -is empty, the link is also as if ID-ONLY were non-nil. The -default value of `denote-link-description-function' returns an -empty string when the region is empty. Thus, the link will have -no description in this case. - -When called from Lisp, FILE is a string representing a full file -system path. FILE-TYPE is a symbol as described in -`denote-file-type'. DESCRIPTION is a string. Whether the caller -treats the active region specially, is up to it. - -(fn FILE FILE-TYPE DESCRIPTION &optional ID-ONLY)" t) -(autoload 'denote-link-with-signature "denote" "\ -Insert link to file with signature. -Prompt for file using minibuffer completion, limiting the list of -candidates to files with a signature in their file name. - -By default, the description of the link includes the signature, -if present, followed by the file's title, if any. - -For more advanced uses with Lisp, refer to the `denote-link' -function." t) -(function-put 'denote-link-with-signature 'interactive-only 't) -(autoload 'denote-find-link "denote" "\ -Use minibuffer completion to visit linked file." t) -(function-put 'denote-find-link 'interactive-only 't) -(autoload 'denote-find-backlink "denote" "\ -Use minibuffer completion to visit backlink to current file. - -Like `denote-find-link', but select backlink to follow." t) -(function-put 'denote-find-backlink 'interactive-only 't) -(autoload 'denote-link-after-creating "denote" "\ -Create new note in the background and link to it directly. - -Use `denote' interactively to produce the new note. Its doc -string explains which prompts will be used and under what -conditions. - -With optional ID-ONLY as a prefix argument create a link that -consists of just the identifier. Else try to also include the -file's title. This has the same meaning as in `denote-link'. - -For a variant of this, see `denote-link-after-creating-with-command'. - -IMPORTANT NOTE: Normally, `denote' does not save the buffer it -produces for the new note. This is a safety precaution to not -write to disk unless the user wants it (e.g. the user may choose -to kill the buffer, thus cancelling the creation of the note). -However, for this command the creation of the note happens in the -background and the user may miss the step of saving their buffer. -We thus have to save the buffer in order to (i) establish valid -links, and (ii) retrieve whatever front matter from the target -file. Though see `denote-save-buffer-after-creation'. - -(fn &optional ID-ONLY)" t) -(autoload 'denote-link-after-creating-with-command "denote" "\ -Like `denote-link-after-creating' but prompt for note-making COMMAND. -Use this to, for example, call `denote-signature' so that the -newly created note has a signature as part of its file name. - -Optional ID-ONLY has the same meaning as in the command -`denote-link-after-creating'. - -(fn COMMAND &optional ID-ONLY)" t) -(autoload 'denote-link-or-create "denote" "\ -Use `denote-link' on TARGET file, creating it if necessary. - -If TARGET file does not exist, call `denote-link-after-creating' -which runs the `denote' command interactively to create the file. -The established link will then be targeting that new file. - -If TARGET file does not exist, add the user input that was used -to search for it to the minibuffer history of the -`denote-file-prompt'. The user can then retrieve and possibly -further edit their last input, using it as the newly created -note's actual title. At the `denote-file-prompt' type -\\\\[previous-history-element]. - -With optional ID-ONLY as a prefix argument create a link that -consists of just the identifier. Else try to also include the -file's title. This has the same meaning as in `denote-link'. - -(fn TARGET &optional ID-ONLY)" t) -(autoload 'denote-link-buttonize-buffer "denote" "\ -Make denote: links actionable buttons in the current buffer. - -Buttonization applies to the plain text and Markdown file types, -per the user option `denote-file-types'. It will not do anything -in `org-mode' buffers, as buttons already work there. If you do -not use Markdown or plain text, then you do not need this. - -Links work when they point to a file inside the variable -`denote-directory'. - -To buttonize links automatically add this function to the -`find-file-hook'. Or call it interactively for on-demand -buttonization. - -When called from Lisp, with optional BEG and END as buffer -positions, limit the process to the region in-between. - -(fn &optional BEG END)" t) -(autoload 'denote-backlinks "denote" "\ -Produce a buffer with backlinks to the current note. - -The backlinks' buffer shows the file name of the note linking to -the current note, as well as the context of each link. - -File names are fontified by Denote if the user option -`denote-link-fontify-backlinks' is non-nil. If this user option -is nil, the buffer is fontified by Xref. - -The placement of the backlinks' buffer is controlled by the user -option `denote-link-backlinks-display-buffer-action'. By -default, it will show up below the current window." t) -(autoload 'denote-add-links "denote" "\ -Insert links to all notes matching REGEXP. -Use this command to reference multiple files at once. -Particularly useful for the creation of metanotes (read the -manual for more on the matter). - -Optional ID-ONLY has the same meaning as in `denote-link': it -inserts links with just the identifier. - -(fn REGEXP &optional ID-ONLY)" t) -(autoload 'denote-link-dired-marked-notes "denote" "\ -Insert Dired marked FILES as links in BUFFER. - -FILES are Denote notes, meaning that they have our file-naming -scheme, are writable/regular files, and use the appropriate file -type extension (per `denote-file-type'). Furthermore, the marked -files need to be inside the variable `denote-directory' or one of -its subdirectories. No other file is recognised (the list of -marked files ignores whatever does not count as a note for our -purposes). - -The BUFFER is one which visits a Denote note file. If there are -multiple buffers, prompt with completion for one among them. If -there isn't one, throw an error. - -With optional ID-ONLY as a prefix argument, insert links with -just the identifier (same principle as with `denote-link'). - -This command is meant to be used from a Dired buffer. - -(fn FILES BUFFER &optional ID-ONLY)" '(dired-mode)) -(defvar denote-menu-bar-mode t "\ -Non-nil if Denote-Menu-Bar mode is enabled. -See the `denote-menu-bar-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `denote-menu-bar-mode'.") -(custom-autoload 'denote-menu-bar-mode "denote" nil) -(autoload 'denote-menu-bar-mode "denote" "\ -Show Denote menu bar. - -This is a global minor mode. If called interactively, toggle the -`Denote-Menu-Bar mode' mode. If the prefix argument is positive, -enable the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='denote-menu-bar-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(autoload 'denote-link-ol-follow "denote" "\ -Find file of type `denote:' matching LINK. -LINK is the identifier of the note, optionally followed by a -search option akin to that of standard Org `file:' link types. -Read Info node `(org) Search Options'. - -Uses the function `denote-directory' to establish the path to the -file. - -(fn LINK)") -(autoload 'denote-link-ol-complete "denote" "\ -Like `denote-link' but for Org integration. -This lets the user complete a link through the `org-insert-link' -interface by first selecting the `denote:' hyperlink type.") -(autoload 'denote-link-ol-store "denote" "\ -Handler for `org-store-link' adding support for denote: links. -Also see the user option `denote-org-store-link-to-heading'.") -(autoload 'denote-link-ol-export "denote" "\ -Export a `denote:' link from Org files. -The LINK, DESCRIPTION, and FORMAT are handled by the export -backend. - -(fn LINK DESCRIPTION FORMAT)") -(eval-after-load 'org `(funcall ',(lambda nil (with-no-warnings (org-link-set-parameters "denote" :follow #'denote-link-ol-follow :face 'denote-faces-link :complete #'denote-link-ol-complete :store #'denote-link-ol-store :export #'denote-link-ol-export))))) -(autoload 'denote-org-capture "denote" "\ -Create new note through `org-capture-templates'. -Use this as a function that returns the path to the new file. -The file is populated with Denote's front matter. It can then be -expanded with the usual specifiers or strings that -`org-capture-templates' supports. - -This function obeys `denote-prompts', but it ignores `file-type', -if present: it always sets the Org file extension for the created -note to ensure that the capture process works as intended, -especially for the desired output of the -`denote-org-capture-specifiers' (which can include arbitrary -text). - -Consult the manual for template samples.") -(autoload 'denote-org-capture-with-prompts "denote" "\ -Like `denote-org-capture' but with optional prompt parameters. - -When called without arguments, do not prompt for anything. Just -return the front matter with title and keyword fields empty and -the date and identifier fields specified. Also make the file -name consist of only the identifier plus the Org file name -extension. - -Otherwise produce a minibuffer prompt for every non-nil value -that corresponds to the TITLE, KEYWORDS, SUBDIRECTORY, DATE, and -TEMPLATE arguments. The prompts are those used by the standard -`denote' command and all of its utility commands. - -When returning the contents that fill in the Org capture -template, the sequence is as follows: front matter, TEMPLATE, and -then the value of the user option `denote-org-capture-specifiers'. - -Important note: in the case of SUBDIRECTORY actual subdirectories -must exist---Denote does not create them. Same principle for -TEMPLATE as templates must exist and are specified in the user -option `denote-templates'. - -(fn &optional TITLE KEYWORDS SUBDIRECTORY DATE TEMPLATE)") -(register-definition-prefixes "denote" '("denote-")) - - -;;; Generated autoloads from denote-journal-extras.el - -(autoload 'denote-journal-extras-new-entry "denote-journal-extras" "\ -Create a new journal entry in variable `denote-journal-extras-directory'. -Use `denote-journal-extras-keyword' as a keyword for the newly -created file. Set the title of the new entry according to the -value of the user option `denote-journal-extras-title-format'. - -With optional DATE as a prefix argument, prompt for a date. If -`denote-date-prompt-use-org-read-date' is non-nil, use the Org -date selection module. - -When called from Lisp DATE is a string and has the same format as -that covered in the documentation of the `denote' function. It -is internally processed by `denote-parse-date'. - -(fn &optional DATE)" t) -(autoload 'denote-journal-extras-new-or-existing-entry "denote-journal-extras" "\ -Locate an existing journal entry or create a new one. -A journal entry is one that has `denote-journal-extras-keyword' as -part of its file name. - -If there are multiple journal entries for the current date, -prompt for one using minibuffer completion. If there is only -one, visit it outright. If there is no journal entry, create one -by calling `denote-journal-extra-new-entry'. - -With optional DATE as a prefix argument, prompt for a date. If -`denote-date-prompt-use-org-read-date' is non-nil, use the Org -date selection module. - -When called from Lisp, DATE is a string and has the same format -as that covered in the documentation of the `denote' function. -It is internally processed by `denote-parse-date'. - -(fn &optional DATE)" t) -(autoload 'denote-journal-extras-link-or-create-entry "denote-journal-extras" "\ -Use `denote-link' on journal entry, creating it if necessary. -A journal entry is one that has `denote-journal-extras-keyword' as -part of its file name. - -If there are multiple journal entries for the current date, -prompt for one using minibuffer completion. If there is only -one, link to it outright. If there is no journal entry, create one -by calling `denote-journal-extra-new-entry' and link to it. - -With optional DATE as a prefix argument, prompt for a date. If -`denote-date-prompt-use-org-read-date' is non-nil, use the Org -date selection module. - -When called from Lisp, DATE is a string and has the same format -as that covered in the documentation of the `denote' function. -It is internally processed by `denote-parse-date'. - -With optional ID-ONLY as a prefix argument create a link that -consists of just the identifier. Else try to also include the -file's title. This has the same meaning as in `denote-link'. - -(fn &optional DATE ID-ONLY)" t) -(register-definition-prefixes "denote-journal-extras" '("denote-journal-extras-")) - - -;;; Generated autoloads from denote-org-extras.el - -(autoload 'denote-org-extras-link-to-heading "denote-org-extras" "\ -Link to file and then specify a heading to extend the link to. - -The resulting link has the following pattern: - -[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading text]]. - -Because only Org files can have links to individual headings, -limit the list of possible files to those which include the .org -file extension (remember that Denote works with many file types, -per the user option `denote-file-type'). - -The user option `denote-org-extras-store-link-to-heading' -determined whether the `org-store-link' function can save a link -to the current heading. Such links look the same as those of -this command, though the functionality defined herein is -independent of it. - -To only link to a file, use the `denote-link' command." '(org-mode)) -(function-put 'denote-org-extras-link-to-heading 'interactive-only 't) -(autoload 'denote-org-extras-extract-org-subtree "denote-org-extras" "\ -Create new Denote note using the current Org subtree as input. -Remove the subtree from its current file and move its contents -into a new Denote file (a subtree is a heading with all of its -contents, including subheadings). - -Take the text of the subtree's top level heading and use it as -the title of the new note. - -If the heading has any tags, use them as the keywords of the new -note. If the Org file has any #+filetags use them as well (Org's -filetags are inherited by the headings). If none of these are -true and the user option `denote-prompts' includes an entry for -keywords, then prompt for keywords. Else do not include any -keywords. - -If the heading has a PROPERTIES drawer, retain it for further -review. - -If the heading's PROPERTIES drawer includes a DATE or CREATED -property, or there exists a CLOSED statement with a timestamp -value, use that to derive the date (or date and time) of the new -note (if there is only a date, the time is taken as 00:00). If -more than one of these is present, the order of preference is -DATE, then CREATED, then CLOSED. If none of these is present, -use the current time. If the `denote-prompts' includes an entry -for a date, then prompt for a date at this stage (also see -`denote-date-prompt-use-org-read-date'). - -For the rest, consult the value of the user option -`denote-prompts' in the following scenaria: - -- Optionally prompt for a subdirectory, otherwise produce the new - note in the variable `denote-directory'. - -- Optionally prompt for a file signature, otherwise do not use - one. - -Make the new note an Org file regardless of the value of -`denote-file-type'." '(org-mode)) -(autoload 'denote-org-extras-convert-links-to-file-type "denote-org-extras" "\ -Convert denote: links to file: links in the current Org buffer. -Ignore all other link types. Also ignore links that do not -resolve to a file in the variable `denote-directory'." '(org-mode)) -(autoload 'denote-org-extras-convert-links-to-denote-type "denote-org-extras" "\ -Convert file: links to denote: links in the current Org buffer. -Ignore all other link types. Also ignore file: links that do not -point to a file with a Denote file name." '(org-mode)) -(autoload 'denote-org-extras-dblock-insert-links "denote-org-extras" "\ -Create Org dynamic block to insert Denote links matching REGEXP. - -(fn REGEXP)" '(org-mode)) -(eval-after-load 'org '(progn (org-dynamic-block-define "denote-links" 'denote-org-extras-dblock-insert-links))) -(autoload 'denote-org-extras-dblock-insert-missing-links "denote-org-extras" "\ -Create Org dynamic block to insert Denote links matching REGEXP. - -(fn REGEXP)" '(org-mode)) -(eval-after-load 'org '(progn (org-dynamic-block-define "denote-missing-links" 'denote-org-extras-dblock-insert-links))) -(autoload 'denote-org-extras-dblock-insert-backlinks "denote-org-extras" "\ -Create Org dynamic block to insert Denote backlinks to current file." '(org-mode)) -(eval-after-load 'org '(progn (org-dynamic-block-define "denote-backlinks" 'denote-org-extras-dblock-insert-backlinks))) -(autoload 'denote-org-extras-dblock-insert-files "denote-org-extras" "\ -Create Org dynamic block to insert Denote files matching REGEXP. -Sort the files according to SORT-BY-COMPONENT, which is a symbol -among `denote-sort-components'. - -(fn REGEXP SORT-BY-COMPONENT)" '(org-mode)) -(eval-after-load 'org '(progn (org-dynamic-block-define "denote-files" 'denote-org-extras-dblock-insert-files))) -(register-definition-prefixes "denote-org-extras" '("denote-org-extras-" "org-dblock-write:denote-")) - - -;;; Generated autoloads from denote-rename-buffer.el - -(defvar denote-rename-buffer-mode nil "\ -Non-nil if Denote-Rename-Buffer mode is enabled. -See the `denote-rename-buffer-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `denote-rename-buffer-mode'.") -(custom-autoload 'denote-rename-buffer-mode "denote-rename-buffer" nil) -(autoload 'denote-rename-buffer-mode "denote-rename-buffer" "\ -Automatically rename Denote buffers to be easier to read. - -A buffer is renamed upon visiting the underlying file. This -means that existing buffers are not renamed until they are -visited again in a new buffer (files are visited with the command -`find-file' or related). - -This is a global minor mode. If called interactively, toggle the -`Denote-Rename-Buffer mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='denote-rename-buffer-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "denote-rename-buffer" '("denote-rename-buffer")) - - -;;; Generated autoloads from denote-silo-extras.el - -(autoload 'denote-silo-extras-create-note "denote-silo-extras" "\ -Select SILO and run `denote' in it. -SILO is a file path from `denote-silo-extras-directories'. - -When called from Lisp, SILO is a file system path to a directory. - -(fn SILO)" t) -(autoload 'denote-silo-extras-open-or-create "denote-silo-extras" "\ -Select SILO and run `denote-open-or-create' in it. -SILO is a file path from `denote-silo-extras-directories'. - -When called from Lisp, SILO is a file system path to a directory. - -(fn SILO)" t) -(autoload 'denote-silo-extras-select-silo-then-command "denote-silo-extras" "\ -Select SILO and run Denote COMMAND in it. -SILO is a file path from `denote-silo-extras-directories', while -COMMAND is one among `denote-silo-extras-commands'. - -When called from Lisp, SILO is a file system path to a directory. - -(fn SILO COMMAND)" t) -(register-definition-prefixes "denote-silo-extras" '("denote-silo-extras-")) - - -;;; Generated autoloads from denote-sort.el - -(autoload 'denote-sort-files "denote-sort" "\ -Returned sorted list of Denote FILES. - -With COMPONENT as a symbol among `denote-sort-components', -sort files based on the corresponding file name component. - -With COMPONENT as a nil value keep the original date-based -sorting which relies on the identifier of each file name. - -With optional REVERSE as a non-nil value, reverse the sort order. - -(fn FILES COMPONENT &optional REVERSE)") -(autoload 'denote-sort-dired "denote-sort" "\ -Produce Dired dired-buffer with sorted files from variable `denote-directory'. -When called interactively, prompt for FILES-MATCHING-REGEXP, -SORT-BY-COMPONENT, and REVERSE. - -1. FILES-MATCHING-REGEXP limits the list of Denote files to - those matching the provided regular expression. - -2. SORT-BY-COMPONENT sorts the files by their file name - component (one among `denote-sort-components'). - -3. REVERSE is a boolean to reverse the order when it is a non-nil value. - -When called from Lisp, the arguments are a string, a keyword, and -a non-nil value, respectively. - -(fn FILES-MATCHING-REGEXP SORT-BY-COMPONENT REVERSE)" t) -(register-definition-prefixes "denote-sort" '("denote-sort-")) - -;;; End of scraped data - -(provide 'denote-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; denote-autoloads.el ends here blob - c017fab2996d7bc0f661798482dd37722c65901a (mode 644) blob + /dev/null --- elpa/denote-2.3.5/denote-journal-extras.el +++ /dev/null @@ -1,249 +0,0 @@ -;;; denote-journal-extras.el --- Convenience functions for daily journaling -*- lexical-binding: t; -*- - -;; Copyright (C) 2023-2024 Free Software Foundation, Inc. - -;; Author: Protesilaos Stavrou -;; Maintainer: Protesilaos Stavrou -;; URL: https://github.com/protesilaos/denote - -;; This file is NOT part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This is a set of optional convenience functions that used to be -;; provided in the Denote manual. They facilitate the use of Denote -;; for daily journaling. - -;;; Code: - -(require 'denote) - -(defgroup denote-journal-extras nil - "Denote for daily journaling." - :group 'denote - :link '(info-link "(denote) Top") - :link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote")) - -(defcustom denote-journal-extras-directory - (expand-file-name "journal" denote-directory) - "Directory for storing daily journal entries. -This can either be the same as the variable `denote-directory' or -a subdirectory of it. - -A value of nil means to use the variable `denote-directory'. -Journal entries will thus be in a flat listing together with all -other notes. They can still be retrieved easily by searching for -the `denote-journal-extras-keyword'." - :group 'denote-journal-extras - :type '(choice (directory :tag "Provide directory path (is created if missing)") - (const :tag "Use the `denote-directory'" nil))) - -(defcustom denote-journal-extras-keyword "journal" - "Single word keyword to tag journal entries. -It is used by `denote-journal-extras-new-entry' to add a keyword -to the newly created file." - :group 'denote-journal-extras - :type 'string) - -(defcustom denote-journal-extras-title-format 'day-date-month-year-24h - "Date format to construct the title with `denote-journal-extras-new-entry'. -The value is either a symbol or an arbitrary string that is -passed to `format-time-string' (consult its documentation for the -technicalities). - -Acceptable symbols and their corresponding styles are: - -| Symbol | Style | -|-------------------------+-----------------------------------| -| day | Monday | -| day-date-month-year | Monday 19 September 2023 | -| day-date-month-year-24h | Monday 19 September 2023 20:49 | -| day-date-month-year-12h | Monday 19 September 2023 08:49 PM | - -With a nil value, make `denote-journal-extras-new-entry' prompt -for a title." - :group 'denote-journal-extras - :type '(choice - (const :tag "Prompt for title with `denote-journal-extras-new-entry'" nil) - (const :tag "Monday" - :doc "The `format-time-string' is: %A" - day) - (const :tag "Monday 19 September 2023" - :doc "The `format-time-string' is: %A %e %B %Y" - day-date-month-year) - (const :tag "Monday 19 September 2023 20:49" - :doc "The `format-time-string' is: %A %e %B %Y %H:%M" - day-date-month-year-24h) - (const :tag "Monday 19 September 2023 08:49 PM" - :doc "The `format-time-string' is: %A %e %B %Y %I:%M %^p" - day-date-month-year-12h) - (string :tag "Custom string with `format-time-string' specifiers"))) - -(defcustom denote-journal-extras-hook nil - "Normal hook called after `denote-journal-extras-new-entry'. -Use this to, for example, set a timer after starting a new -journal entry (refer to the `tmr' package on GNU ELPA)." - :group 'denote-journal-extras - :type 'hook) - -(defun denote-journal-extras-directory () - "Make the variable `denote-journal-extras-directory' and its parents." - (if-let (((stringp denote-journal-extras-directory)) - (directory (file-name-as-directory (expand-file-name denote-journal-extras-directory)))) - (progn - (when (not (file-directory-p denote-journal-extras-directory)) - (make-directory directory :parents)) - directory) - (denote-directory))) - -(defun denote-journal-extras-daily--title-format (&optional date) - "Return present date in `denote-journal-extras-title-format' or prompt for title. -With optional DATE, use it instead of the present date. DATE has -the same format as that returned by `current-time'." - (format-time-string - (if (and denote-journal-extras-title-format - (stringp denote-journal-extras-title-format)) - denote-journal-extras-title-format - (pcase denote-journal-extras-title-format - ('day "%A") - ('day-date-month-year "%A %e %B %Y") - ('day-date-month-year-24h "%A %e %B %Y %H:%M") - ('day-date-month-year-12h "%A %e %B %Y %I:%M %^p") - (_ (denote-title-prompt (format-time-string "%F" date))))) - date)) - -(defun denote-journal-extras--get-template () - "Return template that has `journal' key in `denote-templates'. -If no template with `journal' key exists but `denote-templates' -is non-nil, prompt the user for a template among -`denote-templates'. Else return nil. - -Also see `denote-journal-extras-new-entry'." - (if-let ((template (alist-get 'journal denote-templates))) - template - (when denote-templates - (denote-template-prompt)))) - -;;;###autoload -(defun denote-journal-extras-new-entry (&optional date) - "Create a new journal entry in variable `denote-journal-extras-directory'. -Use `denote-journal-extras-keyword' as a keyword for the newly -created file. Set the title of the new entry according to the -value of the user option `denote-journal-extras-title-format'. - -With optional DATE as a prefix argument, prompt for a date. If -`denote-date-prompt-use-org-read-date' is non-nil, use the Org -date selection module. - -When called from Lisp DATE is a string and has the same format as -that covered in the documentation of the `denote' function. It -is internally processed by `denote-parse-date'." - (interactive (list (when current-prefix-arg (denote-date-prompt)))) - (let ((internal-date (denote-parse-date date)) - (denote-directory (denote-journal-extras-directory))) - (denote - (denote-journal-extras-daily--title-format internal-date) - `(,denote-journal-extras-keyword) - nil nil date - (denote-journal-extras--get-template)) - (run-hooks 'denote-journal-extras-hook))) - -(defun denote-journal-extras--entry-today (&optional date) - "Return list of files matching a journal for today or optional DATE. -DATE has the same format as that returned by `denote-parse-date'." - (denote-directory-files - (format "%sT[0-9]\\{6\\}.*_%s" - (format-time-string "%Y%m%d" date) - denote-journal-extras-keyword))) - -;;;###autoload -(defun denote-journal-extras-new-or-existing-entry (&optional date) - "Locate an existing journal entry or create a new one. -A journal entry is one that has `denote-journal-extras-keyword' as -part of its file name. - -If there are multiple journal entries for the current date, -prompt for one using minibuffer completion. If there is only -one, visit it outright. If there is no journal entry, create one -by calling `denote-journal-extra-new-entry'. - -With optional DATE as a prefix argument, prompt for a date. If -`denote-date-prompt-use-org-read-date' is non-nil, use the Org -date selection module. - -When called from Lisp, DATE is a string and has the same format -as that covered in the documentation of the `denote' function. -It is internally processed by `denote-parse-date'." - (interactive - (list - (when current-prefix-arg - (denote-date-prompt)))) - (let* ((internal-date (denote-parse-date date)) - (files (denote-journal-extras--entry-today internal-date))) - (cond - ((length> files 1) - (find-file (completing-read "Select journal entry: " files nil :require-match))) - (files - (find-file (car files))) - (t - (denote-journal-extras-new-entry date))))) - -;;;###autoload -(defun denote-journal-extras-link-or-create-entry (&optional date id-only) - "Use `denote-link' on journal entry, creating it if necessary. -A journal entry is one that has `denote-journal-extras-keyword' as -part of its file name. - -If there are multiple journal entries for the current date, -prompt for one using minibuffer completion. If there is only -one, link to it outright. If there is no journal entry, create one -by calling `denote-journal-extra-new-entry' and link to it. - -With optional DATE as a prefix argument, prompt for a date. If -`denote-date-prompt-use-org-read-date' is non-nil, use the Org -date selection module. - -When called from Lisp, DATE is a string and has the same format -as that covered in the documentation of the `denote' function. -It is internally processed by `denote-parse-date'. - -With optional ID-ONLY as a prefix argument create a link that -consists of just the identifier. Else try to also include the -file's title. This has the same meaning as in `denote-link'." - (interactive - (pcase current-prefix-arg - ('(16) (list (denote-date-prompt) :id-only)) - ('(4) (list (denote-date-prompt))))) - (let* ((internal-date (denote-parse-date date)) - (files (denote-journal-extras--entry-today internal-date)) - (path)) - (cond - ((length> files 1) - (setq path (completing-read "Select journal entry: " files nil :require-match))) - (files - (setq path (car files))) - (t - (save-window-excursion - (denote-journal-extras-new-entry date) - (save-buffer) - (setq path (buffer-file-name))))) - (denote-link path - (denote-filetype-heuristics (buffer-file-name)) - (denote--link-get-description path) - id-only))) - -(provide 'denote-journal-extras) -;;; denote-journal-extras.el ends here blob - 03a97dbce0e8ac1329591adbdf74df68cdde1487 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/denote-org-dblock.el +++ /dev/null @@ -1,70 +0,0 @@ -;;; denote-org-dblock.el --- Compatibility alieases for Denote Org Dynamic blocks -*- lexical-binding: t -*- - -;; Copyright (C) 2022-2024 Free Software Foundation, Inc. - -;; Authors: Elias Storms , -;; Protesilaos Stavrou -;; Maintainer: Protesilaos Stavrou -;; Deprecated-since: 3.0.0 -;; URL: https://github.com/protesilaos/denote - -;; This file is NOT part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. -;; -;; This program 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 this program. If not, see . - -;;; Commentary: -;; -;; This file defines compatibility aliases for Org dynamic blocks set -;; up by Denote. The new source of these is the denote-org-extras.el. -;; -;; Below is the old commentary. -;; -;; * * * -;; -;; This file defines Org dynamic blocks using the facility described -;; in the Org manual. Evaluate this: -;; -;; (info "(org) Dynamic Blocks") -;; -;; The dynamic blocks defined herein are documented at length in the -;; Denote manual. See the following node and its subsections: -;; -;; (info "(denote) Use Org dynamic blocks") - -;;; Code: - -(require 'denote-org-extras) - -(define-obsolete-function-alias - 'denote-org-dblock-insert-links - 'denote-org-extras-dblock-insert-links - "3.0.0") - -(define-obsolete-function-alias - 'denote-org-dblock-insert-backlinks - 'denote-org-extras-dblock-insert-backlinks - "3.0.0") - -(define-obsolete-function-alias - 'denote-org-dblock-insert-files - 'denote-org-extras-dblock-insert-files - "3.0.0") - -(display-warning - 'denote - "`denote-org-dblock.el' is obsolete; use `denote-org-extras.el'" - :warning) - -(provide 'denote-org-dblock) -;;; denote-org-dblock.el ends here blob - 38772bc9be3a3c9af33a71406f2e4ea147335585 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/denote-org-extras.el +++ /dev/null @@ -1,544 +0,0 @@ -;;; denote-org-extras.el --- Denote extensions for Org mode -*- lexical-binding: t -*- - -;; Copyright (C) 2024 Free Software Foundation, Inc. - -;; Author: Protesilaos Stavrou -;; Maintainer: Protesilaos Stavrou -;; URL: https://github.com/protesilaos/denote - -;; This file is NOT part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. -;; -;; This program 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 this program. If not, see . - -;;; Commentary: -;; -;; WORK-IN-PROGRESS - -;;; Code: - -(require 'denote) -(require 'denote-sort) -(require 'org) - -;;;; Link to file and heading - -(defun denote-org-extras--get-outline (file) - "Return `outline-regexp' headings and line numbers of FILE." - (with-current-buffer (find-file-noselect file) - (let ((outline-regexp (format "^\\(?:%s\\)" (or (bound-and-true-p outline-regexp) "[*\^L]+"))) - candidates) - (save-excursion - (goto-char (point-min)) - (while (if (bound-and-true-p outline-search-function) - (funcall outline-search-function) - (re-search-forward outline-regexp nil t)) - (push - ;; NOTE 2024-01-20: The -5 (minimum width) is a - ;; sufficiently high number to keep the alignment - ;; consistent in most cases. Larger files will simply - ;; shift the heading text in minibuffer, but this is not an - ;; issue anymore. - (format "%-5s %s" - (line-number-at-pos (point)) - (buffer-substring-no-properties (line-beginning-position) (line-end-position))) - candidates) - (goto-char (1+ (line-end-position))))) - (if candidates - (nreverse candidates) - (user-error "No outline"))))) - -(defun denote-org-extras--outline-prompt (&optional file) - "Prompt for outline among headings retrieved by `denote-org-extras--get-outline'. -With optional FILE use the outline of it, otherwise use that of -the current file." - (completing-read - (format "Select heading inside `%s': " - (propertize (file-name-nondirectory file) 'face 'denote-faces-prompt-current-name)) - (denote--completion-table-no-sort 'imenu (denote-org-extras--get-outline (or file buffer-file-name))) - nil :require-match)) - -(defun denote-org-extras--get-heading-and-id-from-line (line file) - "Return heading text and CUSTOM_ID from the given LINE in FILE." - (with-current-buffer (find-file-noselect file) - (save-excursion - (goto-char (point-min)) - (forward-line line) - (cons (denote-link-ol-get-heading) (denote-link-ol-get-id))))) - -(defun denote-org-extras-format-link-with-heading (file heading-id description) - "Prepare link to FILE with HEADING-ID using DESCRIPTION." - (format "[[denote:%s::#%s][%s]]" - (denote-retrieve-filename-identifier file) - heading-id - description)) - -;;;###autoload -(defun denote-org-extras-link-to-heading () - "Link to file and then specify a heading to extend the link to. - -The resulting link has the following pattern: - -[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading text]]. - -Because only Org files can have links to individual headings, -limit the list of possible files to those which include the .org -file extension (remember that Denote works with many file types, -per the user option `denote-file-type'). - -The user option `denote-org-extras-store-link-to-heading' -determined whether the `org-store-link' function can save a link -to the current heading. Such links look the same as those of -this command, though the functionality defined herein is -independent of it. - -To only link to a file, use the `denote-link' command." - (declare (interactive-only t)) - (interactive nil org-mode) - (unless (derived-mode-p 'org-mode) - (user-error "Links to headings only work between Org files")) - (when-let ((file (denote-file-prompt ".*\\.org")) - (file-text (denote--link-get-description file)) - (heading (denote-org-extras--outline-prompt file)) - (line (string-to-number (car (split-string heading "\t")))) - (heading-data (denote-org-extras--get-heading-and-id-from-line line file)) - (heading-text (car heading-data)) - (heading-id (cdr heading-data)) - (description (denote-link-format-heading-description file-text heading-text))) - (insert (denote-org-extras-format-link-with-heading file heading-id description)))) - -;;;; Extract subtree into its own note - -(defun denote-org-extras--get-heading-date () - "Try to return a timestamp for the current Org heading. -This can be used as the value for the DATE argument of the -`denote' command." - (when-let ((pos (point)) - (timestamp (or (org-entry-get pos "DATE") - (org-entry-get pos "CREATED") - (org-entry-get pos "CLOSED")))) - (date-to-time timestamp))) - -;;;###autoload -(defun denote-org-extras-extract-org-subtree () - "Create new Denote note using the current Org subtree as input. -Remove the subtree from its current file and move its contents -into a new Denote file (a subtree is a heading with all of its -contents, including subheadings). - -Take the text of the subtree's top level heading and use it as -the title of the new note. - -If the heading has any tags, use them as the keywords of the new -note. If the Org file has any #+filetags use them as well (Org's -filetags are inherited by the headings). If none of these are -true and the user option `denote-prompts' includes an entry for -keywords, then prompt for keywords. Else do not include any -keywords. - -If the heading has a PROPERTIES drawer, retain it for further -review. - -If the heading's PROPERTIES drawer includes a DATE or CREATED -property, or there exists a CLOSED statement with a timestamp -value, use that to derive the date (or date and time) of the new -note (if there is only a date, the time is taken as 00:00). If -more than one of these is present, the order of preference is -DATE, then CREATED, then CLOSED. If none of these is present, -use the current time. If the `denote-prompts' includes an entry -for a date, then prompt for a date at this stage (also see -`denote-date-prompt-use-org-read-date'). - -For the rest, consult the value of the user option -`denote-prompts' in the following scenaria: - -- Optionally prompt for a subdirectory, otherwise produce the new - note in the variable `denote-directory'. - -- Optionally prompt for a file signature, otherwise do not use - one. - -Make the new note an Org file regardless of the value of -`denote-file-type'." - (interactive nil org-mode) - (unless (derived-mode-p 'org-mode) - (user-error "Headings can only be extracted from Org files")) - (if-let ((text (org-get-entry)) - (heading (denote-link-ol-get-heading))) - (let ((tags (org-get-tags)) - (date (denote-org-extras--get-heading-date)) - subdirectory - signature) - (dolist (prompt denote-prompts) - (pcase prompt - ('keywords (when (not tags) - (setq tags (denote-keywords-prompt)))) - ('subdirectory (setq subdirectory (denote-subdirectory-prompt))) - ('date (when (not date) (setq date (denote-date-prompt)))) - ('signature (setq signature (denote-signature-prompt))))) - (delete-region (org-entry-beginning-position) - (save-excursion (org-end-of-subtree t) (point))) - (denote heading tags 'org subdirectory date nil signature) - (insert text)) - (user-error "No subtree to extract; aborting"))) - -;;;; Convert links from `:denote' to `:file' and vice versa - -;; TODO 2024-02-28: Do we need to convert between other link types? I -;; think not, since the `denote:' type is modelled after the `file:' -;; one. -(defun denote-org-extras--get-link-type-regexp (type) - "Return regexp for Org link TYPE. -TYPE is a symbol of either `file' or `denote'. - -The regexp consists of four groups. Group 1 is the link type, 2 -is the target, 3 is the target's search terms, and 4 is the -description." - (let ((group-1)) - (pcase type - ('denote (setq group-1 "denote")) - ('file (setq group-1 "file")) - (_ (error "`%s' is an unknown link type" type))) - (format "\\[\\[\\(?1:%s:\\)\\(?:\\(?2:.*?\\)\\(?3:::.*\\)?\\]\\|\\]\\)\\(?4:\\[\\(?:.*?\\)\\]\\)?\\]" group-1))) - -;;;###autoload -(defun denote-org-extras-convert-links-to-file-type () - "Convert denote: links to file: links in the current Org buffer. -Ignore all other link types. Also ignore links that do not -resolve to a file in the variable `denote-directory'." - (interactive nil org-mode) - (if (derived-mode-p 'org-mode) - (progn - (goto-char (point-min)) - (while (re-search-forward (denote-org-extras--get-link-type-regexp 'denote) nil :no-error) - (let* ((id (match-string-no-properties 2)) - (search (or (match-string-no-properties 3) "")) - (desc (or (match-string-no-properties 4) "")) - (file (save-match-data (denote-get-path-by-id id)))) - (when id - (let ((new-text (if desc - (format "[[file:%s%s]%s]" file search desc) - (format "[[file:%s%s]]" file search)))) - (replace-match new-text :fixed-case :literal))))) - ;; TODO 2024-02-28: notify how many changed. - (message "Converted `denote:' links to `file:' links")) - (user-error "The current file is not using Org mode"))) - -;;;###autoload -(defun denote-org-extras-convert-links-to-denote-type () - "Convert file: links to denote: links in the current Org buffer. -Ignore all other link types. Also ignore file: links that do not -point to a file with a Denote file name." - (interactive nil org-mode) - (if (derived-mode-p 'org-mode) - (progn - (goto-char (point-min)) - (while (re-search-forward (denote-org-extras--get-link-type-regexp 'file) nil :no-error) - (let* ((file (match-string-no-properties 2)) - (search (or (match-string-no-properties 3) "")) - (desc (or (match-string-no-properties 4) "")) - (id (save-match-data (denote-retrieve-filename-identifier file)))) - (when id - (let ((new-text (if desc - (format "[[denote:%s%s]%s]" id search desc) - (format "[[denote:%s%s]]" id search)))) - (replace-match new-text :fixed-case :literal))))) - ;; TODO 2024-02-28: notify how many changed. - (message "Converted as `file:' links to `denote:' links")) - (user-error "The current file is not using Org mode"))) - -;;;; Org dynamic blocks - -;; NOTE 2024-01-22 12:26:13 +0200: The following is copied from the -;; now-deleted denote-org-dblock.el. Its original author was Elias -;; Storms , with substantial contributions and -;; further developments by me (Protesilaos). - -;; This section defines Org dynamic blocks using the facility described -;; in the Org manual. Evaluate this: -;; -;; (info "(org) Dynamic Blocks") -;; -;; The dynamic blocks defined herein are documented at length in the -;; Denote manual. See the following node and its subsections: -;; -;; (info "(denote) Use Org dynamic blocks") - -;;;;; Common helper functions - -(defun denote-org-extras-dblock--files (files-matching-regexp &optional sort-by-component reverse) - "Return list of FILES-MATCHING-REGEXP in variable `denote-directory'. -SORT-BY-COMPONENT and REVERSE have the same meaning as -`denote-sort-files'. If both are nil, do not try to perform any -sorting. - -Also see `denote-org-extras-dblock--files-missing-only'." - (cond - ((and sort-by-component reverse) - (denote-sort-get-directory-files files-matching-regexp sort-by-component reverse :omit-current)) - (sort-by-component - (denote-sort-get-directory-files files-matching-regexp sort-by-component nil :omit-current)) - (reverse - (denote-sort-get-directory-files files-matching-regexp :no-component-specified reverse :omit-current)) - (t - (denote-directory-files files-matching-regexp :omit-current)))) - -(defun denote-org-extras-dblock--get-missing-links (regexp) - "Return list of missing links to all notes matching REGEXP. -Missing links are those for which REGEXP does not have a match in -the current buffer." - (let ((found-files (denote-directory-files regexp :omit-current)) - (linked-files (denote-link--expand-identifiers denote-org-link-in-context-regexp))) - (if-let ((final-files (seq-difference found-files linked-files))) - final-files - (message "All links matching `%s' are present" regexp) - '()))) - -(defun denote-org-extras-dblock--files-missing-only (files-matching-regexp &optional sort-by-component reverse) - "Return list of missing links to FILES-MATCHING-REGEXP. -SORT-BY-COMPONENT and REVERSE have the same meaning as -`denote-sort-files'. If both are nil, do not try to perform any -sorting. - -Also see `denote-org-extras-dblock--files'." - (denote-sort-files - (denote-org-extras-dblock--get-missing-links files-matching-regexp) - sort-by-component - reverse)) - -;;;;; Dynamic block to insert links - -;;;###autoload -(defun denote-org-extras-dblock-insert-links (regexp) - "Create Org dynamic block to insert Denote links matching REGEXP." - (interactive - (list - (denote-files-matching-regexp-prompt)) - org-mode) - (org-create-dblock (list :name "denote-links" - :regexp regexp - :sort-by-component nil - :reverse-sort nil - :id-only nil)) - (org-update-dblock)) - -;; NOTE 2024-03-30: This is how the autoload is done in org.el. -;;;###autoload -(eval-after-load 'org - '(progn - (org-dynamic-block-define "denote-links" 'denote-org-extras-dblock-insert-links))) - -(defun org-dblock-write:denote-links (params) - "Function to update `denote-links' Org Dynamic blocks. -Used by `org-dblock-update' with PARAMS provided by the dynamic block." - (let* ((regexp (plist-get params :regexp)) - (rx (if (listp regexp) (macroexpand `(rx ,regexp)) regexp)) - (sort (plist-get params :sort-by-component)) - (reverse (plist-get params :reverse-sort)) - (block-name (plist-get params :block-name)) - (files (denote-org-extras-dblock--files rx sort reverse))) - (when block-name (insert "#+name: " block-name "\n")) - (denote-link--insert-links files 'org (plist-get params :id-only) :no-other-sorting) - (join-line))) ; remove trailing empty line - -;;;;; Dynamic block to insert missing links - -;;;###autoload -(defun denote-org-extras-dblock-insert-missing-links (regexp) - "Create Org dynamic block to insert Denote links matching REGEXP." - (interactive - (list - (denote-files-matching-regexp-prompt)) - org-mode) - (org-create-dblock (list :name "denote-missing-links" - :regexp regexp - :sort-by-component nil - :reverse-sort nil - :id-only nil)) - (org-update-dblock)) - -;; NOTE 2024-03-30: This is how the autoload is done in org.el. -;;;###autoload -(eval-after-load 'org - '(progn - (org-dynamic-block-define "denote-missing-links" 'denote-org-extras-dblock-insert-links))) - -(defun org-dblock-write:denote-missing-links (params) - "Function to update `denote-links' Org Dynamic blocks. -Used by `org-dblock-update' with PARAMS provided by the dynamic block." - (let* ((regexp (plist-get params :regexp)) - (rx (if (listp regexp) (macroexpand `(rx ,regexp)) regexp)) - (sort (plist-get params :sort-by-component)) - (reverse (plist-get params :reverse-sort)) - (block-name (plist-get params :block-name)) - (files (denote-org-extras-dblock--files-missing-only rx sort reverse))) - (when block-name (insert "#+name: " block-name "\n")) - (denote-link--insert-links files 'org (plist-get params :id-only) :no-other-sorting) - (join-line))) ; remove trailing empty line - -;;;;; Dynamic block to insert backlinks - -(defun denote-org-extras-dblock--maybe-sort-backlinks (files sort-by-component reverse) - "Sort backlink FILES if SORT-BY-COMPONENT and/or REVERSE is non-nil." - (cond - ((and sort-by-component reverse) - (denote-sort-files files sort-by-component reverse)) - (sort-by-component - (denote-sort-files files sort-by-component)) - (reverse - (denote-sort-files files :no-component-specified reverse)) - (t - files))) - -;;;###autoload -(defun denote-org-extras-dblock-insert-backlinks () - "Create Org dynamic block to insert Denote backlinks to current file." - (interactive nil org-mode) - (org-create-dblock (list :name "denote-backlinks" - :sort-by-component nil - :reverse-sort nil - :id-only nil)) - (org-update-dblock)) - -;; NOTE 2024-03-30: This is how the autoload is done in org.el. -;;;###autoload -(eval-after-load 'org - '(progn - (org-dynamic-block-define "denote-backlinks" 'denote-org-extras-dblock-insert-backlinks))) - -(defun org-dblock-write:denote-backlinks (params) - "Function to update `denote-backlinks' Org Dynamic blocks. -Used by `org-dblock-update' with PARAMS provided by the dynamic block." - (when-let ((files (denote-link-return-backlinks))) - (let* ((sort (plist-get params :sort-by-component)) - (reverse (plist-get params :reverse-sort)) - (files (denote-org-extras-dblock--maybe-sort-backlinks files sort reverse))) - (denote-link--insert-links files 'org (plist-get params :id-only) :no-other-sorting) - (join-line)))) ; remove trailing empty line - -;;;;; Dynamic block to insert entire file contents - -(defun denote-org-extras-dblock--get-file-contents (file &optional no-front-matter add-links) - "Insert the contents of FILE. -With optional NO-FRONT-MATTER as non-nil, try to remove the front -matter from the top of the file. If NO-FRONT-MATTER is a number, -remove that many lines starting from the top. If it is any other -non-nil value, delete from the top until the first blank line. - -With optional ADD-LINKS as non-nil, first insert a link to the -file and then insert its contents. In this case, format the -contents as a typographic list. If ADD-LINKS is `id-only', then -insert links as `denote-link' does when supplied with an ID-ONLY -argument." - (when (denote-file-is-note-p file) - (with-temp-buffer - (when add-links - (insert - (format "- %s\n\n" - (denote-format-link - file - (denote--link-get-description file) - 'org - (eq add-links 'id-only))))) - (let ((beginning-of-contents (point))) - (insert-file-contents file) - (when no-front-matter - (delete-region - (if (natnump no-front-matter) - (progn (forward-line no-front-matter) (line-beginning-position)) - (1+ (re-search-forward "^$" nil :no-error 1))) - beginning-of-contents)) - (when add-links - (indent-region beginning-of-contents (point-max) 2))) - (buffer-string)))) - -(defvar denote-org-extras-dblock-file-contents-separator - (concat "\n\n" (make-string 50 ?-) "\n\n\n") - "Fallback separator used by `denote-org-extras-dblock-add-files'.") - -(defun denote-org-extras-dblock--separator (separator) - "Return appropriate value of SEPARATOR for `denote-org-extras-dblock-add-files'." - (cond - ((null separator) "") - ((stringp separator) separator) - (t denote-org-extras-dblock-file-contents-separator))) - -(defun denote-org-extras-dblock-add-files (regexp &optional separator no-front-matter add-links sort-by-component reverse) - "Insert files matching REGEXP. - -Seaprate them with the optional SEPARATOR. If SEPARATOR is nil, -use the `denote-org-extras-dblock-file-contents-separator'. - -If optional NO-FRONT-MATTER is non-nil try to remove the front -matter from the top of the file. Do it by finding the first -blank line, starting from the top of the buffer. - -If optional ADD-LINKS is non-nil, first insert a link to the file -and then insert its contents. In this case, format the contents -as a typographic list. - -If optional SORT-BY-COMPONENT is a symbol among `denote-sort-components', -sort files matching REGEXP by the corresponding Denote file name -component. If the symbol is not among `denote-sort-components', -fall back to the default identifier-based sorting. - -If optional REVERSE is non-nil reverse the sort order." - (let* ((files (denote-org-extras-dblock--files regexp sort-by-component reverse)) - (files-contents (mapcar - (lambda (file) (denote-org-extras-dblock--get-file-contents file no-front-matter add-links)) - files))) - (insert (string-join files-contents (denote-org-extras-dblock--separator separator))))) - -;;;###autoload -(defun denote-org-extras-dblock-insert-files (regexp sort-by-component) - "Create Org dynamic block to insert Denote files matching REGEXP. -Sort the files according to SORT-BY-COMPONENT, which is a symbol -among `denote-sort-components'." - (interactive - (list - (denote-files-matching-regexp-prompt) - (denote-sort-component-prompt)) - org-mode) - (org-create-dblock (list :name "denote-files" - :regexp regexp - :sort-by-component sort-by-component - :reverse-sort nil - :no-front-matter nil - :file-separator nil - :add-links nil)) - (org-update-dblock)) - -;; NOTE 2024-03-30: This is how the autoload is done in org.el. -;;;###autoload -(eval-after-load 'org - '(progn - (org-dynamic-block-define "denote-files" 'denote-org-extras-dblock-insert-files))) - -(defun org-dblock-write:denote-files (params) - "Function to update `denote-files' Org Dynamic blocks. -Used by `org-dblock-update' with PARAMS provided by the dynamic block." - (let* ((regexp (plist-get params :regexp)) - (rx (if (listp regexp) (macroexpand `(rx ,regexp)) regexp)) - (sort (plist-get params :sort-by-component)) - (reverse (plist-get params :reverse-sort)) - (block-name (plist-get params :block-name)) - (separator (plist-get params :file-separator)) - (no-f-m (plist-get params :no-front-matter)) - (add-links (plist-get params :add-links))) - (when block-name (insert "#+name: " block-name "\n")) - (when rx (denote-org-extras-dblock-add-files rx separator no-f-m add-links sort reverse))) - (join-line)) ; remove trailing empty line - - -(provide 'denote-org-extras) -;;; denote-org-extras.el ends here blob - 3f77c277346522b8a454f5e529d3398f4d10d116 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/denote-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from denote.el -*- no-byte-compile: t -*- -(define-package "denote" "2.3.5" "Simple notes with an efficient file-naming scheme" '((emacs "28.1")) :commit "914e5be6c1ad319cf924aaddc3515aaffe1d4bcc" :authors '(("Protesilaos Stavrou" . "info@protesilaos.com")) :maintainer '("Protesilaos Stavrou" . "info@protesilaos.com") :url "https://github.com/protesilaos/denote") blob - 1335d3a22c7e8cf1e62e3db855dcab15123bb3f0 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/denote-rename-buffer.el +++ /dev/null @@ -1,159 +0,0 @@ -;;; denote-rename-buffer.el --- Rename Denote buffers to be shorter and easier to read -*- lexical-binding: t -*- - -;; Copyright (C) 2023-2024 Free Software Foundation, Inc. - -;; Author: Protesilaos Stavrou -;; Maintainer: Protesilaos Stavrou -;; URL: https://github.com/protesilaos/denote - -;; This file is NOT part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. -;; -;; This program 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 this program. If not, see . - -;;; Commentary: -;; -;; Rename Denote buffers to be shorter and easier to read. Enable -;; `denote-rename-buffer-mode' to automatically rename the buffer of a -;; Denote file. The renaming function is specified in the user option -;; `denote-rename-buffer-function'. - -;;; Code: - -(require 'denote) - -(defgroup denote-rename-buffer nil - "Rename Denote buffers to be shorter and easier to read." - :group 'denote - :link '(info-link "(denote) Top") - :link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote")) - -(defcustom denote-rename-buffer-format "%t" - "The format of the buffer name `denote-rename-buffer' should use. -Thie value is a string that treats specially the following -specifiers: - -- The %t is the Denote TITLE of the file. -- The %i is the Denote IDENTIFIER of the file. -- The %d is the same as %i (DATE mnemonic). -- The %s is the Denote SIGNATURE of the file. -- The %k is the Denote KEYWORDS of the file. -- The %% is a literal percent sign. - -In addition, the following flags are available for each of the specifiers: - -- 0 :: Pad to the width, if given, with zeros instead of spaces. -- - :: Pad to the width, if given, on the right instead of the left. -- < :: Truncate to the width and precision, if given, on the left. -- > :: Truncate to the width and precision, if given, on the right. -- ^ :: Convert to upper case. -- _ :: Convert to lower case. - -When combined all together, the above are written thus: - - %SPECIFIER-CHARACTER - -Any other string it taken as-is. Users may want, for example, to -include some text that makes Denote buffers stand out, such as -a [D] prefix." - :type 'string - :package-version '(denote . "2.1.0") - :group 'denote-rename-buffer) - -(defcustom denote-rename-buffer-function #'denote-rename-buffer - "Symbol of function that is called to rename the Denote file buffer. -The default `denote-rename-buffer' function uses the pattern -described in `denote-rename-buffer-format'. - -Users can set this variable to an arbitrary function that does -something else. The function is called without arguments from -the `find-file-hook' and `denote-after-new-note-hook'. - -A nil value for this variable means that the title of the Denote -buffer will be used, if available." - :type '(choice - (const :tag "Rename using the `denote-rename-buffer-format'" denote-rename-buffer) - (function :tag "Use a custom renaming function")) - :package-version '(denote . "2.1.0") - :group 'denote-rename-buffer) - -(defun denote-rename-buffer--format (buffer) - "Parse the BUFFER through the `denote-rename-buffer-format'." - (when-let ((file (buffer-file-name buffer)) - (type (denote-filetype-heuristics file))) - (string-trim - (format-spec denote-rename-buffer-format - (list (cons ?t (cond - ((denote-retrieve-front-matter-title-value file type)) - ((denote-retrieve-filename-title file)) - (t ""))) - (cons ?i (or (denote-retrieve-filename-identifier file) "")) - (cons ?d (or (denote-retrieve-filename-identifier file) "")) - (cons ?s (or (denote-retrieve-filename-signature file) "")) - (cons ?k (if-let ((kws (denote-retrieve-front-matter-keywords-value file type))) - (denote-keywords-combine kws) - (or (denote-retrieve-filename-keywords file) ""))) - (cons ?% "%")) - 'delete)))) - -(defun denote-rename-buffer (&optional buffer) - "Rename current buffer or optional BUFFER with `denote-rename-buffer-format'. -The symbol of this function is the default value of the user -option `denote-rename-buffer-function' and is thus used by the -`denote-rename-buffer-mode'." - (when-let ((file (buffer-file-name buffer)) - ((denote-file-has-identifier-p file)) - (new-name (denote-rename-buffer--format (or buffer (current-buffer)))) - ((not (string-blank-p new-name)))) - (rename-buffer new-name :unique))) - -(make-obsolete - 'denote-rename-buffer-with-title - 'denote-rename-buffer - "2.1.0") - -(make-obsolete - 'denote-rename-buffer-with-identifier - 'denote-rename-buffer - "2.1.0") - -(defun denote-rename-buffer--fallback (&optional buffer) - "Fallback to rename BUFFER or `current-buffer'. -This is called if `denote-rename-buffer-rename-function' is nil." - (let ((denote-rename-buffer-format "%t")) - (denote-rename-buffer buffer))) - -(defun denote-rename-buffer-rename-function-or-fallback () - "Call `denote-rename-buffer-function' or its fallback to rename with title. -Add this to `find-file-hook' and `denote-after-new-note-hook'." - (funcall (or denote-rename-buffer-function #'denote-rename-buffer--fallback))) - -;;;###autoload -(define-minor-mode denote-rename-buffer-mode - "Automatically rename Denote buffers to be easier to read. -A buffer is renamed upon visiting the underlying file. This -means that existing buffers are not renamed until they are -visited again in a new buffer (files are visited with the command -`find-file' or related)." - :global t - (if denote-rename-buffer-mode - (progn - (add-hook 'denote-after-new-note-hook #'denote-rename-buffer-rename-function-or-fallback) - (add-hook 'denote-after-rename-file-hook #'denote-rename-buffer-rename-function-or-fallback) - (add-hook 'find-file-hook #'denote-rename-buffer-rename-function-or-fallback)) - (remove-hook 'denote-after-new-note-hook #'denote-rename-buffer-rename-function-or-fallback) - (remove-hook 'denote-after-rename-file-hook #'denote-rename-buffer-rename-function-or-fallback) - (remove-hook 'find-file-hook #'denote-rename-buffer-rename-function-or-fallback))) - -(provide 'denote-rename-buffer) -;;; denote-rename-buffer.el ends here blob - 1e4cfd5155e4e1ada1ede5f2a2e09e5d76fd3d92 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/denote-silo-extras.el +++ /dev/null @@ -1,102 +0,0 @@ -;;; denote-silo-extras.el --- Convenience functions for using Denote in multiple silos -*- lexical-binding: t; -*- - -;; Copyright (C) 2023-2024 Free Software Foundation, Inc. - -;; Author: Protesilaos Stavrou -;; Maintainer: Protesilaos Stavrou -;; URL: https://github.com/protesilaos/denote - -;; This file is NOT part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This is a set of convenience functions that used to be provided in -;; the Denote manual. A "silo" is a `denote-directory' that is -;; self-contained. Users can maintain multiple silos. Consult the -;; manual for the details. With the Denote package installed, -;; evaluate the following to read the relevant node: -;; -;; (info "(denote) Maintain separate directory silos for notes") - -;;; Code: - -(require 'denote) - -(defgroup denote-silo-extras nil - "Make it easier to use Denote across Silos." - :group 'denote - :link '(info-link "(denote) Top") - :link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote")) - -(defcustom denote-silo-extras-directories - `(,denote-directory) - "List of file paths pointing to Denote silos. -Each file path points to a directory, which takes the same value -as the variable `denote-directory'." - :group 'denote-silo-extras - :link '(info-link "(denote) Maintain separate directories for notes") - :type '(repeat directory)) - -(defvar denote-silo-extras-directory-history nil - "Minibuffer history for `denote-silo-extras--directory-prompt'.") - -(defalias 'denote-silo-extras--directory-history 'denote-silo-extras-directory-history - "Compatibility alias for `denote-silo-extras-directory-history'.") - -(defun denote-silo-extras--directory-prompt () - "Prompt for directory among `denote-silo-extras-directories'." - (let ((default (car denote-silo-extras-directory-history))) - (completing-read - (format-prompt "Select a silo" default) - denote-silo-extras-directories nil :require-match - nil 'denote-silo-extras-directory-history))) - -;;;###autoload -(defun denote-silo-extras-create-note (silo) - "Select SILO and run `denote' in it. -SILO is a file path from `denote-silo-extras-directories'. - -When called from Lisp, SILO is a file system path to a directory." - (interactive (list (denote-silo-extras--directory-prompt))) - (let ((denote-directory silo)) - (call-interactively #'denote))) - -;;;###autoload -(defun denote-silo-extras-open-or-create (silo) - "Select SILO and run `denote-open-or-create' in it. -SILO is a file path from `denote-silo-extras-directories'. - -When called from Lisp, SILO is a file system path to a directory." - (interactive (list (denote-silo-extras--directory-prompt))) - (let ((denote-directory silo)) - (call-interactively #'denote-open-or-create))) - -;;;###autoload -(defun denote-silo-extras-select-silo-then-command (silo command) - "Select SILO and run Denote COMMAND in it. -SILO is a file path from `denote-silo-extras-directories', while -COMMAND is one among `denote-silo-extras-commands'. - -When called from Lisp, SILO is a file system path to a directory." - (interactive - (list - (denote-silo-extras--directory-prompt) - (denote-command-prompt))) - (let ((denote-directory silo)) - (call-interactively command))) - -(provide 'denote-silo-extras) -;;; denote-silo-extras.el ends here blob - de443dc9bcecbcc118891fb70e71484189a24131 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/denote-sort.el +++ /dev/null @@ -1,201 +0,0 @@ -;;; denote-sort.el --- Sort Denote files based on a file name component -*- lexical-binding: t -*- - -;; Copyright (C) 2023-2024 Free Software Foundation, Inc. - -;; Author: Protesilaos Stavrou -;; Maintainer: Protesilaos Stavrou -;; URL: https://github.com/protesilaos/denote - -;; This file is NOT part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. -;; -;; This program 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 this program. If not, see . - -;;; Commentary: -;; -;; Sort Denote files based on their file name components, namely, the -;; signature, title, or keywords. - -;;; Code: - -(require 'denote) - -(defgroup denote-sort nil - "Sort Denote files based on a file name component." - :group 'denote - :link '(info-link "(denote) Top") - :link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote")) - -(defvar denote-sort-comparison-function #'string-collate-lessp - "String comparison function used by `denote-sort-files' subroutines.") - -(defvar denote-sort-components '(title keywords signature identifier) - "List of sorting keys applicable for `denote-sort-files' and related.") - -;; NOTE 2023-12-04: We can have compound sorting algorithms such as -;; title+signature, but I want to keep this simple for the time being. -;; Let us first hear from users to understand if there is a real need -;; for such a feature. -(defmacro denote-sort--define-lessp (component) - "Define function to sort by COMPONENT." - (let ((retrieve-fn (intern (format "denote-retrieve-filename-%s" component)))) - `(defun ,(intern (format "denote-sort-%s-lessp" component)) (file1 file2) - ,(format - "Return smallest between FILE1 and FILE2 based on their %s. -The comparison is done with `denote-sort-comparison-function' between the -two title values." - component) - (let* ((one (,retrieve-fn file1)) - (two (,retrieve-fn file2)) - (one-empty-p (or (null one) (string-empty-p one))) - (two-empty-p (or (null two) (string-empty-p two)))) - (cond - (one-empty-p nil) - ((and (not one-empty-p) two-empty-p) one) - (t (funcall denote-sort-comparison-function one two))))))) - -;; TODO 2023-12-04: Subject to the above NOTE, we can also sort by -;; directory and by file length. -(denote-sort--define-lessp title) -(denote-sort--define-lessp keywords) -(denote-sort--define-lessp signature) - -;;;###autoload -(defun denote-sort-files (files component &optional reverse) - "Returned sorted list of Denote FILES. - -With COMPONENT as a symbol among `denote-sort-components', -sort files based on the corresponding file name component. - -With COMPONENT as a nil value keep the original date-based -sorting which relies on the identifier of each file name. - -With optional REVERSE as a non-nil value, reverse the sort order." - (let* ((files-to-sort (copy-sequence files)) - (sort-fn (when component - (pcase component - ('title #'denote-sort-title-lessp) - ('keywords #'denote-sort-keywords-lessp) - ('signature #'denote-sort-signature-lessp)))) - (sorted-files (if sort-fn (sort files sort-fn) files-to-sort))) - (if reverse - (reverse sorted-files) - sorted-files))) - -(defun denote-sort-get-directory-files (files-matching-regexp sort-by-component &optional reverse omit-current) - "Return sorted list of files in variable `denote-directory'. - -With FILES-MATCHING-REGEXP as a string limit files to those -matching the given regular expression. - -With SORT-BY-COMPONENT as a symbol among `denote-sort-components', -pass it to `denote-sort-files' to sort by the corresponding file -name component. - -With optional REVERSE as a non-nil value, reverse the sort order. - -With optional OMIT-CURRENT, do not include the current file in -the list." - (denote-sort-files - (denote-directory-files files-matching-regexp omit-current) - sort-by-component - reverse)) - -(defun denote-sort-get-links (files-matching-regexp sort-by-component current-file-type id-only &optional reverse) - "Return sorted typographic list of links for FILES-MATCHING-REGEXP. - -With FILES-MATCHING-REGEXP as a string, match files stored in the -variable `denote-directory'. - -With SORT-BY-COMPONENT as a symbol among `denote-sort-components', -sort FILES-MATCHING-REGEXP by the given Denote file name -component. If SORT-BY-COMPONENT is nil or an unknown non-nil -value, default to the identifier-based sorting. - -With CURRENT-FILE-TYPE as a symbol among those specified in -`denote-file-type' (or the `car' of each element in -`denote-file-types'), format the link accordingly. With a nil or -unknown non-nil value, default to the Org notation. - -With ID-ONLY as a non-nil value, produce links that consist only -of the identifier, thus deviating from CURRENT-FILE-TYPE. - -With optional REVERSE as a non-nil value, reverse the sort order." - (denote-link--prepare-links - (denote-sort-get-directory-files files-matching-regexp sort-by-component reverse) - current-file-type - id-only)) - -(defvar denote-sort-component-history nil - "Minibuffer history of `denote-sort-component-prompt'.") - -(defalias 'denote-sort--component-hist 'denote-sort-component-history - "Compatibility alias for `denote-sort-component-history'.") - -(defun denote-sort-component-prompt () - "Prompt `denote-sort-files' for sorting key among `denote-sort-components'." - (let ((default (car denote-sort-component-history))) - (intern - (completing-read - (format-prompt "Sort by file name component" default) - denote-sort-components nil :require-match - nil 'denote-sort-component-history default)))) - -(defvar-local denote-sort--dired-buffer nil - "Buffer object of current `denote-sort-dired'.") - -;;;###autoload -(defun denote-sort-dired (files-matching-regexp sort-by-component reverse) - "Produce Dired dired-buffer with sorted files from variable `denote-directory'. -When called interactively, prompt for FILES-MATCHING-REGEXP, -SORT-BY-COMPONENT, and REVERSE. - -1. FILES-MATCHING-REGEXP limits the list of Denote files to - those matching the provided regular expression. - -2. SORT-BY-COMPONENT sorts the files by their file name - component (one among `denote-sort-components'). - -3. REVERSE is a boolean to reverse the order when it is a non-nil value. - -When called from Lisp, the arguments are a string, a keyword, and -a non-nil value, respectively." - (interactive - (list - (denote-files-matching-regexp-prompt) - (denote-sort-component-prompt) - (y-or-n-p "Reverse sort? "))) - (if-let ((default-directory (denote-directory)) - (files (denote-sort-get-directory-files files-matching-regexp sort-by-component reverse)) - ;; NOTE 2023-12-04: Passing the FILES-MATCHING-REGEXP as - ;; buffer-name produces an error if the regexp contains a - ;; wildcard for a directory. I can reproduce this in emacs - ;; -Q and am not sure if it is a bug. Anyway, I will report - ;; it upstream, but even if it is fixed we cannot use it - ;; for now (whatever fix will be available for Emacs 30+). - (denote-sort-dired-buffer-name (format "Denote sort `%s' by `%s'" files-matching-regexp sort-by-component)) - (buffer-name (format "Denote sort by `%s' at %s" sort-by-component (format-time-string "%T")))) - (let ((dired-buffer (dired (cons buffer-name (mapcar #'file-relative-name files))))) - (setq denote-sort--dired-buffer dired-buffer) - (with-current-buffer dired-buffer - (setq-local revert-buffer-function - (lambda (&rest _) - (kill-buffer dired-buffer) - (denote-sort-dired files-matching-regexp sort-by-component reverse)))) - ;; Because of the above NOTE, I am printing a message. Not - ;; what I want, but it is better than nothing... - (message denote-sort-dired-buffer-name)) - (message "No matching files for: %s" files-matching-regexp))) - -(provide 'denote-sort) -;;; denote-sort.el ends here blob - 50c970a4e5128b26a114b24f282472a9be5bac2f (mode 644) blob + /dev/null --- elpa/denote-2.3.5/denote.el +++ /dev/null @@ -1,4689 +0,0 @@ -;;; denote.el --- Simple notes with an efficient file-naming scheme -*- lexical-binding: t -*- - -;; Copyright (C) 2022-2024 Free Software Foundation, Inc. - -;; Author: Protesilaos Stavrou -;; Maintainer: Protesilaos Stavrou -;; URL: https://github.com/protesilaos/denote -;; Version: 2.3.5 -;; Package-Requires: ((emacs "28.1")) - -;; This file is NOT part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. -;; -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Denote aims to be a simple-to-use, focused-in-scope, and effective -;; note-taking and file-naming tool for Emacs. -;; -;; Denote is based on the idea that files should follow a predictable -;; and descriptive file-naming scheme. The file name must offer a -;; clear indication of what the contents are about, without reference -;; to any other metadata. Denote basically streamlines the creation -;; of such files or file names while providing facilities to link -;; between them (where those files are editable). -;; -;; Denote's file-naming scheme is not limited to "notes". It can be used -;; for all types of file, including those that are not editable in Emacs, -;; such as videos. Naming files in a consistent way makes their -;; filtering and retrieval considerably easier. Denote provides relevant -;; facilities to rename files, regardless of file type. -;; -;; The manual describes all the technicalities about the file-naming -;; scheme, points of entry to creating new notes, commands to check -;; links between notes, and more: ;; . -;; If you have the info manual available, evaluate: -;; -;; (info "(denote) Top") -;; -;; What follows is a general overview of its core core design -;; principles (again: please read the manual for the technicalities): -;; -;; * Predictability :: File names must follow a consistent and -;; descriptive naming convention (see the manual's "The file-naming -;; scheme"). The file name alone should offer a clear indication of -;; what the contents are, without reference to any other metadatum. -;; This convention is not specific to note-taking, as it is pertinent -;; to any form of file that is part of the user's long-term storage -;; (see the manual's "Renaming files"). -;; -;; * Composability :: Be a good Emacs citizen, by integrating with other -;; packages or built-in functionality instead of re-inventing -;; functions such as for filtering or greping. The author of Denote -;; (Protesilaos, aka "Prot") writes ordinary notes in plain text -;; (`.txt'), switching on demand to an Org file only when its expanded -;; set of functionality is required for the task at hand (see the -;; manual's "Points of entry"). -;; -;; * Portability :: Notes are plain text and should remain portable. -;; The way Denote writes file names, the front matter it includes in -;; the note's header, and the links it establishes must all be -;; adequately usable with standard Unix tools. No need for a databse -;; or some specialised software. As Denote develops and this manual -;; is fully fleshed out, there will be concrete examples on how to do -;; the Denote-equivalent on the command-line. -;; -;; * Flexibility :: Do not assume the user's preference for a -;; note-taking methodology. Denote is conceptually similar to the -;; Zettelkasten Method, which you can learn more about in this -;; detailed introduction: . -;; Notes are atomic (one file per note) and have a unique identifier. -;; However, Denote does not enforce a particular methodology for -;; knowledge management, such as a restricted vocabulary or mutually -;; exclusive sets of keywords. Denote also does not check if the user -;; writes thematically atomic notes. It is up to the user to apply -;; the requisite rigor and/or creativity in pursuit of their preferred -;; workflow (see the manual's "Writing metanotes"). -;; -;; * Hackability :: Denote's code base consists of small and reusable -;; functions. They all have documentation strings. The idea is to -;; make it easier for users of varying levels of expertise to -;; understand what is going on and make surgical interventions where -;; necessary (e.g. to tweak some formatting). In this manual, we -;; provide concrete examples on such user-level configurations (see -;; the manual's "Keep a journal or diary"). -;; -;; Now the important part... "Denote" is the familiar word, though it -;; also is a play on the "note" concept. Plus, we can come up with -;; acronyms, recursive or otherwise, of increasingly dubious utility -;; like: -;; -;; + Don't Ever Note Only The Epiphenomenal -;; + Denote Everything Neatly; Omit The Excesses -;; -;; But we'll let you get back to work. Don't Eschew or Neglect your -;; Obligations, Tasks, and Engagements. - -;;; Code: - -(require 'seq) -(require 'xref) -(require 'dired) -(eval-when-compile (require 'subr-x)) - -(defgroup denote () - "Simple notes with an efficient file-naming scheme." - :group 'files - :link '(info-link "(denote) Top") - :link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote")) - -;;;; User options - -;; About the autoload: (info "(elisp) File Local Variables") - -(define-obsolete-variable-alias - 'denote-user-enforced-denote-directory - 'denote-directory - "2.3.0") - -;;;###autoload (put 'denote-directory 'safe-local-variable (lambda (val) (or (stringp val) (eq val 'local) (eq val 'default-directory)))) -(defcustom denote-directory (expand-file-name "~/Documents/notes/") - "Directory for storing personal notes. - -If you intend to reference this variable in Lisp, consider using -the function `denote-directory' instead." - :group 'denote - :safe (lambda (val) (or (stringp val) (eq val 'local) (eq val 'default-directory))) - :package-version '(denote . "2.0.0") - :link '(info-link "(denote) Maintain separate directories for notes") - :type 'directory) - -(defcustom denote-save-buffer-after-creation nil - "Control whether commands that creeate new notes save their buffer outright. - -The default behaviour of commands such as `denote' (or related) -is to not save the buffer they create. This gives the user the -chance to review the text before writing it to a file. The user -may choose to delete the unsaved buffer, thus not creating a new -note. - -If this user option is set to a non-nil value, such buffers are -saved automatically." - :group 'denote - :package-version '(denote . "2.3.0") - :type 'boolean) - -;;;###autoload (put 'denote-known-keywords 'safe-local-variable #'listp) -(defcustom denote-known-keywords - '("emacs" "philosophy" "politics" "economics") - "List of strings with predefined keywords for `denote'. -Also see user options: `denote-infer-keywords', -`denote-sort-keywords', `denote-file-name-slug-functions'." - :group 'denote - :safe #'listp - :package-version '(denote . "0.1.0") - :type '(repeat string)) - -;;;###autoload (put 'denote-infer-keywords 'safe-local-variable (lambda (val) (or val (null val)))) -(defcustom denote-infer-keywords t - "Whether to infer keywords from existing notes' file names. - -When non-nil, search the file names of existing notes in the -variable `denote-directory' for their keyword field and extract -the entries as \"inferred keywords\". These are combined with -`denote-known-keywords' and are presented as completion -candidates while using `denote' and related commands -interactively. - -If nil, refrain from inferring keywords. The aforementioned -completion prompt only shows the `denote-known-keywords'. Use -this if you want to enforce a restricted vocabulary. - -The user option `denote-excluded-keywords-regexp' can be used to -exclude keywords that match a regular expression. - -Inferred keywords are specific to the value of the variable -`denote-directory'. If a silo with a local value is used, as -explained in that variable's doc string, the inferred keywords -are specific to the given silo. - -For advanced Lisp usage, the function `denote-keywords' returns -the appropriate list of strings." - :group 'denote - :safe (lambda (val) (or val (null val))) - :package-version '(denote . "0.1.0") - :type 'boolean) - -(defcustom denote-prompts '(title keywords) - "Specify the prompts followed by relevant Denote commands. - -Commands that prompt for user input to construct a Denote file name -include, but are not limited to: `denote', `denote-signature', -`denote-type', `denote-date', `denote-subdirectory', -`denote-rename-file', `denote-dired-rename-files'. - -The value of this user option is a list of symbols, which includes any -of the following: - -- `title': Prompt for the title of the new note. - -- `keywords': Prompts with completion for the keywords of the new - note. Available candidates are those specified in the user - option `denote-known-keywords'. If the user option - `denote-infer-keywords' is non-nil, keywords in existing note - file names are included in the list of candidates. The - `keywords' prompt uses `completing-read-multiple', meaning that - it can accept multiple keywords separated by a comma (or - whatever the value of `crm-separator' is). - -- `file-type': Prompts with completion for the file type of the - new note. Available candidates are those specified in the user - option `denote-file-type'. Without this prompt, `denote' uses - the value of `denote-file-type'. - -- `subdirectory': Prompts with completion for a subdirectory in - which to create the note. Available candidates are the value - of the user option `denote-directory' and all of its - subdirectories. Any subdirectory must already exist: Denote - will not create it. - -- `date': Prompts for the date of the new note. It will expect - an input like 2022-06-16 or a date plus time: 2022-06-16 14:30. - Without the `date' prompt, the `denote' command uses the - `current-time'. (To leverage the more sophisticated Org - method, see the `denote-date-prompt-use-org-read-date'.) - -- `template': Prompts for a KEY among `denote-templates'. The - value of that KEY is used to populate the new note with - content, which is added after the front matter. - -- `signature': Prompts for an arbitrary string that can be used - to establish a sequential relationship between files (e.g. 1, - 1a, 1b, 1b1, 1b2, ...). Signatures have no strictly defined - function and are up to the user to apply as they see fit. One - use-case is to implement Niklas Luhmann's Zettelkasten system - for a sequence of notes (Folgezettel). Signatures are not - included in a file's front matter. They are reserved solely - for creating a sequence in a file listing, at least for the - time being. To insert a link that includes the signature, use - the command `denote-link-with-signature'. - -The prompts occur in the given order. - -If the value of this user option is nil, no prompts are used. -The resulting file name will consist of an identifier (i.e. the -date and time) and a supported file type extension (per -`denote-file-type'). - -Recall that Denote's standard file-naming scheme is defined as -follows (read the manual for the technicalities): - - DATE--TITLE__KEYWORDS.EXT - -Depending on the inclusion of the `title', `keywords', and -`signature' prompts, file names will be any of those -permutations: - - DATE.EXT - DATE--TITLE.EXT - DATE__KEYWORDS.EXT - DATE==SIGNATURE.EXT - DATE==SIGNATURE--TITLE.EXT - DATE==SIGNATURE--TITLE__KEYWORDS.EXT - DATE==SIGNATURE__KEYWORDS.EXT - -When in doubt, always include the `title' and `keywords' -prompts (the default style). - -Finally, this user option only affects the interactive use of the -`denote' or other relevant commands (advanced users can call it from -Lisp). In Lisp usage, the behaviour is always what the caller -specifies, based on the supplied arguments. - -Also see `denote-history-completion-in-prompts'." - :group 'denote - :package-version '(denote . "2.3.0") - :link '(info-link "(denote) The denote-prompts option") - :type '(radio (const :tag "Use no prompts" nil) - (set :tag "Available prompts" :greedy t - (const :tag "Title" title) - (const :tag "Keywords" keywords) - (const :tag "Date" date) - (const :tag "File type extension" file-type) - (const :tag "Subdirectory" subdirectory) - (const :tag "Template" template) - (const :tag "Signature" signature)))) - -(defcustom denote-sort-keywords t - "Whether to sort keywords in new files. - -When non-nil, the keywords of `denote' are sorted with -`string-collate-lessp' regardless of the order they were inserted at the -minibuffer prompt. - -If nil, show the keywords in their given order." - :group 'denote - :package-version '(denote . "0.1.0") - :type 'boolean) - -(make-obsolete - 'denote-allow-multi-word-keywords - 'denote-file-name-letter-casing - "2.1.0") - -(defcustom denote-file-type nil - "The file type extension for new notes. - -By default (a nil value), the file type is that of Org mode. -Though the `org' symbol can be specified for the same effect. - -When the value is the symbol `markdown-yaml', the file type is -that of Markdown mode and the front matter uses YAML notation. -Similarly, `markdown-toml' is Markdown but has TOML syntax in the -front matter. - -When the value is `text', the file type is that of Text mode. - -Any other non-nil value is the same as the default. - -NOTE: Expert users can change the supported file-types by editing -the value of `denote-file-types'. That variable, which is not a -user option, controls the behaviour of all file-type-aware -functions (creating notes, renaming them, inserting front matter, -formatting a link, etc.). Consult its documentation for the -technicalities." - :type '(choice - (const :tag "Unspecified (defaults to Org)" nil) - (const :tag "Org mode (default)" org) - (const :tag "Markdown (YAML front matter)" markdown-yaml) - (const :tag "Markdown (TOML front matter)" markdown-toml) - (const :tag "Plain text" text)) - :package-version '(denote . "0.6.0") - :group 'denote) - -(defcustom denote-date-format nil - "Date format in the front matter (file header) of new notes. - -When nil (the default value), use a file-type-specific -format (also check `denote-file-type'): - -- For Org, an inactive timestamp is used, such as [2022-06-30 Wed - 15:31]. - -- For Markdown, the RFC3339 standard is applied: - 2022-06-30T15:48:00+03:00. - -- For plain text, the format is that of ISO 8601: 2022-06-30. - -If the value is a string, ignore the above and use it instead. -The string must include format specifiers for the date. These -are described in the doc string of `format-time-string'." - :type '(choice - (const :tag "Use appropiate format for each file type" nil) - (string :tag "Custom format for `format-time-string'")) - :package-version '(denote . "0.2.0") - :group 'denote) - -(defcustom denote-date-prompt-use-org-read-date nil - "Whether to use `org-read-date' in date prompts. - -If non-nil, use `org-read-date'. If nil, input the date as a -string, as described in `denote'. - -This option is relevant when `denote-prompts' includes a `date' -and/or when the user invokes the command `denote-date'." - :group 'denote - :package-version '(denote . "0.6.0") - :type 'boolean) - -(defcustom denote-org-store-link-to-heading t - "Determine whether `org-store-link' links to the current Org heading. - -When non-nil store link to the current Org heading inside the -Denote file. If the heading does not have a CUSTOM_ID, create it -and include it in its PROPERTIES drawer. If a CUSTOM_ID exists, -take it as-is. - -Make the resulting link a combination of the `denote:' link type, -pointing to the identifier of the current file, plus the value of -the heading's CUSTOM_ID, such as: - -- [[denote:20240118T060608][Some test]] -- [[denote:20240118T060608::#h:eed0fb8e-4cc7-478f][Some test::Heading text]] - -Both lead to the same Denote file, but the latter jumps to the -heading with the given CUSTOM_ID. Notice that the link to the -heading also has a different description, which includes the -heading text. - -The value of the CUSTOM_ID is determined by the Org user option -`org-id-method'. The sample shown above uses the default UUID -infrastructure (though I deleted a few characters to not get -complaints from the byte compiler about long lines in the doc -string...). - -If this user option is set to nil, only store links to the Denote -file (using its identifier), but not to the given heading. This -is what Denote was doing in versions prior to 2.3.0. - -What `org-store-link' does is merely collect a link. To actually insert -it, use the command `org-insert-link'. Note that `org-capture' uses -`org-store-link' internally when it needs to store a link. - -[ This feature only works in Org mode files, as other file types - do not have a linking mechanism that handles unique identifiers - for headings or other patterns to jump to. If `org-store-link' - is invoked in one such file, it captures only the Denote - identifier of the file, even if this user option is set to a - non-nil value. ]" - :group 'denote - :package-version '(denote . "2.3.0") - :type 'boolean) - -(defcustom denote-templates nil - "Alist of content templates for new notes. -A template is arbitrary text that Denote will add to a newly -created note right below the front matter. - -Templates are expressed as a (KEY . STRING) association. - -- The KEY is the name which identifies the template. It is an - arbitrary symbol, such as `report', `memo', `statement'. - -- The STRING is ordinary text that Denote will insert as-is. It - can contain newline characters to add spacing. The manual of - Denote contains examples on how to use the `concat' function, - beside writing a generic string. - -The user can choose a template either by invoking the command -`denote-template' or by changing the user option `denote-prompts' -to always prompt for a template when calling the `denote' -command." - :type '(alist :key-type symbol :value-type string) - :package-version '(denote . "0.5.0") - :link '(info-link "(denote) The denote-templates option") - :group 'denote) - -(defcustom denote-backlinks-show-context nil - "When non-nil, show link context in the backlinks buffer. - -The context is the line a link to the current note is found in. -The context includes multiple links to the same note, if those -are present. - -When nil, only show a simple list of file names that link to the -current note." - :group 'denote - :package-version '(denote . "1.2.0") - :type 'boolean) - -(defcustom denote-rename-no-confirm nil - "Make renaming commands not prompt for confirmation and save buffers outright. - -This affects the behaviour of the commands `denote-rename-file', -`denote-dired-rename-files', `denote-rename-file-using-front-matter', -`denote-dired-rename-marked-files-with-keywords', -`denote-dired-rename-marked-files-using-front-matter', -`denote-keywords-add', `denote-keywords-remove', and any other -command that builds on top of them. - -The default behaviour of the `denote-rename-file' command (and -others like it) is to ask for an affirmative answer as a final -step before changing the file name and, where relevant, inserting -or updating the corresponding front matter. It also does not -save the affected file's buffer to let the user inspect and -confirm the changes (such as by invoking the command -`diff-buffer-with-file'). - -With this user option bound to a non-nil value, buffers are saved -as well. The assumption is that the user who opts in to this -feature is familiar with the `denote-rename-file' operation (or -related) and knows it is reliable. - -Specialised commands that build on top of `denote-rename-file' (or related) -may internally bind this user option to a non-nil value in order -to perform their operation (e.g. `denote-dired-rename-files' goes -through each marked Dired file, prompting for the information to -use, but carries out the renaming without asking for confirmation)." - :group 'denote - :package-version '(denote . "2.3.0") - :type 'boolean) - -(defcustom denote-excluded-directories-regexp nil - "Regular expression of directories to exclude from all operations. -Omit matching directories from file prompts and also exclude them -from all functions that check the contents of the variable -`denote-directory'. The regexp needs to match only the name of -the directory, not its full path. - -File prompts are used by several commands, such as `denote-link' -and `denote-subdirectory'. - -Functions that check for files include `denote-directory-files' -and `denote-directory-subdirectories'. - -The match is performed with `string-match-p'." - :group 'denote - :package-version '(denote . "1.2.0") - :type 'string) - -(defcustom denote-excluded-keywords-regexp nil - "Regular expression of keywords to not infer. -Keywords are inferred from file names and provided at relevant -prompts as completion candidates when the user option -`denote-infer-keywords' is non-nil. - -The match is performed with `string-match-p'." - :group 'denote - :package-version '(denote . "1.2.0") - :type 'string) - -(defcustom denote-after-new-note-hook nil - "Normal hook that runs after the `denote' command. -This also covers all convenience functions that call `denote' -internally, such as `denote-signature' and `denote-type' (check -the default value of the user option `denote-commands-for-new-notes')." - :group 'denote - :package-version '(denote . "2.1.0") - :link '(info-link "(denote) Standard note creation") - :type 'hook) - -(defcustom denote-after-rename-file-hook nil - "Normal hook called after a succesful Denote rename operation. -This affects the behaviour of the commands `denote-rename-file', -`denote-dired-rename-files', `denote-rename-file-using-front-matter', -`denote-dired-rename-marked-files-with-keywords', -`denote-dired-rename-marked-files-using-front-matter', -`denote-keywords-add', `denote-keywords-remove', and any other -command that builds on top of them." - :group 'denote - :package-version '(denote . "2.3.0") - :link '(info-link "(denote) Renaming files") - :type 'hook) - -(defcustom denote-region-after-new-note-functions nil - "Abnormal hook called after `denote-region'. -Functions in this hook are called with two arguments, -representing the beginning and end buffer positions of the region -that was inserted in the new note. These are called only if -`denote-region' is invoked while a region is active. - -A common use-case is to call `org-insert-structure-template' -after a region is inserted. This case does not actually require -the aforementioned arguments, in which case the function can -simply declare them as ignored by prefixing the argument names -with an underscore. For example, the following will prompt for a -structure template as soon as `denote-region' is done: - - (defun my-denote-region-org-structure-template (_beg _end) - (when (derived-mode-p \\='org-mode) - (activate-mark) - (call-interactively \\='org-insert-structure-template))) - - (add-hook \\='denote-region-after-new-note-functions - #\\='my-denote-region-org-structure-template)" - :group 'denote - :package-version '(denote . "2.1.0") - :link '(info-link "(denote) Create a note with the region's contents") - :type 'hook) - -(defvar denote-prompts-with-history-as-completion - '(denote-title-prompt denote-signature-prompt denote-files-matching-regexp-prompt) - "Prompts that conditionally perform completion against their history. - -These are minibuffer prompts that ordinarily accept a free form string -input, as opposed to matching against a predefined set. - -These prompts can optionally perform completion against their own -minibuffer history when the user option `denote-history-completion-in-prompts' -is set to a non-nil value.") - -(defcustom denote-history-completion-in-prompts t - "Toggle history completion in all `denote-prompts-with-history-as-completion'. - -When this user option is set to a non-nil value, use minibuffer history -entries as completion candidates in `denote-prompts-with-history-as-completion'. -Those will show previous inputs from their respective history as -possible values to select, either to (i) re-insert them verbatim or (ii) -with the intent to edit further (depending on the minibuffer user -interface, one can select a candidate with TAB without exiting the -minibuffer, as opposed to what RET normally does by selecting and -exiting). - -When this user option is set to a nil value, all of the -`denote-prompts-with-history-as-completion' do not use minibuffer -completion: they just prompt for a string of characters. Their -history is still available through all the standard ways of retrieving -minibuffer history, such as with the command `previous-history-element'. - -History completion still allows arbitrary values to be provided as -input: they do not have to match the available minibuffer completion -candidates. - -Note that some prompts, like `denote-keywords-prompt', always use -minibuffer completion, due to the specifics of their data. - -[ Consider enabling the built-in `savehist-mode' to persist minibuffer - histories between sessions.] - -Also see `denote-prompts'." - :type 'boolean - :package-version '(denote . "2.3.0") - :group 'denote) - -(defcustom denote-commands-for-new-notes - '(denote - denote-date - denote-subdirectory - denote-template - denote-type - denote-signature) - "List of commands for `denote-command-prompt' that create a new note. -These are used by commands such as `denote-open-or-create-with-command' -and `denote-link-after-creating-with-command'." - :group 'denote - :package-version '(denote . "2.1.0") - :link '(info-link "(denote) Choose which commands to prompt for") - :type '(repeat symbol)) - -(defcustom denote-file-name-slug-functions - '((title . denote-sluggify-title) - (signature . denote-sluggify-signature) - (keyword . denote-sluggify-keyword)) - "Specify the method Denote uses to format the components of the file name. - -The value is an alist where each element is a cons cell of the -form (COMPONENT . METHOD). - -- The COMPONENT is an unquoted symbol among `title', `signature', - `keyword' (notice the absence of `s', see below), which - refers to the corresponding component of the file name. - -- The METHOD is the function to be used to format the given - component. This function should take a string as its parameter - and return the string formatted for the file name. In the case - of the `keyword' component, the function receives a SINGLE - string representing a single keyword and return it formatted - for the file name. Joining the keywords together is handled by - Denote. - -Note that the `keyword' function is also applied to the keywords -of the front matter. - -By default, if a function is not specified for a component, we -use `denote-sluggify-title', `denote-sluggify-keyword' and -`denote-sluggify-signature'. - -Remember that deviating from the default file-naming scheme of Denote -will make things harder to search in the future, as files can/will have -permutations that create uncertainty. The sluggification scheme and -concomitant restrictions we impose by default are there for a very good -reason: they are the distillation of years of experience. Here we give -you what you wish, but bear in mind it may not be what you need. You -have been warned." - :group 'denote - :package-version '(denote . "2.3.0") - :link '(info-link "(denote) User-defined sluggification of file name components") - :type '(alist :key (choice (const title) - (const signature) - (const keyword)) - :value function)) - -(make-obsolete - 'denote-file-name-letter-casing - 'denote-file-name-slug-functions - "2.3.0") - -(defcustom denote-link-description-function #'denote-link-description-with-signature-and-title - "Function to create the description of links. - -The function specified takes a FILE argument and returns the description -as a string. - -By default, the title of the file is returned as the description. If -the file has a signature, it is prepended to the title." - :group 'denote - :type '(choice - (function :tag "Link to title and include signature, if present" denote-link-description-with-signature-and-title) - (function :tag "Custom function like `denote-link-description-with-signature-and-title'")) - :package-version '(denote . "2.3.0")) - -;;;; Main variables - -;; For character classes, evaluate: (info "(elisp) Char Classes") - -(defconst denote-id-format "%Y%m%dT%H%M%S" - "Format of ID prefix of a note's filename. -The note's ID is derived from the date and time of its creation.") - -(defconst denote-id-regexp "\\([0-9]\\{8\\}\\)\\(T[0-9]\\{6\\}\\)" - "Regular expression to match `denote-id-format'.") - -(defconst denote-signature-regexp "==\\([^.]*?\\)\\(--.*\\|__.*\\|\\..*\\)*$" - "Regular expression to match the SIGNATURE field in a file name.") - -(defconst denote-title-regexp "--\\([^.]*?\\)\\(--.*\\|__.*\\|\\..*\\)*$" - "Regular expression to match the TITLE field in a file name.") - -(defconst denote-keywords-regexp "__\\([^.]*?\\)\\(--.*\\|__.*\\|\\..*\\)*$" - "Regular expression to match the KEYWORDS field in a file name.") - -(defconst denote-excluded-punctuation-regexp "[][{}!@#$%^&*()=+'\"?,.\|;:~`‘’“”/]*" - "Punctionation that is removed from file names. -We consider those characters illegal for our purposes.") - -(defvar denote-excluded-punctuation-extra-regexp nil - "Additional punctuation that is removed from file names. -This variable is for advanced users who need to extend the -`denote-excluded-punctuation-regexp'. Once we have a better -understanding of what we should be omitting, we will update -things accordingly.") - -;;;; File helper functions - -(defun denote--completion-table (category candidates) - "Pass appropriate metadata CATEGORY to completion CANDIDATES." - (lambda (string pred action) - (if (eq action 'metadata) - `(metadata (category . ,category)) - (complete-with-action action candidates string pred)))) - -(defun denote--completion-table-no-sort (category candidates) - "Pass appropriate metadata CATEGORY to completion CANDIDATES. -Like `denote--completion-table' but also disable sorting." - (lambda (string pred action) - (if (eq action 'metadata) - `(metadata (category . ,category) - (display-sort-function . ,#'identity)) - (complete-with-action action candidates string pred)))) - -(defun denote--default-directory-is-silo-p () - "Return path to silo if `default-directory' is a silo." - (when-let ((dir-locals (dir-locals-find-file default-directory)) - ((alist-get 'denote-directory dir-local-variables-alist))) - (cond - ((listp dir-locals) - (car dir-locals)) - ((stringp dir-locals) - dir-locals)))) - -(defun denote--make-denote-directory () - "Make the variable `denote-directory' and its parents, if needed." - (when (not (file-directory-p denote-directory)) - (make-directory denote-directory :parents))) - -(defun denote-directory () - "Return path of variable `denote-directory' as a proper directory. -Custom Lisp code can `let' bind the variable `denote-directory' -to override what this function returns." - ;; NOTE 2024-02-09: We may want to remove this condition eventually. - ;; The reason is that we want to stop supporting the dir-local - ;; values of `default-directory' or `local' in favour of just - ;; specifying a string. I don't think we can delete this altogether - ;; though, as it will break existing configurations. - (if-let (((or (eq denote-directory 'default-directory) (eq denote-directory 'local))) - (silo-dir (denote--default-directory-is-silo-p))) - silo-dir - (let ((denote-directory (file-name-as-directory (expand-file-name denote-directory)))) - (denote--make-denote-directory) - denote-directory))) - -(defun denote--slug-no-punct (str &optional extra-characters) - "Remove punctuation from STR. -Concretely, replace with an empty string anything that matches -the `denote-excluded-punctuation-regexp' and -`denote-excluded-punctuation-extra-regexp'. - -EXTRA-CHARACTERS is an optional string that has the same meaning -as the aforementioned variables." - (dolist (regexp (list denote-excluded-punctuation-regexp - denote-excluded-punctuation-extra-regexp - extra-characters)) - (when (stringp regexp) - (setq str (replace-regexp-in-string regexp "" str)))) - str) - -(defun denote--slug-no-punct-for-signature (str &optional extra-characters) - "Remove punctuation (except = signs) from STR. - -This works the same way as `denote--slug-no-punct', except that = -signs are not removed from STR. - -EXTRA-CHARACTERS is an optional string. See -`denote--slug-no-punct' for its documentation." - (dolist (regexp (list denote-excluded-punctuation-regexp - denote-excluded-punctuation-extra-regexp - extra-characters)) - (when (stringp regexp) - (setq str (replace-regexp-in-string (string-replace "=" "" regexp) "" str)))) - str) - -(defun denote--slug-hyphenate (str) - "Replace spaces and underscores with hyphens in STR. -Also replace multiple hyphens with a single one and remove any -leading and trailing hyphen." - (replace-regexp-in-string - "^-\\|-$" "" - (replace-regexp-in-string - "-\\{2,\\}" "-" - (replace-regexp-in-string "_\\|\s+" "-" str)))) - -(defun denote--remove-dot-characters (str) - "Remove the dot character from STR." - (replace-regexp-in-string "\\." "" str)) - -(defun denote--trim-right-token-characters (str) - "Remove =, - and _ from the end of STR." - (string-trim-right str "[=_-]+")) - -(defun denote--replace-consecutive-token-characters (str) - "Replace consecutive characters with a single one in STR. -Spaces, underscores and equal signs are replaced with a single -one in str." - (replace-regexp-in-string - "-\\{2,\\}" "-" - (replace-regexp-in-string - "_\\{2,\\}" "_" - (replace-regexp-in-string - "=\\{2,\\}" "=" str)))) - -(defun denote-sluggify (component str) - "Make STR an appropriate slug for file name COMPONENT. - -Apply the function specified in `denote-file-name-slug-function' -to COMPONENT which is one of `title', `signature', `keyword'. If -the resulting string still contains consecutive -,_ or =, they -are replaced by a single occurence of the character. If -COMPONENT is `keyword', remove underscores from STR as they are -used as the keywords separator in file names." - (let* ((slug-function (alist-get component denote-file-name-slug-functions)) - (str-slug (cond ((eq component 'title) - (funcall (or slug-function #'denote-sluggify-title) str)) - ((eq component 'keyword) - (replace-regexp-in-string - "_" "" - (funcall (or slug-function #'denote-sluggify-keyword) str))) - ((eq component 'signature) - (funcall (or slug-function #'denote-sluggify-signature) str))))) - (denote--trim-right-token-characters - (denote--replace-consecutive-token-characters - (denote--remove-dot-characters str-slug))))) - -(make-obsolete - 'denote-letter-case - 'denote-sluggify - "2.3.0") - -(defun denote--slug-put-equals (str) - "Replace spaces and underscores with equals signs in STR. -Also replace multiple equals signs with a single one and remove -any leading and trailing signs." - (replace-regexp-in-string - "^=\\|=$" "" - (replace-regexp-in-string - "=\\{2,\\}" "=" - (replace-regexp-in-string "_\\|\s+" "=" str)))) - -(defun denote-sluggify-title (str) - "Make STR an appropriate slug for title." - (downcase (denote--slug-hyphenate (denote--slug-no-punct str)))) - -(defun denote-sluggify-signature (str) - "Make STR an appropriate slug for signature." - (downcase (denote--slug-put-equals (denote--slug-no-punct-for-signature str "-+")))) - -(defun denote-sluggify-keyword (str) - "Sluggify STR while joining separate words." - (downcase - (replace-regexp-in-string - "-" "" - (denote--slug-hyphenate (denote--slug-no-punct str))))) - -(make-obsolete - 'denote-sluggify-and-join - 'denote-sluggify-keyword - "2.3.0") - -(defun denote-sluggify-keywords (keywords) - "Sluggify KEYWORDS, which is a list of strings." - (mapcar (lambda (keyword) - (denote-sluggify 'keyword keyword)) - keywords)) - -(defun denote--file-empty-p (file) - "Return non-nil if FILE is empty." - (zerop (or (file-attribute-size (file-attributes file)) 0))) - -(defun denote-file-has-identifier-p (file) - "Return non-nil if FILE has a Denote identifier." - (denote-retrieve-filename-identifier file)) - -(defun denote-file-has-supported-extension-p (file) - "Return non-nil if FILE has supported extension. -Also account for the possibility of an added .gpg suffix. -Supported extensions are those implied by `denote-file-type'." - (seq-some (lambda (e) - (string-suffix-p e file)) - (denote-file-type-extensions-with-encryption))) - -(defun denote-filename-is-note-p (filename) - "Return non-nil if FILENAME is a valid name for a Denote note. -For our purposes, its path must be part of the variable -`denote-directory', it must have a Denote identifier in its name, -and use one of the extensions implied by `denote-file-type'." - (and (string-prefix-p (denote-directory) (expand-file-name filename)) - (denote-file-has-identifier-p filename) - (denote-file-has-supported-extension-p filename))) - -(defun denote-file-is-note-p (file) - "Return non-nil if FILE is an actual Denote note. -For our purposes, a note must not be a directory, must satisfy -`file-regular-p' and `denote-filename-is-note-p'." - (and (not (file-directory-p file)) - (file-regular-p file) - (denote-filename-is-note-p file))) - -(defun denote-file-has-signature-p (file) - "Return non-nil if FILE has a Denote identifier." - (denote-retrieve-filename-signature file)) - -(make-obsolete 'denote-file-directory-p nil "2.0.0") - -(defun denote--file-regular-writable-p (file) - "Return non-nil if FILE is regular and writable." - (and (file-regular-p file) - (file-writable-p file))) - -(defun denote-file-is-writable-and-supported-p (file) - "Return non-nil if FILE is writable and has supported extension." - (and (denote--file-regular-writable-p file) - (denote-file-has-supported-extension-p file))) - -(defun denote-get-file-name-relative-to-denote-directory (file) - "Return name of FILE relative to the variable `denote-directory'. -FILE must be an absolute path." - (when-let ((dir (denote-directory)) - ((file-name-absolute-p file)) - (file-name (expand-file-name file)) - ((string-prefix-p dir file-name))) - (substring-no-properties file-name (length dir)))) - -(defun denote-extract-id-from-string (string) - "Return existing Denote identifier in STRING, else nil." - (when (string-match denote-id-regexp string) - (match-string-no-properties 0 string))) - -(define-obsolete-function-alias - 'denote--default-dir-has-denote-prefix - 'denote--dir-in-denote-directory-p - "2.1.0") - -(defun denote--exclude-directory-regexp-p (file) - "Return non-nil if FILE matches `denote-excluded-directories-regexp'." - (and denote-excluded-directories-regexp - (string-match-p denote-excluded-directories-regexp file))) - -(defun denote--directory-files-recursively-predicate (file) - "Predicate used by `directory-files-recursively' on FILE. - -Return t if FILE is valid, else return nil." - (let ((rel (denote-get-file-name-relative-to-denote-directory file))) - (cond - ((string-match-p "\\`\\." rel) nil) - ((string-match-p "/\\." rel) nil) - ((denote--exclude-directory-regexp-p rel) nil) - ((file-readable-p file))))) - -(defun denote--directory-all-files-recursively () - "Return list of all files in variable `denote-directory'. -Avoids traversing dotfiles (unconditionally) and whatever matches -`denote-excluded-directories-regexp'." - (directory-files-recursively - (denote-directory) - directory-files-no-dot-files-regexp - :include-directories - #'denote--directory-files-recursively-predicate - :follow-symlinks)) - -(defun denote--directory-get-files () - "Return list with full path of valid files in variable `denote-directory'. -Consider files that satisfy `denote-file-has-identifier-p' and -are not backups." - (mapcar - #'expand-file-name - (seq-filter - (lambda (file) - (and (denote-file-has-identifier-p file) - (not (backup-file-name-p file)))) - (denote--directory-all-files-recursively)))) - -(defun denote-directory-files (&optional files-matching-regexp omit-current text-only) - "Return list of absolute file paths in variable `denote-directory'. - -Files only need to have an identifier. The return value may thus -include file types that are not implied by `denote-file-type'. - -Remember that the variable `denote-directory' accepts a dir-local -value, as explained in its doc string. - -With optional FILES-MATCHING-REGEXP, restrict files to those -matching the given regular expression. - -With optional OMIT-CURRENT as a non-nil value, do not include the -current Denote file in the returned list. - -With optional TEXT-ONLY as a non-nil value, limit the results to -text files that satisfy `denote-file-is-note-p'." - (let ((files (denote--directory-get-files))) - (when (and omit-current buffer-file-name (denote-file-has-identifier-p buffer-file-name)) - (setq files (delete buffer-file-name files))) - (when files-matching-regexp - (setq files (seq-filter - (lambda (f) - (string-match-p files-matching-regexp (denote-get-file-name-relative-to-denote-directory f))) - files))) - (when text-only - (setq files (seq-filter #'denote-file-is-note-p files))) - files)) - -;; NOTE 2023-11-30: We are declaring `denote-directory-text-only-files' -;; obsolete, though we keep it around for the foreseeable future. It -;; WILL BE REMOVED ahead of version 3.0.0 of Denote, whenever that -;; happens. -(make-obsolete 'denote-directory-text-only-files 'denote-directory-files "2.2.0") - -(defun denote-directory-text-only-files () - "Return list of text files in variable `denote-directory'. -Filter `denote-directory-files' using `denote-file-is-note-p'." - (denote-directory-files nil nil :text-only)) - -(defun denote-directory-subdirectories () - "Return list of subdirectories in variable `denote-directory'. -Omit dotfiles (such as .git) unconditionally. Also exclude -whatever matches `denote-excluded-directories-regexp'." - (seq-remove - (lambda (filename) - (let ((rel (denote-get-file-name-relative-to-denote-directory filename))) - (or (not (file-directory-p filename)) - (string-match-p "\\`\\." rel) - (string-match-p "/\\." rel) - (denote--exclude-directory-regexp-p rel)))) - (denote--directory-all-files-recursively))) - -(define-obsolete-variable-alias - 'denote--encryption-file-extensions - 'denote-encryption-file-extensions - "2.0.0") - -;; TODO 2023-01-24: Perhaps there is a good reason to make this a user -;; option, but I am keeping it as a generic variable for now. -(defvar denote-encryption-file-extensions '(".gpg" ".age") - "List of strings specifying file extensions for encryption.") - -(define-obsolete-function-alias - 'denote--extensions-with-encryption - 'denote-file-type-extensions-with-encryption - "2.0.0") - -(defun denote-file-type-extensions-with-encryption () - "Derive `denote-file-type-extensions' plus `denote-encryption-file-extensions'." - (let ((file-extensions (denote-file-type-extensions)) - all) - (dolist (ext file-extensions) - (dolist (enc denote-encryption-file-extensions) - (push (concat ext enc) all))) - (append file-extensions all))) - -(defun denote-get-file-extension (file) - "Return extension of FILE with dot included. -Account for `denote-encryption-file-extensions'. In other words, -return something like .org.gpg if it is part of the file, else -return .org." - (let ((outer-extension (file-name-extension file :period))) - (if-let (((member outer-extension denote-encryption-file-extensions)) - (file (file-name-sans-extension file)) - (inner-extension (file-name-extension file :period))) - (concat inner-extension outer-extension) - outer-extension))) - -(defun denote-get-file-extension-sans-encryption (file) - "Return extension of FILE with dot included and without the encryption part. -Build on top of `denote-get-file-extension' though always return -something like .org even if the actual file extension is -.org.gpg." - (let ((extension (denote-get-file-extension file))) - (if (string-match (regexp-opt denote-encryption-file-extensions) extension) - (substring extension 0 (match-beginning 0)) - extension))) - -(defun denote-get-path-by-id (id) - "Return absolute path of ID string in `denote-directory-files'." - (let ((files - (seq-filter - (lambda (file) - (string-prefix-p id (file-name-nondirectory file))) - (denote-directory-files)))) - (if (length< files 2) - (car files) - (seq-find - (lambda (file) - (let ((file-extension (denote-get-file-extension file))) - (and (denote-file-is-note-p file) - (or (string= (denote--file-extension denote-file-type) - file-extension) - (string= ".org" file-extension) - (member file-extension (denote-file-type-extensions)))))) - files)))) - -(defun denote-get-relative-path-by-id (id &optional directory) - "Return relative path of ID string in `denote-directory-files'. -The path is relative to DIRECTORY (default: ‘default-directory’)." - (file-relative-name (denote-get-path-by-id id) directory)) - -;; NOTE 2023-11-30: We are declaring `denote-directory-files-matching-regexp' -;; obsolete, though we keep it around for the foreseeable future. It -;; WILL BE REMOVED ahead of version 3.0.0 of Denote, whenever that -;; happens. -(make-obsolete 'denote-directory-files-matching-regexp 'denote-directory-files "2.2.0") - -(defun denote-directory-files-matching-regexp (regexp) - "Return list of files matching REGEXP in `denote-directory-files'." - (denote-directory-files regexp)) - -;; NOTE 2023-11-30: We are declaring `denote-all-files' obsolete, -;; though we keep it around for the foreseeable future. It WILL BE -;; REMOVED ahead of version 3.0.0 of Denote, whenever that happens. -(make-obsolete 'denote-all-files 'denote-directory-files "2.2.0") - -(defun denote-all-files (&optional omit-current) - "Return the list of Denote files in variable `denote-directory'. -With optional OMIT-CURRENT, do not include the current Denote -file in the returned list." - (denote-directory-files nil omit-current nil)) - -(defvar denote-file-history nil - "Minibuffer history of `denote-file-prompt'.") - -(defalias 'denote--file-history 'denote-file-history - "Compatibility alias for `denote-file-history'.") - -;; NOTE 2024-02-29: Based on `project--read-file-cpd-relative' from -;; the built-in project.el -(defun denote-file-prompt (&optional files-matching-regexp prompt-text) - "Prompt for file with identifier in variable `denote-directory'. -With optional FILES-MATCHING-REGEXP, filter the candidates per -the given regular expression. - -With optional PROMPT-TEXT, use it instead of the default call to -\"Select NOTE\"." - (when-let ((all-files (denote-directory-files files-matching-regexp :omit-current))) - (let* ((common-parent-directory - (let ((common-prefix (try-completion "" all-files))) - (if (> (length common-prefix) 0) - (file-name-directory common-prefix)))) - (cpd-length (length common-parent-directory)) - (prompt-prefix (or prompt-text "Select FILE")) - (prompt (if (zerop cpd-length) - (format "%s: " prompt-prefix) - (format "%s in %s: " prompt-prefix common-parent-directory))) - (included-cpd (when (member common-parent-directory all-files) - (setq all-files - (delete common-parent-directory all-files)) - t)) - (substrings (mapcar (lambda (s) (substring s cpd-length)) all-files)) - (_ (when included-cpd - (setq substrings (cons "./" substrings)))) - (new-collection (denote--completion-table 'file substrings)) - (relname (completing-read prompt new-collection nil nil nil 'denote-file-history)) - (absname (expand-file-name relname common-parent-directory))) - ;; NOTE 2024-02-29: This delete and add feels awkward. I wish - ;; we could tell `completing-read' to just leave this up to us. - (setq denote-file-history (delete relname denote-file-history)) - (add-to-history 'denote-file-history absname) - absname))) - -;;;; Keywords - -(defun denote-extract-keywords-from-path (path) - "Extract keywords from PATH and return them as a list of strings. -PATH must be a Denote-style file name where keywords are prefixed -with an underscore. - -If PATH has no such keywords, return nil." - (when-let ((kws (denote-retrieve-filename-keywords path))) - (split-string kws "_"))) - -(defun denote--inferred-keywords () - "Extract keywords from `denote-directory-files'. -This function returns duplicates. The `denote-keywords' is the -one that doesn't." - (let ((kw (mapcan #'denote-extract-keywords-from-path (denote-directory-files)))) - (if-let ((regexp denote-excluded-keywords-regexp)) - (seq-remove (apply-partially #'string-match-p regexp) kw) - kw))) - -(defun denote-keywords () - "Return appropriate list of keyword candidates. -If `denote-infer-keywords' is non-nil, infer keywords from -existing notes and combine them into a list with -`denote-known-keywords'. Else use only the latter. - -Inferred keywords are filtered by the user option -`denote-excluded-keywords-regexp'." - (delete-dups - (if denote-infer-keywords - (append (denote--inferred-keywords) denote-known-keywords) - denote-known-keywords))) - -(defun denote-convert-file-name-keywords-to-crm (string) - "Make STRING with keywords readable by `completing-read-multiple'. -STRING consists of underscore-separated words, as those appear in -the keywords component of a Denote file name. STRING is the same -as the return value of `denote-retrieve-filename-keywords'." - (string-join (split-string string "_" :omit-nulls "_") ",")) - -(defvar denote-keyword-history nil - "Minibuffer history of inputted keywords.") - -(defalias 'denote--keyword-history 'denote-keyword-history - "Compatibility alias for `denote-keyword-history'.") - -(defun denote--keywords-crm (keywords &optional prompt initial) - "Use `completing-read-multiple' for KEYWORDS. -With optional PROMPT, use it instead of a generic text for file -keywords. With optional INITIAL, add it to the minibuffer as -initial input." - (delete-dups - (completing-read-multiple - (format-prompt (or prompt "New file KEYWORDS") nil) - keywords nil nil initial 'denote-keyword-history))) - -(defun denote-keywords-prompt (&optional prompt-text initial-keywords) - "Prompt for one or more keywords. -Read entries as separate when they are demarcated by the -`crm-separator', which typically is a comma. - -With optional PROMPT-TEXT, use it to prompt the user for -keywords. Else use a generic prompt. With optional -INITIAL-KEYWORDS use them as the initial minibuffer text. - -Return an empty list if the minibuffer input is empty." - (denote--keywords-crm (denote-keywords) prompt-text initial-keywords)) - -(defun denote-keywords-sort (keywords) - "Sort KEYWORDS if `denote-sort-keywords' is non-nil. -KEYWORDS is a list of strings, per `denote-keywords-prompt'." - (if denote-sort-keywords - (sort (copy-sequence keywords) #'string-collate-lessp) - keywords)) - -(define-obsolete-function-alias - 'denote--keywords-combine - 'denote-keywords-combine - "2.1.0") - -(defun denote-keywords-combine (keywords) - "Combine KEYWORDS list of strings into a single string. -Keywords are separated by the underscore character, per the -Denote file-naming scheme." - (string-join keywords "_")) - -(defun denote--keywords-add-to-history (keywords) - "Append KEYWORDS to `denote-keyword-history'." - (mapc - (lambda (kw) - (add-to-history 'denote-keyword-history kw)) - (delete-dups keywords))) - -;;;; File types - -(defvar denote-org-front-matter - "#+title: %s -#+date: %s -#+filetags: %s -#+identifier: %s -\n" - "Org front matter. -It is passed to `format' with arguments TITLE, DATE, KEYWORDS, -ID. Advanced users are advised to consult Info node `(denote) -Change the front matter format'.") - -(defvar denote-yaml-front-matter - "--- -title: %s -date: %s -tags: %s -identifier: %S ----\n\n" - "YAML (Markdown) front matter. -It is passed to `format' with arguments TITLE, DATE, KEYWORDS, -ID. Advanced users are advised to consult Info node `(denote) -Change the front matter format'.") - -(defvar denote-toml-front-matter - "+++ -title = %s -date = %s -tags = %s -identifier = %S -+++\n\n" - "TOML (Markdown) front matter. -It is passed to `format' with arguments TITLE, DATE, KEYWORDS, -ID. Advanced users are advised to consult Info node `(denote) -Change the front matter format'.") - -(defvar denote-text-front-matter - "title: %s -date: %s -tags: %s -identifier: %s ----------------------------\n\n" - "Plain text front matter. -It is passed to `format' with arguments TITLE, DATE, KEYWORDS, -ID. Advanced users are advised to consult Info node `(denote) -Change the front matter format'.") - -(define-obsolete-function-alias - 'denote-surround-with-quotes - 'denote-format-string-for-md-front-matter - "2.3.0") - -(defun denote-format-string-for-md-front-matter (s) - "Surround string S with quotes. -If S is not a string, return a literal emptry string. - -This can be used in `denote-file-types' to format front mattter." - (if (stringp s) - (format "%S" s) - "\"\"")) - -(defun denote-trim-whitespace (s) - "Trim whitespace around string S. -This can be used in `denote-file-types' to format front mattter." - (string-trim s)) - -(defun denote--trim-quotes (s) - "Trim quotes around string S." - (let ((trims "[\"']+")) - (string-trim s trims trims))) - -(defun denote-trim-whitespace-then-quotes (s) - "Trim whitespace then quotes around string S. -This can be used in `denote-file-types' to format front mattter." - (denote--trim-quotes (denote-trim-whitespace s))) - -(defun denote-format-string-for-org-front-matter (s) - "Return string S as-is for Org or plain text front matter. -If S is not a string, return an empty string." - (if (stringp s) s "")) - -(defun denote-format-keywords-for-md-front-matter (keywords) - "Format front matter KEYWORDS for markdown file type. -KEYWORDS is a list of strings. Consult the `denote-file-types' -for how this is used." - (format "[%s]" (mapconcat (lambda (k) (format "%S" k)) keywords ", "))) - -(defun denote-format-keywords-for-text-front-matter (keywords) - "Format front matter KEYWORDS for text file type. -KEYWORDS is a list of strings. Consult the `denote-file-types' -for how this is used." - (string-join keywords " ")) - -(defun denote-format-keywords-for-org-front-matter (keywords) - "Format front matter KEYWORDS for org file type. -KEYWORDS is a list of strings. Consult the `denote-file-types' -for how this is used." - (if keywords - (format ":%s:" (string-join keywords ":")) - "")) - -(defun denote-extract-keywords-from-front-matter (keywords-string) - "Extract keywords list from front matter KEYWORDS-STRING. -Split KEYWORDS-STRING into a list of strings. - -Consult the `denote-file-types' for how this is used." - (split-string keywords-string "[:,\s]+" t "[][ \"']+")) - -(defvar denote-file-types - '((org - :extension ".org" - :date-function denote-date-org-timestamp - :front-matter denote-org-front-matter - :title-key-regexp "^#\\+title\\s-*:" - :title-value-function denote-format-string-for-org-front-matter - :title-value-reverse-function denote-trim-whitespace - :keywords-key-regexp "^#\\+filetags\\s-*:" - :keywords-value-function denote-format-keywords-for-org-front-matter - :keywords-value-reverse-function denote-extract-keywords-from-front-matter - :link denote-org-link-format - :link-in-context-regexp denote-org-link-in-context-regexp) - (markdown-yaml - :extension ".md" - :date-function denote-date-rfc3339 - :front-matter denote-yaml-front-matter - :title-key-regexp "^title\\s-*:" - :title-value-function denote-format-string-for-md-front-matter - :title-value-reverse-function denote-trim-whitespace-then-quotes - :keywords-key-regexp "^tags\\s-*:" - :keywords-value-function denote-format-keywords-for-md-front-matter - :keywords-value-reverse-function denote-extract-keywords-from-front-matter - :link denote-md-link-format - :link-in-context-regexp denote-md-link-in-context-regexp) - (markdown-toml - :extension ".md" - :date-function denote-date-rfc3339 - :front-matter denote-toml-front-matter - :title-key-regexp "^title\\s-*=" - :title-value-function denote-format-string-for-md-front-matter - :title-value-reverse-function denote-trim-whitespace-then-quotes - :keywords-key-regexp "^tags\\s-*=" - :keywords-value-function denote-format-keywords-for-md-front-matter - :keywords-value-reverse-function denote-extract-keywords-from-front-matter - :link denote-md-link-format - :link-in-context-regexp denote-md-link-in-context-regexp) - (text - :extension ".txt" - :date-function denote-date-iso-8601 - :front-matter denote-text-front-matter - :title-key-regexp "^title\\s-*:" - :title-value-function denote-format-string-for-org-front-matter - :title-value-reverse-function denote-trim-whitespace - :keywords-key-regexp "^tags\\s-*:" - :keywords-value-function denote-format-keywords-for-text-front-matter - :keywords-value-reverse-function denote-extract-keywords-from-front-matter - :link denote-org-link-format - :link-in-context-regexp denote-org-link-in-context-regexp)) - "Alist of `denote-file-type' and their format properties. - -Each element is of the form (SYMBOL PROPERTY-LIST). SYMBOL is -one of those specified in `denote-file-type' or an arbitrary -symbol that defines a new file type. - -PROPERTY-LIST is a plist that consists of the following elements: - -- `:extension' is a string with the file extension including the - period. - -- `:date-function' is a function that can format a date. See the - functions `denote-date-iso-8601', `denote-date-rfc3339', and - `denote-date-org-timestamp'. - -- `:front-matter' is either a string passed to `format' or a - variable holding such a string. The `format' function accepts - four arguments, which come from `denote' in this order: TITLE, - DATE, KEYWORDS, IDENTIFIER. Read the doc string of `format' on - how to reorder arguments. - -- `:title-key-regexp' is a regular expression that is used to - retrieve the title line in a file. The first line matching - this regexp is considered the title line. - -- `:title-value-function' is the function used to format the raw - title string for inclusion in the front matter (e.g. to - surround it with quotes). Use the `identity' function if no - further processing is required. - -- `:title-value-reverse-function' is the function used to - retrieve the raw title string from the front matter. It - performs the reverse of `:title-value-function'. - -- `:keywords-key-regexp' is a regular expression used to retrieve - the keywords' line in the file. The first line matching this - regexp is considered the keywords' line. - -- `:keywords-value-function' is the function used to format the - keywords' list of strings as a single string, with appropriate - delimiters, for inclusion in the front matter. - -- `:keywords-value-reverse-function' is the function used to - retrieve the keywords' value from the front matter. It - performs the reverse of the `:keywords-value-function'. - -- `:link' is a string, or variable holding a string, that - specifies the format of a link. See the variables - `denote-org-link-format', `denote-md-link-format'. - -- `:link-in-context-regexp' is a regular expression that is used - to match the aforementioned link format. See the variables - `denote-org-link-in-context-regexp',`denote-md-link-in-context-regexp'. - -If `denote-file-type' is nil, use the first element of this list -for new note creation. The default is `org'.") - -(defun denote--date-format-function (file-type) - "Return date format function of FILE-TYPE." - (plist-get - (alist-get file-type denote-file-types) - :date-function)) - -(defun denote--file-extension (file-type) - "Return file type extension based on FILE-TYPE." - (plist-get - (alist-get file-type denote-file-types) - :extension)) - -(defun denote--front-matter (file-type) - "Return front matter based on FILE-TYPE." - (let ((prop (plist-get - (alist-get file-type denote-file-types) - :front-matter))) - (if (symbolp prop) - (symbol-value prop) - prop))) - -(defun denote--title-key-regexp (file-type) - "Return the title key regexp associated to FILE-TYPE." - (plist-get - (alist-get file-type denote-file-types) - :title-key-regexp)) - -(defun denote--title-value-function (file-type) - "Convert title string to a front matter title, per FILE-TYPE." - (plist-get - (alist-get file-type denote-file-types) - :title-value-function)) - -(defun denote--title-value-reverse-function (file-type) - "Convert front matter title to the title string, per FILE-TYPE." - (plist-get - (alist-get file-type denote-file-types) - :title-value-reverse-function)) - -(defun denote--keywords-key-regexp (file-type) - "Return the keywords key regexp associated to FILE-TYPE." - (plist-get - (alist-get file-type denote-file-types) - :keywords-key-regexp)) - -(defun denote--keywords-value-function (file-type) - "Convert keywords' string to front matter keywords, per FILE-TYPE." - (plist-get - (alist-get file-type denote-file-types) - :keywords-value-function)) - -(defun denote--keywords-value-reverse-function (file-type) - "Convert front matter keywords to keywords' list, per FILE-TYPE." - (plist-get - (alist-get file-type denote-file-types) - :keywords-value-reverse-function)) - -(defun denote--link-format (file-type) - "Return link format extension based on FILE-TYPE." - (let ((prop (plist-get - (alist-get file-type denote-file-types) - :link))) - (if (symbolp prop) - (symbol-value prop) - prop))) - -(defun denote--link-in-context-regexp (file-type) - "Return link regexp in context based on FILE-TYPE." - (let ((prop (plist-get - (alist-get file-type denote-file-types) - :link-in-context-regexp))) - (if (symbolp prop) - (symbol-value prop) - prop))) - -(define-obsolete-function-alias - 'denote--extensions - 'denote-file-type-extensions - "2.0.0") - -(defun denote-file-type-extensions () - "Return all file type extensions in `denote-file-types'." - (delete-dups - (mapcar (lambda (type) - (plist-get (cdr type) :extension)) - denote-file-types))) - -(defun denote--file-type-keys () - "Return all `denote-file-types' keys." - (delete-dups (mapcar #'car denote-file-types))) - -(defun denote--format-front-matter (title date keywords id filetype) - "Front matter for new notes. - -TITLE, DATE, and ID are all strings or functions that return a -string. KEYWORDS is a list of strings. FILETYPE is one of the -values of `denote-file-type'." - (let* ((fm (denote--front-matter filetype)) - (title (denote--format-front-matter-title title filetype)) - (kws (denote--format-front-matter-keywords keywords filetype))) - (if fm (format fm title date kws id) ""))) - -(defun denote--get-title-line-from-front-matter (title file-type) - "Retrieve title line from front matter based on FILE-TYPE. -Format TITLE in the title line. The returned line does not -contain the newline." - (let ((front-matter (denote--format-front-matter title "" nil "" file-type)) - (key-regexp (denote--title-key-regexp file-type))) - (with-temp-buffer - (insert front-matter) - (goto-char (point-min)) - (when (re-search-forward key-regexp nil t 1) - (buffer-substring-no-properties (line-beginning-position) (line-end-position)))))) - -(defun denote--get-keywords-line-from-front-matter (keywords file-type) - "Retrieve keywords line from front matter based on FILE-TYPE. -Format KEYWORDS in the keywords line. The returned line does not -contain the newline." - (let ((front-matter (denote--format-front-matter "" "" keywords "" file-type)) - (key-regexp (denote--keywords-key-regexp file-type))) - (with-temp-buffer - (insert front-matter) - (goto-char (point-min)) - (when (re-search-forward key-regexp nil t 1) - (buffer-substring-no-properties (line-beginning-position) (line-end-position)))))) - -;;;; Front matter or content retrieval functions - -(defun denote-retrieve-filename-identifier (file) - "Extract identifier from FILE name, if present, else return nil. - -To create a new one, refer to the function -`denote-create-unique-file-identifier'." - (let ((filename (file-name-nondirectory file))) - (if (string-match (concat "\\`" denote-id-regexp) filename) - (match-string-no-properties 0 filename)))) - -;; TODO 2023-12-08: Maybe we can only use -;; `denote-retrieve-filename-identifier' and remove this function. -(defun denote-retrieve-filename-identifier-with-error (file) - "Extract identifier from FILE name, if present, else signal an error." - (or (denote-retrieve-filename-identifier file) - (error "Cannot find `%s' as a file with a Denote identifier" file))) - -(defun denote-get-identifier (&optional date) - "Convert DATE into a Denote identifier using `denote-id-format'. -DATE is parsed by `denote-valid-date-p'. If DATE is nil, use the -current time." - (format-time-string - denote-id-format - (when date (denote-valid-date-p date)))) - -(defun denote-create-unique-file-identifier (file used-ids &optional date) - "Generate a unique identifier for FILE not in USED-IDS hash-table. - -The conditions are as follows: - -- If optional DATE is non-nil pass it to `denote-get-identifier'. - DATE will have to conform with `denote-valid-date-p'. If it - does not, return an error. - -- If optional DATE is nil, use the file attributes to determine - the last modified date and format it as an identifier. - -- As a fallback, derive an identifier from the current time. - -To only return an existing identifier, refer to the function -`denote-retrieve-filename-identifier'." - (let ((id (cond - (date (denote-get-identifier date)) - ((denote--file-attributes-time file)) - (t (denote-get-identifier))))) - (denote--find-first-unused-id id used-ids))) - -(define-obsolete-function-alias - 'denote-retrieve-or-create-file-identifier - 'denote-retrieve-filename-identifier - "2.1.0") - -(defun denote-retrieve-filename-keywords (file) - "Extract keywords from FILE name, if present, else return nil. -Return matched keywords as a single string." - (let ((filename (file-name-nondirectory file))) - (when (string-match denote-keywords-regexp filename) - (match-string 1 filename)))) - -(defun denote-retrieve-filename-signature (file) - "Extract signature from FILE name, if present, else return nil." - (let ((filename (file-name-nondirectory file))) - (when (string-match denote-signature-regexp filename) - (match-string 1 filename)))) - -(defun denote-retrieve-filename-title (file) - "Extract Denote title component from FILE name, else return nil." - (let ((filename (file-name-nondirectory file))) - (when (string-match denote-title-regexp filename) - (match-string 1 filename)))) - -(defun denote--file-with-temp-buffer-subr (file) - "Return path to FILE or its buffer together with the appropriate function. -Subroutine of `denote--file-with-temp-buffer'." - (let* ((buffer (get-file-buffer file)) - (file-exists (file-exists-p file)) - (buffer-modified (buffer-modified-p buffer))) - (cond - ((or (and file-exists - buffer - (not buffer-modified) - (not (eq buffer-modified 'autosaved))) - (and file-exists (not buffer))) - (cons #'insert-file-contents file)) - (buffer - (cons #'insert-buffer buffer)) - ;; (t - ;; (error "Cannot find anything about file `%s'" file)) - ))) - -(defmacro denote--file-with-temp-buffer (file &rest body) - "If FILE exists, insert its contents in a temp buffer and call BODY." - (declare (indent 1)) - `(when-let ((file-and-function (denote--file-with-temp-buffer-subr ,file))) - (with-temp-buffer - (funcall (car file-and-function) (cdr file-and-function)) - (goto-char (point-min)) - ,@body))) - -(defun denote-retrieve-front-matter-title-value (file file-type) - "Return title value from FILE front matter per FILE-TYPE." - (denote--file-with-temp-buffer file - (when (re-search-forward (denote--title-key-regexp file-type) nil t 1) - (funcall (denote--title-value-reverse-function file-type) - (buffer-substring-no-properties (point) (line-end-position)))))) - -(defun denote-retrieve-front-matter-title-line (file file-type) - "Return title line from FILE front matter per FILE-TYPE." - (denote--file-with-temp-buffer file - (when (re-search-forward (denote--title-key-regexp file-type) nil t 1) - (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) - -(defun denote-retrieve-front-matter-keywords-value (file file-type) - "Return keywords value from FILE front matter per FILE-TYPE. -The return value is a list of strings. To get a combined string -the way it would appear in a Denote file name, use -`denote-retrieve-front-matter-keywords-value-as-string'." - (denote--file-with-temp-buffer file - (when (re-search-forward (denote--keywords-key-regexp file-type) nil t 1) - (funcall (denote--keywords-value-reverse-function file-type) - (buffer-substring-no-properties (point) (line-end-position)))))) - -(defun denote-retrieve-front-matter-keywords-value-as-string (file file-type) - "Return keywords value from FILE front matter per FILE-TYPE. -The return value is a string, with the underscrore as a separator -between individual keywords. To get a list of strings instead, -use `denote-retrieve-front-matter-keywords-value' (the current function uses -that internally)." - (denote-keywords-combine (denote-retrieve-front-matter-keywords-value file file-type))) - -(defun denote-retrieve-front-matter-keywords-line (file file-type) - "Return keywords line from FILE front matter per FILE-TYPE." - (denote--file-with-temp-buffer file - (when (re-search-forward (denote--keywords-key-regexp file-type) nil t 1) - (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) - -(defalias 'denote-retrieve-title-value 'denote-retrieve-front-matter-title-value - "Alias for `denote-retrieve-front-matter-title-value'.") - -(defalias 'denote-retrieve-title-line 'denote-retrieve-front-matter-title-line - "Alias for `denote-retrieve-front-matter-title-line'.") - -(defalias 'denote-retrieve-keywords-value 'denote-retrieve-front-matter-keywords-value - "Alias for `denote-retrieve-front-matter-keywords-value'.") - -(defalias 'denote-retrieve-keywords-line 'denote-retrieve-front-matter-keywords-line - "Alias for `denote-retrieve-front-matter-keywords-line'.") - -(defalias 'denote-retrieve-keywords-value-as-string 'denote-retrieve-front-matter-keywords-value-as-string - "Alias for `denote-retrieve-front-matter-keywords-value-as-string'.") - -(define-obsolete-function-alias - 'denote--retrieve-title-or-filename - 'denote-retrieve-title-or-filename - "2.3.0") - -(defun denote-retrieve-title-or-filename (file type) - "Return appropriate title for FILE given its TYPE. -Try to find the value of the title in the front matter of FILE, -otherwise use its file name. - -This is a wrapper for `denote-retrieve-front-matter-title-value' and -`denote-retrieve-filename-title'." - (if-let (((denote-file-is-note-p file)) - (title (denote-retrieve-front-matter-title-value file type)) - ((not (string-blank-p title)))) - title - (or (denote-retrieve-filename-title file) - (file-name-base file)))) - -(defun denote--retrieve-location-in-xrefs (identifier) - "Return list of xrefs for IDENTIFIER with their respective location. -Limit the search to text files, per `denote-directory-files' with -non-nil `text-only' parameter." - (mapcar #'xref-match-item-location - (xref-matches-in-files identifier - (denote-directory-files nil nil :text-only)))) - -(defun denote--retrieve-group-in-xrefs (identifier) - "Access location of xrefs for IDENTIFIER and group them per file. -See `denote--retrieve-locations-in-xrefs'." - (mapcar #'xref-location-group - (denote--retrieve-location-in-xrefs identifier))) - -(defun denote--retrieve-files-in-xrefs (identifier) - "Return sorted, deduplicated file names with IDENTIFIER in their contents." - (sort - (delete-dups - (denote--retrieve-group-in-xrefs identifier)) - #'string-collate-lessp)) - -;;;; New note - -;;;;; Common helpers for new notes - -(defun denote-format-file-name (dir-path id keywords title extension signature) - "Format file name. -DIR-PATH, ID, KEYWORDS, TITLE, EXTENSION and SIGNATURE are -expected to be supplied by `denote' or equivalent command. - -DIR-PATH is a string pointing to a directory. It ends with a -forward slash (the function `denote-directory' makes sure this is -the case when returning the value of the variable `denote-directory'). -DIR-PATH cannot be nil or an empty string. - -ID is a string holding the identifier of the note. It cannot be -nil or an empty string and must match `denote-id-regexp'. - -DIR-PATH and ID form the base file name. - -KEYWORDS is a list of strings that is reduced to a single string -by `denote-keywords-combine'. KEYWORDS can be an empty list or -a nil value, in which case the relevant file name component is -not added to the base file name. - -TITLE and SIGNATURE are strings. They can be an empty string, in -which case their respective file name component is not added to -the base file name. - -EXTENSION is a string that contains a dot followed by the file -type extension. It can be an empty string or a nil value, in -which case it is not added to the base file name." - (cond - ((null dir-path) - (error "DIR-PATH must not be nil")) - ((string-empty-p dir-path) - (error "DIR-PATH must not be an empty string")) - ((not (string-suffix-p "/" dir-path)) - (error "DIR-PATH does not end with a / as directories ought to")) - ((null id) - (error "ID must not be nil")) - ((string-empty-p id) - (error "ID must not be an empty string")) - ((not (string-match-p denote-id-regexp id)) - (error "ID `%s' does not match `denote-id-regexp'" id))) - (let ((file-name (concat dir-path id))) - (when (and signature (not (string-empty-p signature))) - (setq file-name (concat file-name "==" (denote-sluggify 'signature signature)))) - (when (and title (not (string-empty-p title))) - (setq file-name (concat file-name "--" (denote-sluggify 'title title)))) - (when keywords - (setq file-name (concat file-name "__" (denote-keywords-combine (denote-sluggify-keywords keywords))))) - (concat file-name extension))) - -(defun denote--format-front-matter-title (title file-type) - "Format TITLE according to FILE-TYPE for the file's front matter." - (funcall (denote--title-value-function file-type) title)) - -(defun denote--format-front-matter-keywords (keywords file-type) - "Format KEYWORDS according to FILE-TYPE for the file's front matter. -Apply `denote-sluggify' to KEYWORDS." - (let ((kws (denote-sluggify-keywords keywords))) - (funcall (denote--keywords-value-function file-type) kws))) - -(defun denote--path (title keywords dir id file-type signature) - "Return path to new file. -Use ID, TITLE, KEYWORDS, FILE-TYPE and SIGNATURE to construct -path to DIR." - (denote-format-file-name - dir id keywords title (denote--file-extension file-type) signature)) - -;; Adapted from `org-hugo--org-date-time-to-rfc3339' in the `ox-hugo' -;; package: . -(defun denote-date-rfc3339 (date) - "Format DATE using the RFC3339 specification." - (replace-regexp-in-string - "\\([0-9]\\{2\\}\\)\\([0-9]\\{2\\}\\)\\'" "\\1:\\2" - (format-time-string "%FT%T%z" date))) - -(defun denote-date-org-timestamp (date) - "Format DATE using the Org inactive timestamp notation." - (format-time-string "[%F %a %R]" date)) - -(defun denote-date-iso-8601 (date) - "Format DATE according to ISO 8601 standard." - (format-time-string "%F" date)) - -(defun denote--date (date file-type) - "Expand DATE in an appropriate format for FILE-TYPE." - (let ((format denote-date-format)) - (cond - (format - (format-time-string format date)) - ((when-let ((fn (denote--date-format-function file-type))) - (funcall fn date))) - (t - (denote-date-org-timestamp date))))) - -(defun denote--prepare-note (title keywords date id directory file-type template signature) - "Prepare a new note file. - -Arguments TITLE, KEYWORDS, DATE, ID, DIRECTORY, FILE-TYPE, -TEMPLATE, and SIGNATURE should be valid for note creation." - (let* ((path (denote--path title keywords directory id file-type signature)) - (buffer (find-file path)) - (header (denote--format-front-matter - title (denote--date date file-type) keywords - id - file-type))) - (with-current-buffer buffer - (insert header) - (insert template)))) - -(defun denote--dir-in-denote-directory-p (directory) - "Return non-nil if DIRECTORY is in variable `denote-directory'." - (and directory - (string-prefix-p (denote-directory) - (expand-file-name directory)))) - -(defun denote--valid-file-type (filetype) - "Return a valid filetype symbol given the argument FILETYPE. -If none is found, the first element of `denote-file-types' is -returned." - (let ((type (cond - ((stringp filetype) (intern filetype)) - ((symbolp filetype) filetype) - (t (error "The `%s' is neither a string nor a symbol" filetype))))) - (if (memq type (denote--file-type-keys)) - type - (caar denote-file-types)))) - -(defun denote--date-add-current-time (date) - "Add current time to DATE, if necessary. -The idea is to turn 2020-01-15 into 2020-01-15 16:19 so that the -hour and minute component is not left to 00:00. - -This reduces the burden on the user who would otherwise need to -input that value in order to avoid the error of duplicate -identifiers. - -It also addresses a difference between Emacs 28 and Emacs 29 -where the former does not read dates without a time component." - (if (<= (length date) 10) - (format "%s %s" date (format-time-string "%H:%M:%S" (current-time))) - date)) - -(define-obsolete-function-alias - 'denote--valid-date - 'denote-valid-date-p - "2.3.0") - -(defun denote-valid-date-p (date) - "Return DATE as a valid date. -A valid DATE is a value that can be parsed by either -`decode-time' or `date-to-time'. Those functions signal an error -if DATE is a value they do not recognise. - -If DATE is nil, return nil." - (if (and (or (numberp date) (listp date)) - (decode-time date)) - date - (date-to-time (denote--date-add-current-time date)))) - -(defun denote-parse-date (date) - "Return DATE as an appropriate value for the `denote' command. -Pass DATE through `denote-valid-date-p' and use its return value. -If either that or DATE is nil, return `current-time'." - (or (denote-valid-date-p date) (current-time))) - -(defun denote--buffer-file-names () - "Return file names of Denote buffers." - (delq nil - (mapcar - (lambda (buffer) - (when-let (((buffer-live-p buffer)) - (file (buffer-file-name buffer)) - ((denote-filename-is-note-p file))) - file)) - (buffer-list)))) - -(defun denote--id-exists-p (identifier) - "Return non-nil if IDENTIFIER already exists." - (seq-some - (lambda (file) - (string-prefix-p identifier (file-name-nondirectory file))) - (append (denote-directory-files) (denote--buffer-file-names)))) - -(defun denote--get-all-used-ids () - "Return a hash-table of all used identifiers. -It checks files in variable `denote-directory' and active buffer files." - (let* ((ids (make-hash-table :test 'equal)) - (file-names (mapcar - (lambda (file) (file-name-nondirectory file)) - (denote-directory-files))) - (names (append file-names (denote--buffer-file-names)))) - (dolist (name names) - (let ((id (denote-retrieve-filename-identifier name))) - (puthash id t ids))) - ids)) - -(defun denote--find-first-unused-id (id used-ids) - "Return the first unused id starting at ID from USED-IDS. -USED-IDS is a hash-table of all used IDs. If ID is already used, -increment it 1 second at a time until an available id is found." - (let ((current-id id)) - (while (gethash current-id used-ids) - (setq current-id (denote-get-identifier (time-add (date-to-time current-id) 1)))) - current-id)) - -(make-obsolete 'denote-barf-duplicate-id nil "2.1.0") - -(defvar denote-command-prompt-history nil - "Minibuffer history for `denote-command-prompt'.") - -(defalias 'denote--command-prompt-history 'denote-command-prompt-history - "Compatibility alias for `denote-command-prompt-history'.") - -(defun denote-command-prompt () - "Prompt for command among `denote-commands-for-new-notes'." - (let ((default (car denote-command-prompt-history))) - (intern - (completing-read - (format-prompt "Run note-creating Denote command" default) - denote-commands-for-new-notes nil :require-match - nil 'denote-command-prompt-history default)))) - -;;;;; The `denote' command and its prompts - -(defun denote--prompt-with-completion-p (fn) - "Return non-nil if FN prompt should perform completion. -FN is one among `denote-prompts-with-history-as-completion' and performs -completion when the user option `denote-history-completion-in-prompts' -is non-nil." - (and denote-history-completion-in-prompts - (memq fn denote-prompts-with-history-as-completion))) - -(defvar denote-ignore-region-in-denote-command nil - "If non-nil, the region is ignored by the `denote' command. - -The `denote' command uses the region as the default title when -prompted for a title. When this variable is non-nil, the -`denote' command ignores the region. This variable is useful in -commands that have their own way of handling the region.") - -(defvar denote-title-prompt-current-default nil - "Currently bound default title for `denote-title-prompt'. -Set the value of this variable within the lexical scope of a -command that needs to supply a default title before calling -`denote-title-prompt'.") - -(defun denote--command-with-features (command force-use-file-prompt-as-default-title force-ignore-region force-save in-background) - "Execute file-creating COMMAND with specified features. - -COMMAND is the symbol of a file-creating command to call, such as -`denote' or `denote-signature'. - -With non-nil FORCE-USE-FILE-PROMPT-AS-DEFAULT-TITLE, use the last -item of `denote-file-history' as the default title of the title -prompt. This is useful in a command such as `denote-link' where -the entry of the file prompt can be reused as the default title. - -With non-nil FORCE-IGNORE-REGION, the region is ignore when -creating the note, i.e. it will not be used as the initial title -in a title prompt. Else, the value of -`denote-ignore-region-in-denote-command' is respected. - -With non-nil FORCE-SAVE, the file is saved at the end of the note -creation. Else, the value of `denote-save-buffer-after-creation' -is respected. - -With non-nil IN-BACKGROUND, the note creation happens in the -background, i.e. the note's buffer will not be displayed after -the note is created. - -Note that if all parameters except COMMAND are nil, this is -equivalent to `(call-interactively command)'. - -The path of the newly created file is returned." - (let ((denote-save-buffer-after-creation - (or force-save denote-save-buffer-after-creation)) - (denote-ignore-region-in-denote-command - (or force-ignore-region denote-ignore-region-in-denote-command)) - (denote-title-prompt-current-default - (if force-use-file-prompt-as-default-title - (when denote-file-history - (file-name-nondirectory (pop denote-file-history))) - denote-title-prompt-current-default)) - (path)) - (if in-background - (save-window-excursion - (call-interactively command) - (setq path (buffer-file-name))) - (call-interactively command) - (setq path (buffer-file-name))) - path)) - -;;;###autoload -(defun denote (&optional title keywords file-type subdirectory date template signature) - "Create a new note with the appropriate metadata and file name. - -Run the `denote-after-new-note-hook' after creating the new note. - -When called interactively, the metadata and file name are prompted -according to the value of `denote-prompts'. - -When called from Lisp, all arguments are optional. - -- TITLE is a string or a function returning a string. - -- KEYWORDS is a list of strings. The list can be empty or the - value can be set to nil. - -- FILE-TYPE is a symbol among those described in `denote-file-type'. - -- SUBDIRECTORY is a string representing the path to either the - value of the variable `denote-directory' or a subdirectory - thereof. The subdirectory must exist: Denote will not create - it. If SUBDIRECTORY does not resolve to a valid path, the - variable `denote-directory' is used instead. - -- DATE is a string representing a date like 2022-06-30 or a date - and time like 2022-06-16 14:30. A nil value or an empty string - is interpreted as the `current-time'. - -- TEMPLATE is a symbol which represents the key of a cons cell in - the user option `denote-templates'. The value of that key is - inserted to the newly created buffer after the front matter. - -- SIGNATURE is a string or a function returning a string." - (interactive - (let ((args (make-vector 7 nil))) - (dolist (prompt denote-prompts) - (pcase prompt - ('title (aset args 0 (denote-title-prompt - (when (and (not denote-ignore-region-in-denote-command) - (use-region-p)) - (buffer-substring-no-properties - (region-beginning) - (region-end)))))) - ('keywords (aset args 1 (denote-keywords-prompt))) - ('file-type (aset args 2 (denote-file-type-prompt))) - ('subdirectory (aset args 3 (denote-subdirectory-prompt))) - ('date (aset args 4 (denote-date-prompt))) - ('template (aset args 5 (denote-template-prompt))) - ('signature (aset args 6 (denote-signature-prompt))))) - (append args nil))) - (let* ((title (or title "")) - (file-type (denote--valid-file-type (or file-type denote-file-type))) - (kws (denote-keywords-sort keywords)) - (date (denote-parse-date date)) - (id (denote--find-first-unused-id - (denote-get-identifier date) - (denote--get-all-used-ids))) - (directory (if (denote--dir-in-denote-directory-p subdirectory) - (file-name-as-directory subdirectory) - (denote-directory))) - (template (if (stringp template) - template - (or (alist-get template denote-templates) ""))) - (signature (or signature ""))) - (denote--prepare-note title kws date id directory file-type template signature) - (when denote-save-buffer-after-creation (save-buffer)) - (denote--keywords-add-to-history keywords) - (run-hooks 'denote-after-new-note-hook))) - -(defvar denote-title-history nil - "Minibuffer history of `denote-title-prompt'.") - -(defalias 'denote--title-history 'denote-title-history - "Compatibility alias for `denote-title-history'.") - -(defmacro denote--with-conditional-completion (fn prompt history &optional initial-value default-value) - "Produce body of FN that may perform completion. -Use PROMPT, HISTORY, INITIAL-VALUE, and DEFAULT-VALUE as arguments for -the given minibuffer prompt." - `(if (denote--prompt-with-completion-p ,fn) - ;; NOTE 2023-10-27: By default SPC performs completion in the - ;; minibuffer. We do not want that, as the user should be able to - ;; input an arbitrary string, while still performing completion - ;; against their input history. - (minibuffer-with-setup-hook - (lambda () - (use-local-map - (let ((map (make-composed-keymap nil (current-local-map)))) - (define-key map (kbd "SPC") nil) - map))) - (completing-read ,prompt ,history nil nil ,initial-value ',history ,default-value)) - (read-string ,prompt ,initial-value ',history ,default-value))) - -(defun denote-title-prompt (&optional initial-title prompt-text) - "Prompt for title string. - -With optional INITIAL-TITLE use it as the initial minibuffer -text. With optional PROMPT-TEXT use it in the minibuffer instead -of the default prompt. - -Previous inputs at this prompt are available for minibuffer completion -if the user option `denote-history-completion-in-prompts' is set to a -non-nil value." - (denote--with-conditional-completion - 'denote-title-prompt - (format-prompt (or prompt-text "New file TITLE") denote-title-prompt-current-default) - denote-title-history - initial-title - denote-title-prompt-current-default)) - -(defvar denote-file-type-history nil - "Minibuffer history of `denote-file-type-prompt'.") - -(defalias 'denote--file-type-history 'denote-file-type-history - "Compatibility alias for `denote-file-type-history'.") - -(defun denote-file-type-prompt () - "Prompt for `denote-file-type'. -Note that a non-nil value other than `text', `markdown-yaml', and -`markdown-toml' falls back to an Org file type. We use `org' -here for clarity." - (completing-read - "Select file TYPE: " (denote--file-type-keys) nil t - nil 'denote-file-type-history)) - -(defvar denote-date-history nil - "Minibuffer history of `denote-date-prompt'.") - -(defalias 'denote--date-history 'denote-date-history - "Compatibility alias for `denote-date-history'.") - -(declare-function org-read-date "org" (&optional with-time to-time from-string prompt default-time default-input inactive)) - -(defun denote-date-prompt () - "Prompt for date, expecting YYYY-MM-DD or that plus HH:MM. -Use Org's more advanced date selection utility if the user option -`denote-date-prompt-use-org-read-date' is non-nil." - (if (and denote-date-prompt-use-org-read-date - (require 'org nil :no-error)) - (let* ((time (org-read-date nil t)) - (org-time-seconds (format-time-string "%S" time)) - (cur-time-seconds (format-time-string "%S" (current-time)))) - ;; When the user does not input a time, org-read-date defaults to 00 for seconds. - ;; When the seconds are 00, we add the current seconds to avoid identifier collisions. - (when (string-equal "00" org-time-seconds) - (setq time (time-add time (string-to-number cur-time-seconds)))) - (format-time-string "%Y-%m-%d %H:%M:%S" time)) - (read-string - "DATE and TIME for note (e.g. 2022-06-16 14:30): " - nil 'denote-date-history))) - -(defun denote-prompt-for-date-return-id () - "Use `denote-date-prompt' and return it as `denote-id-format'." - (denote-get-identifier (denote-date-prompt))) - -(defvar denote-subdirectory-history nil - "Minibuffer history of `denote-subdirectory-prompt'.") - -(defalias 'denote--subdir-history 'denote-subdirectory-history - "Compatibility alias for `denote-subdirectory-history'.") - -;; Making it a completion table is useful for packages that read the -;; metadata, such as `marginalia' and `embark'. -(defun denote--subdirs-completion-table (dirs) - "Match DIRS as a completion table." - (let* ((def (car denote-subdirectory-history)) - (table (denote--completion-table 'file dirs)) - (prompt (if def - (format "Select SUBDIRECTORY [%s]: " def) - "Select SUBDIRECTORY: "))) - (completing-read prompt table nil t nil 'denote-subdirectory-history def))) - -(defun denote-subdirectory-prompt () - "Prompt for subdirectory of the variable `denote-directory'. -The table uses the `file' completion category (so it works with -packages such as `marginalia' and `embark')." - (let* ((root (directory-file-name (denote-directory))) - (subdirs (denote-directory-subdirectories)) - (dirs (push root subdirs))) - (denote--subdirs-completion-table dirs))) - -(defvar denote-template-history nil - "Minibuffer history of `denote-template-prompt'.") - -(defalias 'denote--template-history 'denote-template-history - "Compatibility alias for `denote-template-history'.") - -(defun denote-template-prompt () - "Prompt for template key in `denote-templates' and return its value." - (let ((templates denote-templates)) - (alist-get - (intern - (completing-read - "Select TEMPLATE key: " (mapcar #'car templates) - nil t nil 'denote-template-history)) - templates))) - -(defvar denote-signature-history nil - "Minibuffer history of `denote-signature-prompt'.") - -(defalias 'denote--signature-history 'denote-signature-history - "Compatibility alias for `denote-signature-history'.") - -(defun denote-signature-prompt (&optional initial-signature prompt-text) - "Prompt for signature string. -With optional INITIAL-SIGNATURE use it as the initial minibuffer -text. With optional PROMPT-TEXT use it in the minibuffer instead -of the default prompt. - -Previous inputs at this prompt are available for minibuffer completion -if the user option `denote-history-completion-in-prompts' is set to a -non-nil value." - (when (and initial-signature (string-empty-p initial-signature)) - (setq initial-signature nil)) - (denote--with-conditional-completion - 'denote-signature-prompt - (format-prompt (or prompt-text "New file SIGNATURE") nil) - denote-signature-history - initial-signature)) - -(defvar denote-files-matching-regexp-history nil - "Minibuffer history of `denote-files-matching-regexp-prompt'.") - -(defalias 'denote--files-matching-regexp-hist 'denote-files-matching-regexp-history - "Compatibility alias for `denote-files-matching-regexp-history'.") - -(defun denote-files-matching-regexp-prompt (&optional prompt-text) - "Prompt for REGEXP to filter Denote files by. -With optional PROMPT-TEXT use it instead of a generic prompt." - (denote--with-conditional-completion - 'denote-files-matching-regexp-prompt - (format-prompt (or prompt-text "Match files with the given REGEXP") nil) - denote-files-matching-regexp-history)) - -;;;;; Convenience commands as `denote' variants - -(defalias 'denote-create-note 'denote - "Alias for `denote' command.") - -(defun denote--add-prompts (additional-prompts) - "Add all the elements in the ADDITIONAL-PROMPTS list to `denote-prompts'." - (seq-union additional-prompts denote-prompts)) - -;;;###autoload -(defun denote-type () - "Create note while prompting for a file type. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `file-type' prompt appended to its existing prompts." - (declare (interactive-only t)) - (interactive) - (let ((denote-prompts (denote--add-prompts '(file-type)))) - (call-interactively #'denote))) - -(defalias 'denote-create-note-using-type 'denote-type - "Alias for `denote-type' command.") - -;;;###autoload -(defun denote-date () - "Create note while prompting for a date. - -The date can be in YEAR-MONTH-DAY notation like 2022-06-30 or -that plus the time: 2022-06-16 14:30. When the user option -`denote-date-prompt-use-org-read-date' is non-nil, the date -prompt uses the more powerful Org+calendar system. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `date' prompt appended to its existing prompts." - (declare (interactive-only t)) - (interactive) - (let ((denote-prompts (denote--add-prompts '(date)))) - (call-interactively #'denote))) - -(defalias 'denote-create-note-using-date 'denote-date - "Alias for `denote-date' command.") - -;;;###autoload -(defun denote-subdirectory () - "Create note while prompting for a subdirectory. - -Available candidates include the value of the variable -`denote-directory' and any subdirectory thereof. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `subdirectory' prompt appended to its existing prompts." - (declare (interactive-only t)) - (interactive) - (let ((denote-prompts (denote--add-prompts '(subdirectory)))) - (call-interactively #'denote))) - -(defalias 'denote-create-note-in-subdirectory 'denote-subdirectory - "Alias for `denote-subdirectory' command.") - -;;;###autoload -(defun denote-template () - "Create note while prompting for a template. - -Available candidates include the keys in the `denote-templates' -alist. The value of the selected key is inserted in the newly -created note after the front matter. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `template' prompt appended to its existing prompts." - (declare (interactive-only t)) - (interactive) - (let ((denote-prompts (denote--add-prompts '(template)))) - (call-interactively #'denote))) - -(defalias 'denote-create-note-with-template 'denote-template - "Alias for `denote-template' command.") - -;;;###autoload -(defun denote-signature () - "Create note while prompting for a file signature. - -This is the equivalent of calling `denote' when `denote-prompts' -has the `signature' prompt appended to its existing prompts." - (declare (interactive-only t)) - (interactive) - (let ((denote-prompts (denote--add-prompts '(signature)))) - (call-interactively #'denote))) - -(defalias 'denote-create-note-using-signature 'denote-signature - "Alias for `denote-signature' command.") - -;;;###autoload -(defun denote-region () - "Call `denote' and insert therein the text of the active region." - (declare (interactive-only t)) - (interactive) - (if-let (((region-active-p)) - ;; We capture the text early, otherwise it will be empty - ;; the moment `insert' is called. - (text (buffer-substring-no-properties (region-beginning) (region-end)))) - (progn - (let ((denote-ignore-region-in-denote-command t)) - (call-interactively 'denote)) - (push-mark (point)) - (insert text) - (run-hook-with-args 'denote-region-after-new-note-functions (mark) (point))) - (call-interactively 'denote))) - -;;;;; Other convenience commands - -;;;###autoload -(defun denote-open-or-create (target) - "Visit TARGET file in variable `denote-directory'. -If file does not exist, invoke `denote' to create a file. - -If TARGET file does not exist, add the user input that was used -to search for it to the minibuffer history of the -`denote-file-prompt'. The user can then retrieve and possibly -further edit their last input, using it as the newly created -note's actual title. At the `denote-file-prompt' type -\\\\[previous-history-element]." - (interactive (list (denote-file-prompt))) - (if (and target (file-exists-p target)) - (find-file target) - (denote--command-with-features #'denote :use-file-prompt-as-def-title nil nil nil))) - -;;;###autoload -(defun denote-open-or-create-with-command () - "Visit TARGET file in variable `denote-directory'. -If file does not exist, invoke `denote' to create a file. - -If TARGET file does not exist, add the user input that was used -to search for it to the minibuffer history of the -`denote-file-prompt'. The user can then retrieve and possibly -further edit their last input, using it as the newly created -note's actual title. At the `denote-file-prompt' type -\\\\[previous-history-element]." - (declare (interactive-only t)) - (interactive) - (let ((target (denote-file-prompt))) - (if (and target (file-exists-p target)) - (find-file target) - (denote--command-with-features (denote-command-prompt) :use-file-prompt-as-def-title nil nil nil)))) - -;;;; Note modification - -;;;;; Common helpers for note modifications - -(defun denote--file-types-with-extension (extension) - "Return only the entries of `denote-file-types' with EXTENSION. -See the format of `denote-file-types'." - (seq-filter (lambda (type) - (string-equal (plist-get (cdr type) :extension) extension)) - denote-file-types)) - -(defun denote--file-type-org-capture-p () - "Return non-nil if this is an `org-capture' buffer." - (and (bound-and-true-p org-capture-mode) - (derived-mode-p 'org-mode) - (string-match-p "\\`CAPTURE.*\\.org" (buffer-name)))) - -(defun denote-filetype-heuristics (file) - "Return likely file type of FILE. -If in the process of `org-capture', consider the file type to be that of -Org. Otherwise, use the file extension to detect the file type of FILE. - -If more than one file type correspond to this file extension, use the -first file type for which the :title-key-regexp in `denote-file-types' -matches in the file. - -If no file type in `denote-file-types' has the file extension, -the file type is assumed to be the first one in `denote-file-types'." - (cond - ((denote--file-type-org-capture-p) 'org) - (file - (let* ((extension (denote-get-file-extension-sans-encryption file)) - (types (denote--file-types-with-extension extension))) - (cond ((null types) - (caar denote-file-types)) - ((= (length types) 1) - (caar types)) - (t - (or (car (seq-find - (lambda (type) - (denote--regexp-in-file-p (plist-get (cdr type) :title-key-regexp) file)) - types)) - (caar types)))))))) - -(defun denote--file-attributes-time (file) - "Return `file-attribute-modification-time' of FILE as identifier." - (denote-get-identifier (file-attribute-modification-time (file-attributes file)))) - -(defun denote--revert-dired (buf) - "Revert BUF if appropriate. -Do it if BUF is in Dired mode and is either part of the variable -`denote-directory' or the `current-buffer'." - (let ((current (current-buffer))) - (with-current-buffer buf - (when (and (eq major-mode 'dired-mode) - (or (denote--dir-in-denote-directory-p default-directory) - (eq current buf))) - (revert-buffer))))) - -(defun denote-update-dired-buffers () - "Update Dired buffers of variable `denote-directory'. -Also revert the current Dired buffer even if it is not inside the -variable `denote-directory'." - (mapc #'denote--revert-dired (buffer-list))) - -(defun denote-rename-file-and-buffer (old-name new-name) - "Rename file named OLD-NAME to NEW-NAME, updating buffer name." - (unless (string= (expand-file-name old-name) (expand-file-name new-name)) - (cond - ((derived-mode-p 'dired-mode) - (dired-rename-file old-name new-name nil)) - ;; NOTE 2024-02-25: The `vc-rename-file' requires the file to be - ;; saved, but our convention is to not save the buffer after - ;; changing front matter unless we absolutely have to (allows - ;; users to do `diff-buffer-with-file', for example). - ((and denote-save-buffer-after-creation (not (buffer-modified-p)) (vc-backend old-name)) - (vc-rename-file old-name new-name)) - (t - (rename-file old-name new-name nil))) - (when-let ((buffer (find-buffer-visiting old-name))) - (with-current-buffer buffer - (set-visited-file-name new-name nil t))))) - -(defun denote--add-front-matter (file title keywords id file-type &optional save-buffer) - "Prepend front matter to FILE if `denote-file-is-note-p'. -The TITLE, KEYWORDS ID, and FILE-TYPE are passed from the -renaming command and are used to construct a new front matter -block if appropriate. - -With optional SAVE-BUFFER, save the buffer corresponding to FILE." - (when-let ((date (denote--date (date-to-time id) file-type)) - (new-front-matter (denote--format-front-matter title date keywords id file-type))) - (with-current-buffer (find-file-noselect file) - (goto-char (point-min)) - (insert new-front-matter) - (when save-buffer (save-buffer))))) - -(defun denote--regexp-in-file-p (regexp file) - "Return t if REGEXP matches in the FILE." - (denote--file-with-temp-buffer file - (re-search-forward regexp nil t 1))) - -(defun denote--edit-front-matter-p (file file-type) - "Test if FILE should be subject to front matter rewrite. -Use FILE-TYPE to look for the front matter lines. This is -relevant for operations that insert or rewrite the front matter -in a Denote note. - -For the purposes of this test, FILE is a Denote note when it -contains a title line, a keywords line or both." - (and (denote--front-matter file-type) - (or (denote--regexp-in-file-p (denote--title-key-regexp file-type) file) - (denote--regexp-in-file-p (denote--keywords-key-regexp file-type) file)))) - -(defun denote-rewrite-keywords (file keywords file-type &optional save-buffer) - "Rewrite KEYWORDS in FILE outright according to FILE-TYPE. - -Do the same as `denote-rewrite-front-matter' for keywords, -but do not ask for confirmation. - -With optional SAVE-BUFFER, save the buffer corresponding to FILE. - -This function is for use in the commands `denote-keywords-add', -`denote-keywords-remove', `denote-dired-rename-files', or -related." - (with-current-buffer (find-file-noselect file) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (when (re-search-forward (denote--keywords-key-regexp file-type) nil t 1) - (goto-char (line-beginning-position)) - (insert (denote--get-keywords-line-from-front-matter keywords file-type)) - (delete-region (point) (line-end-position)) - (when save-buffer (save-buffer))))))) - -(define-obsolete-function-alias - 'denote--rewrite-keywords - 'denote-rewrite-keywords - "2.0.0") - -(defun denote-rewrite-front-matter (file title keywords file-type &optional no-confirm) - "Rewrite front matter of note after `denote-rename-file'. -The FILE, TITLE, KEYWORDS, and FILE-TYPE are given by the -renaming command and are used to construct new front matter -values if appropriate. - -With optional NO-CONFIRM, do not prompt to confirm the rewriting -of the front matter. Otherwise produce a `y-or-n-p' prompt to -that effect. - -With optional NO-CONFIRM, save the buffer after performing the -rewrite. Otherwise leave it unsaved for furthter review by the -user." - (when-let ((old-title-line (denote-retrieve-front-matter-title-line file file-type)) - (old-keywords-line (denote-retrieve-front-matter-keywords-line file file-type)) - (new-title-line (denote--get-title-line-from-front-matter title file-type)) - (new-keywords-line (denote--get-keywords-line-from-front-matter keywords file-type))) - (with-current-buffer (find-file-noselect file) - (when (or no-confirm - (y-or-n-p (format - "Replace front matter?\n-%s\n+%s\n\n-%s\n+%s?" - (propertize old-title-line 'face 'denote-faces-prompt-old-name) - (propertize new-title-line 'face 'denote-faces-prompt-new-name) - (propertize old-keywords-line 'face 'denote-faces-prompt-old-name) - (propertize new-keywords-line 'face 'denote-faces-prompt-new-name)))) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (re-search-forward (denote--title-key-regexp file-type) nil t 1) - (goto-char (line-beginning-position)) - (insert new-title-line) - (delete-region (point) (line-end-position)) - (goto-char (point-min)) - (re-search-forward (denote--keywords-key-regexp file-type) nil t 1) - (goto-char (line-beginning-position)) - (insert new-keywords-line) - (delete-region (point) (line-end-position)) - (when no-confirm (save-buffer)))))))) - -(define-obsolete-function-alias - 'denote--rewrite-front-matter - 'denote-rewrite-front-matter - "2.0.0") - -;;;;; The renaming commands and their prompts - -(defun denote--rename-dired-file-or-prompt () - "Return Dired file at point, else prompt for one. -Throw error if FILE is not regular, else return FILE." - (or (dired-get-filename nil t) - (let* ((file (buffer-file-name)) - (format (if file - (format "Rename FILE Denote-style [%s]: " file) - "Rename FILE Denote-style: ")) - (selected-file (read-file-name format nil file t nil))) - (if (or (file-directory-p selected-file) - (not (file-regular-p selected-file))) - (user-error "Only rename regular files") - selected-file)))) - -(defun denote-rename-file-prompt (old-name new-name) - "Prompt to rename file named OLD-NAME to NEW-NAME." - (unless (string= (expand-file-name old-name) (expand-file-name new-name)) - (y-or-n-p - (format "Rename %s to %s?" - (propertize (file-name-nondirectory old-name) 'face 'denote-faces-prompt-old-name) - (propertize (file-name-nondirectory new-name) 'face 'denote-faces-prompt-new-name))))) - -;; NOTE 2023-10-20: We do not need a user option for this, though it -;; can be useful to have it as a variable. -(defvar denote-rename-max-mini-window-height 0.33 - "How much to enlarge `max-mini-window-height' for renaming operations.") - -;;;###autoload -(defun denote-rename-file (file &optional title keywords signature date) - "Rename file and update existing front matter if appropriate. - -Always rename the file where it is located in the file system: -never move it to another directory. - -If in Dired, consider FILE to be the one at point, else prompt -with minibuffer completion for one. When called from Lisp, FILE -is a file system path represented as a string. - -If FILE has a Denote-compliant identifier, retain it while -updating components of the file name referenced by the user -option `denote-prompts'. By default, these are the TITLE and -KEYWORDS. The SIGNATURE is another one. When called from Lisp, -TITLE and SIGNATURE are strings, while KEYWORDS is a list of -strings. - -If there is no identifier, create an identifier based on the -following conditions: - -1. If the `denote-prompts' includes an entry for date prompts, - then prompt for DATE and take its input to produce a new - identifier. For use in Lisp, DATE must conform with - `denote-valid-date-p'. - -2. If DATE is nil (e.g. when `denote-prompts' does not include a - date entry), use the file attributes to determine the last - modified date of FILE and format it as an identifier. - -3. As a fallback, derive an identifier from the current date and - time. - -4. At any rate, if the resulting identifier is not unique among - the files in the variable `denote-directory', increment it - such that it becomes unique. - -In interactive use, and assuming `denote-prompts' includes a -title entry, make the TITLE prompt have prefilled text in the -minibuffer that consists of the current title of FILE. The -current title is either retrieved from the front matter (such as -the #+title in Org) or from the file name. - -Do the same for the SIGNATURE prompt, subject to `denote-prompts', -by prefilling the minibuffer with the current signature of FILE, -if any. - -Same principle for the KEYWORDS prompt: convert the keywords in -the file name into a comma-separated string and prefill the -minibuffer with it (the KEYWORDS prompt accepts more than one -keywords, each separated by a comma, else the `crm-separator'). - -For all prompts, interpret an empty input as an instruction to -remove that file name component. For example, if a TITLE prompt -is available and FILE is 20240211T093531--some-title__keyword1.org -then rename FILE to 20240211T093531__keyword1.org. - -If a file name component is present, but there is no entry for it in -`denote-prompts', keep it as-is. - -[ NOTE: Please check with your minibuffer user interface how to - provide an empty input. The Emacs default setup accepts the - empty minibuffer contents as they are, though popular packages - like `vertico' use the first available completion candidate - instead. For `vertico', the user must either move one up to - select the prompt and then type RET there with empty contents, - or use the command `vertico-exit-input' with empty contents. - That Vertico command is bound to M-RET as of this writing on - 2024-02-13 08:08 +0200. ] - -When renaming FILE, read its file type extension (like .org) and -preserve it through the renaming process. Files that have no -extension are left without one. - -As a final step, ask for confirmation, showing the difference -between old and new file names. Do not ask for confirmation if -the user option `denote-rename-no-confirm' is set to a non-nil -value. - -If FILE has front matter for TITLE and KEYWORDS, ask to rewrite -their values in order to reflect the new input, unless -`denote-rename-no-confirm' is non-nil. When the -`denote-rename-no-confirm' is nil (the default), do not save the -underlying buffer, thus giving the user the option to -double-check the result, such as by invokling the command -`diff-buffer-with-file'. The rewrite of the TITLE and KEYWORDS -in the front matter should not affect the rest of the front -matter. - -If the file does not have front matter but is among the supported -file types (per `denote-file-type'), add front matter to the top -of it and leave the buffer unsaved for further inspection. Save -the buffer if `denote-rename-no-confirm' is non-nil. - -For the front matter of each file type, refer to the variables: - -- `denote-org-front-matter' -- `denote-text-front-matter' -- `denote-toml-front-matter' -- `denote-yaml-front-matter' - -Run the `denote-after-rename-file-hook' after renaming FILE. - -This command is intended to (i) rename Denote files, (ii) convert -existing supported file types to Denote notes, and (ii) rename -non-note files (e.g. PDF) that can benefit from Denote's -file-naming scheme. - -For a version of this command that works with multiple files -one-by-one, use `denote-dired-rename-files'." - (interactive - (let* ((file (denote--rename-dired-file-or-prompt)) - (file-type (denote-filetype-heuristics file)) - (file-in-prompt (propertize (file-relative-name file) 'face 'denote-faces-prompt-current-name)) - (date nil) - (title (denote-retrieve-title-or-filename file file-type)) - (keywords (denote-convert-file-name-keywords-to-crm (or (denote-retrieve-filename-keywords file) ""))) - (signature (or (denote-retrieve-filename-signature file) ""))) - (dolist (prompt denote-prompts) - (pcase prompt - ('title - (setq title (denote-title-prompt - title - (format "Rename `%s' with TITLE (empty to remove)" file-in-prompt)))) - ('keywords - (setq keywords (denote-keywords-prompt - (format "Rename `%s' with KEYWORDS (empty to remove)" file-in-prompt) - keywords))) - ('signature - (setq signature (denote-signature-prompt - signature - (format "Rename `%s' with SIGNATURE (empty to remove)" file-in-prompt)))) - ('date - (unless (denote-file-has-identifier-p file) - (setq date (denote-date-prompt)))))) - (list file title keywords signature date))) - (setq keywords (denote-keywords-sort - (if (stringp keywords) - (split-string keywords "," :omit-nulls) - keywords))) - (let* ((dir (file-name-directory file)) - (id (or (denote-retrieve-filename-identifier file) - (denote-create-unique-file-identifier file (denote--get-all-used-ids) date))) - ;; TODO 2024-02-13: Should we derive the extension from the - ;; `denote-file-type-prompt' if we are conforming with the - ;; `denote-prompts'? - (extension (denote-get-file-extension file)) - (file-type (denote-filetype-heuristics file)) - (new-name (denote-format-file-name dir id keywords title extension signature)) - (max-mini-window-height denote-rename-max-mini-window-height)) - (when (or denote-rename-no-confirm (denote-rename-file-prompt file new-name)) - (denote-rename-file-and-buffer file new-name) - (denote-update-dired-buffers) - (when (denote-file-is-writable-and-supported-p new-name) - (if (denote--edit-front-matter-p new-name file-type) - (denote-rewrite-front-matter new-name title keywords file-type denote-rename-no-confirm) - (denote--add-front-matter new-name title keywords id file-type denote-rename-no-confirm))) - (run-hooks 'denote-after-rename-file-hook)) - new-name)) - -;;;###autoload -(defun denote-dired-rename-files () - "Rename Dired marked files same way as `denote-rename-file'. -Rename each file in sequence, making all the relevant prompts. -Unlike `denote-rename-file', do not prompt for confirmation of -the changes made to the file: perform them outright (same as -setting `denote-rename-no-confirm' to a non-nil value)." - (declare (interactive-only t)) - (interactive nil dired-mode) - (if-let ((marks (dired-get-marked-files))) - (let ((used-ids (unless (seq-every-p #'denote-file-has-identifier-p marks) - (denote--get-all-used-ids)))) - (dolist (file marks) - (let* ((file-type (denote-filetype-heuristics file)) - (file-in-prompt (propertize (file-relative-name file) 'face 'denote-faces-prompt-current-name)) - (dir (file-name-directory file)) - (id (or (denote-retrieve-filename-identifier file) - (denote-create-unique-file-identifier file used-ids))) - (title (denote-retrieve-title-or-filename file file-type)) - (keywords (denote-convert-file-name-keywords-to-crm (or (denote-retrieve-filename-keywords file) ""))) - (signature (or (denote-retrieve-filename-signature file) "")) - (extension (denote-get-file-extension file))) - (dolist (prompt denote-prompts) - (pcase prompt - ('title - (setq title (denote-title-prompt - title - (format "Rename `%s' with TITLE (empty to remove)" file-in-prompt)))) - ('keywords - (setq keywords (denote-keywords-prompt - (format "Rename `%s' with KEYWORDS (empty to remove)" file-in-prompt) - keywords))) - ('signature - (setq signature (denote-signature-prompt - signature - (format "Rename `%s' with SIGNATURE (empty to remove)" file-in-prompt)))) - ('date - (setq id (denote-prompt-for-date-return-id))))) - (setq keywords (denote-keywords-sort - (if (stringp keywords) - (split-string keywords "," :omit-nulls) - keywords))) - (let ((new-name (denote-format-file-name dir id keywords title extension signature))) - (denote-rename-file-and-buffer file new-name) - (when (denote-file-is-writable-and-supported-p new-name) - (if (denote--edit-front-matter-p new-name file-type) - (denote-rewrite-front-matter new-name title keywords file-type :no-confirm) - (denote--add-front-matter new-name title keywords id file-type :save-buffer))) - (run-hooks 'denote-after-rename-file-hook) - (when used-ids - (puthash id t used-ids))))) - (denote-update-dired-buffers)) - (user-error "No marked files; aborting"))) - -(make-obsolete - 'denote-dired-rename-marked-files - 'denote-dired-rename-marked-files-with-keywords - "2.1.0") - -(defalias 'denote-dired-rename-marked-files 'denote-dired-rename-files - "Alias for `denote-dired-rename-files'.") - -;;;###autoload -(defun denote-dired-rename-marked-files-with-keywords () - "Rename marked files in Dired to a Denote file name by writing keywords. - -Specifically, do the following: - -- retain the file's existing name and make it the TITLE field, - per Denote's file-naming scheme; - -- sluggify the TITLE, according to our conventions (check the - user option `denote-file-name-slug-functions'); - -- prepend an identifier to the TITLE; - -- preserve the file's extension, if any; - -- prompt once for KEYWORDS and apply the user's input to the - corresponding field in the file name, rewriting any keywords - that may exist while removing keywords that do exist if - KEYWORDS is empty; - -- add or rewrite existing front matter to the underlying file, if - it is recognized as a Denote note (per `denote-file-type'), - such that it includes the new keywords. - -Run the `denote-after-rename-file-hook' after renaming is done. - -[ Note that the affected buffers are not saved, unless the user - option `denote-rename-no-confirm' is non-nil. Users can thus - check them to confirm that the new front matter does not cause - any problems (e.g. with the `diff-buffer-with-file' command). - Multiple buffers can be saved in one go with the command - `save-some-buffers' (read its doc string). ]" - (declare (interactive-only t)) - (interactive nil dired-mode) - (if-let ((marks (dired-get-marked-files))) - (let ((keywords (denote-keywords-sort - (denote-keywords-prompt "Rename marked files with KEYWORDS, overwriting existing (empty to ignore/remove)"))) - (used-ids (unless (seq-every-p #'denote-file-has-identifier-p marks) - (denote--get-all-used-ids)))) - (dolist (file marks) - (let* ((dir (file-name-directory file)) - (id (or (denote-retrieve-filename-identifier file) - (denote-create-unique-file-identifier file used-ids))) - (signature (or (denote-retrieve-filename-signature file) "")) - (file-type (denote-filetype-heuristics file)) - (title (denote-retrieve-title-or-filename file file-type)) - (extension (denote-get-file-extension file)) - (new-name (denote-format-file-name dir id keywords title extension signature))) - (denote-rename-file-and-buffer file new-name) - (when (denote-file-is-writable-and-supported-p new-name) - (if (denote--edit-front-matter-p new-name file-type) - (denote-rewrite-keywords new-name keywords file-type denote-rename-no-confirm) - (denote--add-front-matter new-name title keywords id file-type denote-rename-no-confirm))) - (run-hooks 'denote-after-rename-file-hook) - (when used-ids - (puthash id t used-ids)))) - (denote-update-dired-buffers)) - (user-error "No marked files; aborting"))) - -;;;###autoload -(defun denote-rename-file-using-front-matter (file &optional no-confirm save-buffer) - "Rename FILE using its front matter as input. -When called interactively, FILE is the return value of the -function `buffer-file-name' which is subsequently inspected for -the requisite front matter. It is thus implied that the FILE has -a file type that is supported by Denote, per `denote-file-type'. - -Unless NO-CONFIRM is non-nil (such as with a prefix argument), -ask for confirmation, showing the difference between the old and -the new file names. - -Never modify the identifier of the FILE, if any, even if it is -edited in the front matter. Denote considers the file name to be -the source of truth in this case to avoid potential breakage with -typos and the like. - -If NO-CONFIRM is non-nil (such as with a prefix argument) do not -prompt for confirmation while renaming the file. Do it outright. - -If optional SAVE-BUFFER is non-nil (such as with a double prefix -argument), save the corresponding buffer. - -If the user option `denote-rename-no-confirm' is non-nil, -interpret it the same way as a combination of NO-CONFIRM and -SAVE-BUFFER. - -The identifier of the file, if any, is never modified even if it -is edited in the front matter: Denote considers the file name to -be the source of truth in this case, to avoid potential breakage -with typos and the like." - (interactive - (let (no-confirm save-buffer) - (cond - ((and current-prefix-arg (> (prefix-numeric-value current-prefix-arg) 4)) - (setq no-confirm t - save-buffer t)) - (current-prefix-arg - (setq no-confirm t))) - (list buffer-file-name no-confirm save-buffer))) - (unless (denote-file-is-writable-and-supported-p file) - (user-error "The file is not writable or does not have a supported file extension")) - (if-let ((file-type (denote-filetype-heuristics file)) - (title (denote-retrieve-front-matter-title-value file file-type)) - (id (denote-retrieve-filename-identifier file))) - (let* ((keywords (denote-retrieve-front-matter-keywords-value file file-type)) - (signature (or (denote-retrieve-filename-signature file) "")) - (extension (denote-get-file-extension file)) - (dir (file-name-directory file)) - (new-name (denote-format-file-name dir id keywords title extension signature))) - (when (or denote-rename-no-confirm - no-confirm - (denote-rename-file-prompt file new-name)) - (denote-rename-file-and-buffer file new-name) - (denote-update-dired-buffers) - (when (or denote-rename-no-confirm save-buffer) - (save-buffer)) - (run-hooks 'denote-after-rename-file-hook))) - (user-error "No identifier or front matter for title"))) - -;;;###autoload -(defun denote-dired-rename-marked-files-using-front-matter () - "Call `denote-rename-file-using-front-matter' over the Dired marked files. -Refer to the documentation of that command for the technicalities. - -Marked files must count as notes for the purposes of Denote, -which means that they at least have an identifier in their file -name and use a supported file type, per `denote-file-type'. -Files that do not meet this criterion are ignored because Denote -cannot know if they have front matter and what that may be." - (interactive nil dired-mode) - (if-let ((marks (seq-filter - (lambda (m) - (and (denote-file-is-writable-and-supported-p m) - (denote-file-has-identifier-p m))) - (dired-get-marked-files)))) - (progn - (dolist (file marks) - (denote-rename-file-using-front-matter file :no-confirm denote-rename-no-confirm)) - (denote-update-dired-buffers)) - (user-error "No marked Denote files; aborting"))) - -;;;;;; Interactively modify keywords and rename accordingly - -;;;###autoload -(defun denote-keywords-add (keywords &optional save-buffer) - "Prompt for KEYWORDS to add to the current note's front matter. -When called from Lisp, KEYWORDS is a list of strings. - -Rename the file without further prompt so that its name reflects -the new front matter, per `denote-rename-file-using-front-matter'. - -With an optional SAVE-BUFFER (such as a prefix argument when -called interactively), save the buffer outright. Otherwise leave -the buffer unsaved for further review. - -If the user option `denote-rename-no-confirm' is non-nil, -interpret it the same way as SAVE-BUFFER, making SAVE-BUFFER -reduntant. - -Run `denote-after-rename-file-hook' as a final step." - (interactive (list (denote-keywords-prompt "Add KEYWORDS") current-prefix-arg)) - ;; A combination of if-let and let, as we need to take into account - ;; the scenario in which there are no keywords yet. - (if-let ((file (buffer-file-name)) - ((denote-file-is-note-p file)) - (file-type (denote-filetype-heuristics file))) - (let* ((cur-keywords (denote-retrieve-front-matter-keywords-value file file-type)) - (new-keywords (denote-keywords-sort - (seq-uniq (append keywords cur-keywords))))) - (denote-rewrite-keywords file new-keywords file-type) - (denote-rename-file-using-front-matter file :no-confirm (or denote-rename-no-confirm save-buffer)) - (run-hooks 'denote-after-rename-file-hook)) - (user-error "Buffer not visiting a Denote file"))) - -(defalias 'denote-rename-add-keywords 'denote-keywords-add - "Alias for `denote-keywords-add'.") - -(defun denote--keywords-delete-prompt (keywords) - "Prompt for one or more KEYWORDS. -In the case of multiple entries, those are separated by the -`crm-separator', which typically is a comma. In such a case, the -output is sorted with `string-collate-lessp'." - (let ((choice (denote--keywords-crm keywords "Keywords to remove"))) - (if denote-sort-keywords - (sort choice #'string-collate-lessp) - choice))) - -;;;###autoload -(defun denote-keywords-remove (&optional save-buffer) - "Prompt for keywords in current note and remove them. -Keywords are retrieved from the file's front matter. - -Rename the file without further prompt so that its name reflects -the new front matter, per `denote-rename-file-using-front-matter'. - -With an optional SAVE-BUFFER as a prefix argument, save the -buffer outright. Otherwise leave the buffer unsaved for further -review. - -If the user option `denote-rename-no-confirm' is non-nil, -interpret it the same way as SAVE-BUFFER, making SAVE-BUFFER -reduntant. - -Run `denote-after-rename-file-hook' as a final step." - (declare (interactive-only t)) - (interactive "P") - (if-let ((file (buffer-file-name)) - ((denote-file-is-note-p file)) - (file-type (denote-filetype-heuristics file))) - (when-let ((cur-keywords (denote-retrieve-front-matter-keywords-value file file-type)) - (del-keyword (denote--keywords-delete-prompt cur-keywords))) - (denote-rewrite-keywords - file - (seq-difference cur-keywords del-keyword) - file-type) - (denote-rename-file-using-front-matter file :no-confirm (or denote-rename-no-confirm save-buffer)) - (run-hooks 'denote-after-rename-file-hook)) - (user-error "Buffer not visiting a Denote file"))) - -(defalias 'denote-rename-remove-keywords 'denote-keywords-remove - "Alias for `denote-keywords-remove'.") - -;;;;;; Interactively add or remove file name signature - -;;;###autoload -(defun denote-rename-add-signature (file signature) - "Add to FILE name the SIGNATURE. -In interactive use, prompt for FILE, defaulting either to the current -buffer's file or the one at point in a Dired buffer. Also prompt for -SIGNATURE, using the existing one, if any, as the initial value. - -When called from Lisp, FILE is a string pointing to a file system path -and SIGNATURE is a string. - -Ask for confirmation before renaming the file to include the new -signature. Do it unless the user option `denote-rename-no-confirm' is -set to a non-nil value. - -Once the operation is done, reload any Dired buffers and run the -`denote-after-rename-file-hook'. - -Also see `denote-rename-remove-signature'." - (interactive - (let* ((file (denote--rename-dired-file-or-prompt)) - (file-in-prompt (propertize (file-relative-name file) 'face 'denote-faces-prompt-current-name))) - (list - file - (denote-signature-prompt - (or (denote-retrieve-filename-signature file) "") - (format "Rename `%s' with SIGNATURE (empty to remove)" file-in-prompt))))) - (let* ((type (denote-filetype-heuristics file)) - (title (denote-retrieve-title-or-filename file type)) - (keywords-string (denote-retrieve-filename-keywords file)) - (keywords (when keywords-string (split-string keywords-string "_" :omit-nulls "_"))) - (dir (file-name-directory file)) - (id (or (denote-retrieve-filename-identifier file) - (denote-create-unique-file-identifier file (denote--get-all-used-ids)))) - (extension (denote-get-file-extension file)) - (new-name (denote-format-file-name dir id keywords title extension signature))) - (when (or denote-rename-no-confirm (denote-rename-file-prompt file new-name)) - (denote-rename-file-and-buffer file new-name) - (denote-update-dired-buffers) - (run-hooks 'denote-after-rename-file-hook)))) - -;;;###autoload -(defun denote-rename-remove-signature (file) - "Remove the signature of FILE. -In interactive use, prompt for FILE, defaulting either to the current -buffer's file or the one at point in a Dired buffer. When called from -Lisp, FILE is a string pointing to a file system path. - -Ask for confirmation before renaming the file to remove its signature. -Do it unless the user option `denote-rename-no-confirm' is set to a -non-nil value. - -Once the operation is done, reload any Dired buffers and run the -`denote-after-rename-file-hook'. - -Also see `denote-rename-add-signature'." - (interactive (list (denote--rename-dired-file-or-prompt))) - (when (denote-retrieve-filename-signature file) - (let* ((type (denote-filetype-heuristics file)) - (title (denote-retrieve-title-or-filename file type)) - (keywords-string (denote-retrieve-filename-keywords file)) - (keywords (when keywords-string (split-string keywords-string "_" :omit-nulls "_"))) - (dir (file-name-directory file)) - (id (or (denote-retrieve-filename-identifier file) - (denote-create-unique-file-identifier file (denote--get-all-used-ids)))) - (extension (denote-get-file-extension file)) - (new-name (denote-format-file-name dir id keywords title extension nil))) - (when (or denote-rename-no-confirm (denote-rename-file-prompt file new-name)) - (denote-rename-file-and-buffer file new-name) - (denote-update-dired-buffers) - (run-hooks 'denote-after-rename-file-hook))))) - -;;;;; Creation of front matter - -;;;###autoload -(defun denote-add-front-matter (file title keywords) - "Insert front matter at the top of FILE. - -When called interactively, FILE is the return value of the -function `buffer-file-name'. FILE is checked to determine -whether it is a note for Denote's purposes. - -TITLE is a string. Interactively, it is the user input at the -minibuffer prompt. - -KEYWORDS is a list of strings. Interactively, it is the user -input at the minibuffer prompt. This one supports completion for -multiple entries, each separated by the `crm-separator' (normally -a comma). - -The purpose of this command is to help the user generate new -front matter for an existing note (perhaps because the user -deleted the previous one and could not undo the change). - -This command does not rename the file (e.g. to update the -keywords). To rename a file by reading its front matter as -input, use `denote-rename-file-using-front-matter'. - -Note that this command is useful only for existing Denote notes. -If the user needs to convert a generic text file to a Denote -note, they can use one of the command which first rename the file -to make it comply with our file-naming scheme and then add the -relevant front matter. - -[ NOTE: Please check with your minibuffer user interface how to - provide an empty input. The Emacs default setup accepts the - empty minibuffer contents as they are, though popular packages - like `vertico' use the first available completion candidate - instead. For `vertico', the user must either move one up to - select the prompt and then type RET there with empty contents, - or use the command `vertico-exit-input' with empty contents. - That Vertico command is bound to M-RET as of this writing on - 2024-02-29 09:24 +0200. ]" - (interactive - (list - (buffer-file-name) - (denote-title-prompt nil "Add TITLE (empty to ignore)") - (denote-keywords-sort (denote-keywords-prompt "Add KEYWORDS (empty to ignore)")))) - (when-let ((denote-file-is-writable-and-supported-p file) - (id (denote-retrieve-filename-identifier file)) - (file-type (denote-filetype-heuristics file))) - (denote--add-front-matter file title keywords id file-type))) - -(define-obsolete-function-alias - 'denote-change-file-type - 'denote-change-file-type-and-front-matter - "2.1.0") - -;;;###autoload -(defun denote-change-file-type-and-front-matter (file new-file-type) - "Change file type of FILE and add an appropriate front matter. - -If in Dired, consider FILE to be the one at point, else prompt -with minibuffer completion for one. - -Add a front matter in the format of the NEW-FILE-TYPE at the -beginning of the file. - -Retrieve the title of FILE from a line starting with a title -field in its front matter, depending on the previous file -type (e.g. #+title for Org). The same process applies for -keywords. - -As a final step, ask for confirmation, showing the difference -between old and new file names. - -Important note: No attempt is made to modify any other elements -of the file. This needs to be done manually." - (interactive - (list - (denote--rename-dired-file-or-prompt) - (denote--valid-file-type (or (denote-file-type-prompt) denote-file-type)))) - (let* ((dir (file-name-directory file)) - (old-file-type (denote-filetype-heuristics file)) - (id (or (denote-retrieve-filename-identifier file) "")) - (title (denote-retrieve-title-or-filename file old-file-type)) - (keywords (denote-retrieve-front-matter-keywords-value file old-file-type)) - (signature (or (denote-retrieve-filename-signature file) "")) - (old-extension (denote-get-file-extension file)) - (new-extension (denote--file-extension new-file-type)) - (new-name (denote-format-file-name dir id keywords title new-extension signature)) - (max-mini-window-height denote-rename-max-mini-window-height)) - (when (and (not (eq old-extension new-extension)) - (denote-rename-file-prompt file new-name)) - (denote-rename-file-and-buffer file new-name) - (denote-update-dired-buffers) - (when (denote-file-is-writable-and-supported-p new-name) - (denote--add-front-matter new-name title keywords id new-file-type))))) - -;;;; The Denote faces - -(defgroup denote-faces () - "Faces for Denote." - :group 'denote) - -(defface denote-faces-link '((t :inherit link)) - "Face used to style Denote links in the buffer." - :group 'denote-faces - :package-version '(denote . "0.5.0")) - -(defface denote-faces-subdirectory '((t :inherit bold)) - "Face for subdirectory of file name. -This should only ever needed in the backlinks' buffer (or -equivalent), not in Dired." - :group 'denote-faces - :package-version '(denote . "0.2.0")) - -(defface denote-faces-date '((t :inherit font-lock-variable-name-face)) - "Face for file name date in Dired buffers. -This is the part of the identifier that covers the year, month, -and day." - :group 'denote-faces - :package-version '(denote . "0.1.0")) - -(defface denote-faces-time '((t :inherit denote-faces-date)) - "Face for file name time in Dired buffers. -This is the part of the identifier that covers the hours, minutes, -and seconds." - :group 'denote-faces - :package-version '(denote . "0.1.0")) - -(defface denote-faces-title nil - "Face for file name title in Dired buffers." - :group 'denote-faces - :package-version '(denote . "0.1.0")) - -(defface denote-faces-year '((t :inherit denote-faces-date)) - "Face for file name year in Dired buffers. -This is the part of the identifier that covers the year, month, and day." - :group 'denote-faces - :package-version '(denote . "2.3.0")) - -(defface denote-faces-month '((t :inherit denote-faces-date)) - "Face for file name month in Dired buffers. -This is the part of the identifier that covers the year, month, and day." - :group 'denote-faces - :package-version '(denote . "2.3.0")) - -(defface denote-faces-day '((t :inherit denote-faces-date)) - "Face for file name day in Dired buffers. -This is the part of the identifier that covers the year, month, and day." - :group 'denote-faces - :package-version '(denote . "2.3.0")) - -(defface denote-faces-hour '((t :inherit denote-faces-date)) - "Face for file name hours in Dired buffers. -This is the part of the identifier that covers the hours, minutes, -and seconds." - :group 'denote-faces - :package-version '(denote . "2.3.0")) - -(defface denote-faces-minute '((t :inherit denote-faces-date)) - "Face for file name minutes in Dired buffers. -This is the part of the identifier that covers the hours, minutes, -and seconds." - :group 'denote-faces - :package-version '(denote . "2.3.0")) - -(defface denote-faces-second '((t :inherit denote-faces-date)) - "Face for file name seconds in Dired buffers. -This is the part of the identifier that covers the hours, minutes, -and seconds." - :group 'denote-faces - :package-version '(denote . "2.3.0")) - -(defface denote-faces-extension '((t :inherit shadow)) - "Face for file extension type in Dired buffers." - :group 'denote-faces - :package-version '(denote . "0.1.0")) - -(defface denote-faces-keywords '((t :inherit font-lock-builtin-face)) - "Face for file name keywords in Dired buffers." - :group 'denote-faces - :package-version '(denote . "0.1.0")) - -(defface denote-faces-signature '((t :inherit font-lock-warning-face)) - "Face for file name signature in Dired buffers." - :group 'denote-faces - :package-version '(denote . "2.0.0")) - -(defface denote-faces-delimiter - '((((class color) (min-colors 88) (background light)) - :foreground "gray70") - (((class color) (min-colors 88) (background dark)) - :foreground "gray30") - (t :inherit shadow)) - "Face for file name delimiters in Dired buffers." - :group 'denote-faces - :package-version '(denote . "0.1.0")) - -(defface denote-faces-time-delimiter '((t :inherit shadow)) - "Face for the delimiter between date and time in Dired buffers." - :group 'denote-faces - :package-version '(denote . "2.1.0")) - -(defvar denote-faces--file-name-regexp - (concat "\\(?11:[\t\s]+\\|.*/\\)?" - "\\(?1:[0-9]\\{4\\}\\)\\(?12:[0-9]\\{2\\}\\)\\(?13:[0-9]\\{2\\}\\)" - "\\(?10:T\\)" - "\\(?2:[0-9]\\{2\\}\\)\\(?14:[0-9]\\{2\\}\\)\\(?15:[0-9]\\{2\\}\\)" - "\\(?:\\(?3:==\\)\\(?4:[^.]*?\\)\\)?" - "\\(?:\\(?5:--\\)\\(?6:[^.]*?\\)\\)?" - "\\(?:\\(?7:__\\)\\(?8:[^.]*?\\)\\)?" - "\\(?9:\\..*\\)?$") - "Regexp of file names for fontification.") - -(defconst denote-faces-file-name-keywords - `((,denote-faces--file-name-regexp - (11 'denote-faces-subdirectory nil t) - (1 'denote-faces-year nil t) - (12 'denote-faces-month nil t) - (13 'denote-faces-day nil t) - (10 'denote-faces-time-delimiter nil t) - (2 'denote-faces-hour nil t) - (14 'denote-faces-minute nil t) - (15 'denote-faces-second nil t) - (3 'denote-faces-delimiter nil t) - (4 'denote-faces-signature nil t) - (5 'denote-faces-delimiter nil t) - (6 'denote-faces-title nil t) - (7 'denote-faces-delimiter nil t) - (8 'denote-faces-keywords nil t) - (9 'denote-faces-extension nil t ))) - "Keywords for fontification of file names.") - -(make-obsolete-variable 'denote-faces-file-name-keywords-for-backlinks nil "2.2.0") - -(defface denote-faces-prompt-old-name '((t :inherit error)) - "Face for the old name shown in the prompt of `denote-rename-file' etc." - :group 'denote-faces - :package-version '(denote . "2.2.0")) - -(defface denote-faces-prompt-new-name '((t :inherit success)) - "Face for the new name shown in the prompt of `denote-rename-file' etc." - :group 'denote-faces - :package-version '(denote . "2.2.0")) - -(defface denote-faces-prompt-current-name '((t :inherit denote-faces-prompt-old-name)) - "Face for the current file shown in the prompt of `denote-rename-file' etc." - :group 'denote-faces - :package-version '(denote . "2.2.0")) - -;;;; Fontification in Dired - -(defgroup denote-dired () - "Integration between Denote and Dired." - :group 'denote) - -(defcustom denote-dired-directories (list denote-directory) - "List of directories where `denote-dired-mode' should apply to. -For this to take effect, add `denote-dired-mode-in-directories', -to the `dired-mode-hook'. - -If `denote-dired-directories-include-subdirectories' is non-nil, -also apply the effect to all subdirectories of those specified in -the list." - :type '(repeat directory) - :package-version '(denote . "0.1.0") - :link '(info-link "(denote) Fontification in Dired") - :group 'denote-dired) - -(defcustom denote-dired-directories-include-subdirectories nil - "If non-nil `denote-dired-directories' also affects all subdirectories. -Otherwise `denote-dired-directories' works only with exact matches." - :package-version '(denote . "2.2.0") - :link '(info-link "(denote) Fontification in Dired") - :type 'boolean - :group 'denote-dired) - -;; FIXME 2022-08-12: Make `denote-dired-mode' work with diredfl. This -;; may prove challenging. - -(defun denote-dired-add-font-lock (&rest _) - "Append `denote-faces-file-name-keywords' to font lock keywords." - ;; NOTE 2023-10-28: I tried to add the first argument and then - ;; experimented with various combinations of keywords, such as - ;; `(,@dired-font-lock-keywords ,@denote-faces-file-name-keywords). - ;; None of them could be unset upon disabling `denote-dired-mode'. - ;; As such, I am using the `when' here. - (when (derived-mode-p 'dired-mode) - (font-lock-add-keywords nil denote-faces-file-name-keywords t))) - -(defun denote-dired-remove-font-lock (&rest _) - "Remove `denote-faces-file-name-keywords' from font lock keywords." - ;; See NOTE in `denote-dired-add-font-lock'. - (when (derived-mode-p 'dired-mode) - (font-lock-remove-keywords nil denote-faces-file-name-keywords))) - -(declare-function wdired-change-to-wdired-mode "wdired") -(declare-function wdired-finish-edit "wdired") - -;;;###autoload -(define-minor-mode denote-dired-mode - "Fontify all Denote-style file names. -Add this or `denote-dired-mode-in-directories' to -`dired-mode-hook'." - :global nil - :group 'denote-dired - (if denote-dired-mode - (progn - (denote-dired-add-font-lock) - (advice-add #'wdired-change-to-wdired-mode :after #'denote-dired-add-font-lock) - (advice-add #'wdired-finish-edit :after #'denote-dired-add-font-lock)) - (denote-dired-remove-font-lock) - (advice-remove #'wdired-change-to-wdired-mode #'denote-dired-add-font-lock) - (advice-remove #'wdired-finish-edit #'denote-dired-add-font-lock)) - (font-lock-flush (point-min) (point-max))) - -(defun denote-dired--modes-dirs-as-dirs () - "Return `denote-dired-directories' as directories. -The intent is to basically make sure that however a path is -written, it is always returned as a directory." - (mapcar - (lambda (dir) - (file-name-as-directory (file-truename dir))) - denote-dired-directories)) - -;;;###autoload -(defun denote-dired-mode-in-directories () - "Enable `denote-dired-mode' in `denote-dired-directories'. -Add this function to `dired-mode-hook'. - -If `denote-dired-directories-include-subdirectories' is non-nil, -also enable it in all subdirectories." - (when-let ((dirs (denote-dired--modes-dirs-as-dirs)) - ;; Also include subdirs - ((or (member (file-truename default-directory) dirs) - (and denote-dired-directories-include-subdirectories - (seq-some - (lambda (dir) - (string-prefix-p dir (file-truename default-directory))) - dirs))))) - (denote-dired-mode 1))) - -;;;; The linking facility - -(defgroup denote-link () - "Link facility for Denote." - :group 'denote) - -;;;;; User options - -(defcustom denote-link-backlinks-display-buffer-action - '((display-buffer-reuse-window display-buffer-below-selected) - (window-height . fit-window-to-buffer)) - "The action used to display the current file's backlinks buffer. - -The value has the form (FUNCTION . ALIST), where FUNCTION is -either an \"action function\", a list thereof, or possibly an -empty list. ALIST is a list of \"action alist\" which may be -omitted (or be empty). - -Sample configuration to display the buffer in a side window on -the left of the Emacs frame: - - (setq denote-link-backlinks-display-buffer-action - (quote ((display-buffer-reuse-window - display-buffer-in-side-window) - (side . left) - (slot . 99) - (window-width . 0.3)))) - -See Info node `(elisp) Displaying Buffers' for more details -and/or the documentation string of `display-buffer'." - :type '(cons (choice (function :tag "Display Function") - (repeat :tag "Display Functions" function)) - alist) - :package-version '(denote . "0.1.0") - :group 'denote-link) - -;;;;; Link to note - -(defvar denote-org-link-format "[[denote:%s][%s]]" - "Format of Org link to note. -The value is passed to `format' with IDENTIFIER and TITLE -arguments, in this order. - -Also see `denote-org-link-in-context-regexp'.") - -(defvar denote-md-link-format "[%2$s](denote:%1$s)" - "Format of Markdown link to note. -The %N$s notation used in the default value is for `format' as -the supplied arguments are IDENTIFIER and TITLE, in this order. - -Also see `denote-md-link-in-context-regexp'.") - -(defvar denote-id-only-link-format "[[denote:%s]]" - "Format of identifier-only link to note. -The value is passed to `format' with IDENTIFIER as its sole -argument. - -Also see `denote-id-only-link-in-context-regexp'.") - -(defvar denote-org-link-in-context-regexp - (concat "\\[\\[" "denote:" "\\(?1:" denote-id-regexp "\\)" "]" "\\[.*?]]") - "Regexp to match an Org link in its context. -The format of such links is `denote-org-link-format'.") - -(defvar denote-md-link-in-context-regexp - (concat "\\[.*?]" "(denote:" "\\(?1:" denote-id-regexp "\\)" ")") - "Regexp to match a Markdown link in its context. -The format of such links is `denote-md-link-format'.") - -(defvar denote-id-only-link-in-context-regexp - (concat "\\[\\[" "denote:" "\\(?1:" denote-id-regexp "\\)" "]]") - "Regexp to match an identifier-only link in its context. -The format of such links is `denote-id-only-link-format'." ) - -(defun denote-format-link (file description file-type id-only) - "Prepare link to FILE using DESCRIPTION. - -FILE-TYPE and ID-ONLY are used to get the format of the link. -See the `:link' property of `denote-file-types'." - (format - (if (or id-only (null description) (string-empty-p description)) - denote-id-only-link-format - (denote--link-format file-type)) - (denote-retrieve-filename-identifier file) - description)) - -(make-obsolete 'denote-link--format-link 'denote-format-link "2.1.0") -(make-obsolete 'denote-link-signature-format nil "2.3.0") - -(defun denote--link-get-description (file) - "Return link description for FILE." - (funcall - (or denote-link-description-function #'denote-link-description-with-signature-and-title) - file)) - -(defun denote-link-description-with-signature-and-title (file) - "Return link description for FILE. - -- If the region is active, use it as the description. - -- If FILE as a signature, then format the description as a sequence of - the signature text and the title with two spaces between them. - -- If FILE does not have a signature, then use its title as the - description. - -This is useful as the value of the user option -`denote-link-description-function'." - (let* ((file-type (denote-filetype-heuristics file)) - (signature (denote-retrieve-filename-signature file)) - (title (denote-retrieve-title-or-filename file file-type)) - (region-text (denote--get-active-region-content))) - (cond - (region-text region-text) - ((and signature title) (format "%s %s" signature title)) - (t title)))) - -(defun denote--get-active-region-content () - "Return the text of the active region, else nil." - (when-let (((region-active-p)) - (beg (region-beginning)) - (end (region-end))) - (string-trim (buffer-substring-no-properties beg end)))) - -(defun denote--delete-active-region-content () - "Delete the content of the active region, if any." - (when-let (((region-active-p)) - (beg (region-beginning)) - (end (region-end))) - (delete-region beg end))) - -;;;###autoload -(defun denote-link (file file-type description &optional id-only) - "Create link to FILE note in variable `denote-directory' with DESCRIPTION. - -When called interactively, prompt for FILE using completion. In -this case, derive FILE-TYPE from the current buffer. - -The DESCRIPTION is returned by the function specified in variable -`denote-link-description-function'. If the region is active, its -content is deleted and can be used as the description of the -link. The default value of `denote-link-description-function' -returns the content of the active region, if any, else the title -of the linked file is used as the description. The title comes -either from the front matter or the file name. Note that if you -change the default value of `denote-link-description-function', -make sure to use the `region-text' parameter. Regardless of the -value of this user option, `denote-link' will always replace the -content of the active region. - -With optional ID-ONLY as a non-nil argument, such as with a -universal prefix (\\[universal-argument]), insert links with just -the identifier and no further description. In this case, the -link format is always [[denote:IDENTIFIER]]. If the DESCRIPTION -is empty, the link is also as if ID-ONLY were non-nil. The -default value of `denote-link-description-function' returns an -empty string when the region is empty. Thus, the link will have -no description in this case. - -When called from Lisp, FILE is a string representing a full file -system path. FILE-TYPE is a symbol as described in -`denote-file-type'. DESCRIPTION is a string. Whether the caller -treats the active region specially, is up to it." - (interactive - (let* ((file (denote-file-prompt nil "Link to FILE")) - (file-type (denote-filetype-heuristics buffer-file-name)) - (description (when (file-exists-p file) - (denote--link-get-description file)))) - (list file file-type description current-prefix-arg))) - (unless (or (denote--file-type-org-capture-p) - (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name))) - (user-error "The current file type is not recognized by Denote")) - (unless (file-exists-p file) - (user-error "The linked file does not exist")) - (let* ((beg (point))) - (denote--delete-active-region-content) - (insert (denote-format-link file description file-type id-only)) - (unless (derived-mode-p 'org-mode) - (make-button beg (point) 'type 'denote-link-button)))) - -(define-obsolete-function-alias - 'denote-link-insert-link - 'denote-insert-link - "2.0.0") - -(defalias 'denote-insert-link 'denote-link - "Alias for `denote-link' command.") - -;;;###autoload -(defun denote-link-with-signature () - "Insert link to file with signature. -Prompt for file using minibuffer completion, limiting the list of -candidates to files with a signature in their file name. - -By default, the description of the link includes the signature, -if present, followed by the file's title, if any. - -For more advanced uses with Lisp, refer to the `denote-link' -function." - (declare (interactive-only t)) - (interactive) - (unless (or (denote--file-type-org-capture-p) - (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name))) - (user-error "The current file type is not recognized by Denote")) - (let* ((file (denote-file-prompt "=")) - (type (denote-filetype-heuristics (buffer-file-name))) - (description (denote--link-get-description file))) - (denote-link file type description))) - -(defun denote-link--collect-identifiers (regexp) - "Return collection of identifiers in buffer matching REGEXP." - (let (matches) - (save-excursion - (goto-char (point-min)) - (while (or (re-search-forward regexp nil t) - (re-search-forward denote-id-only-link-in-context-regexp nil t)) - (push (match-string-no-properties 1) matches))) - matches)) - -(defun denote-link--expand-identifiers (regexp) - "Expend identifiers matching REGEXP into file paths." - (let ((files (denote-directory-files)) - found-files) - (dolist (file files) - (dolist (i (denote-link--collect-identifiers regexp)) - (when (string-prefix-p i (file-name-nondirectory file)) - (push file found-files)))) - found-files)) - -(defvar denote-link-find-file-history nil - "History for `denote-find-link'.") - -(defalias 'denote-link--find-file-history 'denote-link-find-file-history - "Compatibility alias for `denote-link-find-file-history'.") - -(defun denote-link--find-file-prompt (files) - "Prompt for linked file among FILES." - (let ((file-names (mapcar #'denote-get-file-name-relative-to-denote-directory - files))) - (completing-read - "Find linked file: " - (denote--completion-table 'file file-names) - nil t nil 'denote-link-find-file-history))) - -(defun denote-link-return-links (&optional file) - "Return list of links in current or optional FILE. -Also see `denote-link-return-backlinks'." - (when-let ((current-file (or file (buffer-file-name))) - ((denote-file-has-supported-extension-p current-file)) - (file-type (denote-filetype-heuristics current-file)) - (regexp (denote--link-in-context-regexp file-type))) - (with-temp-buffer - (insert-file-contents current-file) - (denote-link--expand-identifiers regexp)))) - -(defalias 'denote-link-return-forelinks 'denote-link-return-links - "Alias for `denote-link-return-links'.") - -(define-obsolete-function-alias - 'denote-link-find-file - 'denote-find-link - "2.0.0") - -;;;###autoload -(defun denote-find-link () - "Use minibuffer completion to visit linked file." - (declare (interactive-only t)) - (interactive) - (find-file - (concat - (denote-directory) - (denote-link--find-file-prompt - (or (denote-link-return-links) - (user-error "No links found")))))) - -(defun denote-link-return-backlinks (&optional file) - "Return list of backlinks in current or optional FILE. -Also see `denote-link-return-links'." - (when-let ((current-file (or file (buffer-file-name))) - (id (denote-retrieve-filename-identifier-with-error current-file))) - (delete current-file (denote--retrieve-files-in-xrefs id)))) - -(define-obsolete-function-alias - 'denote-link-find-backlink - 'denote-find-backlink - "2.0.0") - -;;;###autoload -(defun denote-find-backlink () - "Use minibuffer completion to visit backlink to current file. - -Like `denote-find-link', but select backlink to follow." - (declare (interactive-only t)) - (interactive) - (find-file - (denote-get-path-by-id - (denote-extract-id-from-string - (denote-link--find-file-prompt - (or (denote-link-return-backlinks) - (user-error "No backlinks found"))))))) - -;;;###autoload -(defun denote-link-after-creating (&optional id-only) - "Create new note in the background and link to it directly. - -Use `denote' interactively to produce the new note. Its doc -string explains which prompts will be used and under what -conditions. - -With optional ID-ONLY as a prefix argument create a link that -consists of just the identifier. Else try to also include the -file's title. This has the same meaning as in `denote-link'. - -For a variant of this, see `denote-link-after-creating-with-command'. - -IMPORTANT NOTE: Normally, `denote' does not save the buffer it -produces for the new note. This is a safety precaution to not -write to disk unless the user wants it (e.g. the user may choose -to kill the buffer, thus cancelling the creation of the note). -However, for this command the creation of the note happens in the -background and the user may miss the step of saving their buffer. -We thus have to save the buffer in order to (i) establish valid -links, and (ii) retrieve whatever front matter from the target -file. Though see `denote-save-buffer-after-creation'." - (interactive "P") - (unless (or (denote--file-type-org-capture-p) - (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name))) - (user-error "The current file type is not recognized by Denote")) - (let* ((type (denote-filetype-heuristics (buffer-file-name))) - (path (denote--command-with-features #'denote nil nil :save :in-background)) - (description (denote--link-get-description path))) - (denote-link path type description id-only))) - -;;;###autoload -(defun denote-link-after-creating-with-command (command &optional id-only) - "Like `denote-link-after-creating' but prompt for note-making COMMAND. -Use this to, for example, call `denote-signature' so that the -newly created note has a signature as part of its file name. - -Optional ID-ONLY has the same meaning as in the command -`denote-link-after-creating'." - (interactive - (list - (denote-command-prompt) - current-prefix-arg)) - (unless (or (denote--file-type-org-capture-p) - (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name))) - (user-error "The current file type is not recognized by Denote")) - (let* ((type (denote-filetype-heuristics (buffer-file-name))) - (path (denote--command-with-features command nil nil :save :in-background)) - (description (denote--link-get-description path))) - (denote-link path type description id-only))) - -;;;###autoload -(defun denote-link-or-create (target &optional id-only) - "Use `denote-link' on TARGET file, creating it if necessary. - -If TARGET file does not exist, call `denote-link-after-creating' -which runs the `denote' command interactively to create the file. -The established link will then be targeting that new file. - -If TARGET file does not exist, add the user input that was used -to search for it to the minibuffer history of the -`denote-file-prompt'. The user can then retrieve and possibly -further edit their last input, using it as the newly created -note's actual title. At the `denote-file-prompt' type -\\\\[previous-history-element]. - -With optional ID-ONLY as a prefix argument create a link that -consists of just the identifier. Else try to also include the -file's title. This has the same meaning as in `denote-link'." - (interactive - (let* ((target (denote-file-prompt))) - (unless (and target (file-exists-p target)) - (setq target (denote--command-with-features #'denote :use-file-prompt-as-def-title :ignore-region :save :in-background))) - (list target current-prefix-arg))) - (unless (or (denote--file-type-org-capture-p) - (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name))) - (user-error "The current file type is not recognized by Denote")) - (denote-link target - (denote-filetype-heuristics (buffer-file-name)) - (denote--link-get-description target) - id-only)) - -(defalias 'denote-link-to-existing-or-new-note 'denote-link-or-create - "Alias for `denote-link-or-create' command.") - -;;;;; Link buttons - -;; Evaluate: (info "(elisp) Button Properties") -;; -;; Button can provide a help-echo function as well, but I think we might -;; not need it. -(define-button-type 'denote-link-button - 'follow-link t - 'face 'denote-faces-link - 'action #'denote-link--find-file-at-button) - -(autoload 'thing-at-point-looking-at "thingatpt") - -(defun denote-link--link-at-point-string () - "Return identifier at point." - (when (or (thing-at-point-looking-at denote-id-only-link-in-context-regexp) - (thing-at-point-looking-at denote-md-link-in-context-regexp) - (thing-at-point-looking-at denote-org-link-in-context-regexp) - ;; Meant to handle the case where a link is broken by - ;; `fill-paragraph' into two lines, in which case it - ;; buttonizes only the "denote:ID" part. Example: - ;; - ;; [[denote:20220619T175212][This is a - ;; test]] - ;; - ;; Maybe there is a better way? - (thing-at-point-looking-at "\\[\\(denote:.*\\)]")) - (match-string-no-properties 0))) - -;; NOTE 2022-06-15: I add this as a variable for advanced users who may -;; prefer something else. If there is demand for it, we can make it a -;; defcustom, but I think it would be premature at this stage. -(defvar denote-link-button-action #'find-file-other-window - "Display buffer action for Denote buttons.") - -(defun denote-link--find-file-at-button (button) - "Visit file referenced by BUTTON." - (let* ((id (denote-extract-id-from-string - (buffer-substring-no-properties - (button-start button) - (button-end button)))) - (file (denote-get-path-by-id id))) - (funcall denote-link-button-action file))) - -;;;###autoload -(defun denote-link-buttonize-buffer (&optional beg end) - "Make denote: links actionable buttons in the current buffer. - -Buttonization applies to the plain text and Markdown file types, -per the user option `denote-file-types'. It will not do anything -in `org-mode' buffers, as buttons already work there. If you do -not use Markdown or plain text, then you do not need this. - -Links work when they point to a file inside the variable -`denote-directory'. - -To buttonize links automatically add this function to the -`find-file-hook'. Or call it interactively for on-demand -buttonization. - -When called from Lisp, with optional BEG and END as buffer -positions, limit the process to the region in-between." - (interactive) - (when (and (not (derived-mode-p 'org-mode)) - buffer-file-name - (denote-file-has-identifier-p buffer-file-name)) - (save-excursion - (goto-char (or beg (point-min))) - (while (re-search-forward denote-id-regexp end t) - (when-let ((string (denote-link--link-at-point-string)) - (beg (match-beginning 0)) - (end (match-end 0))) - (make-button beg end 'type 'denote-link-button)))))) - -(defun denote-link-markdown-follow (link) - "Function to open Denote file present in LINK. -To be assigned to `markdown-follow-link-functions'." - (when (ignore-errors (string-match denote-id-regexp link)) - (funcall denote-link-button-action - (denote-get-path-by-id (match-string 0 link))))) - -(eval-after-load 'markdown-mode - '(add-hook 'markdown-follow-link-functions #'denote-link-markdown-follow)) - -;;;;; Backlinks' buffer - -(define-button-type 'denote-link-backlink-button - 'follow-link t - 'action #'denote-link--backlink-find-file - 'face nil) ; we use this face though we style it later - -(defun denote-link--backlink-find-file (button) - "Action for BUTTON to `find-file'." - (funcall denote-link-button-action (buffer-substring (button-start button) (button-end button)))) - -(defun denote-link--display-buffer (buf) - "Run `display-buffer' on BUF. -Expand `denote-link-backlinks-display-buffer-action'." - (display-buffer - buf - `(,@denote-link-backlinks-display-buffer-action))) - -(define-obsolete-function-alias - 'denote-backlinks-next - 'denote-backlinks-mode-next - "2.3.0") - -(defun denote-backlinks-mode-next (n) - "Use appropriate command for forward motion in backlinks buffer. -With N as a numeric argument, move to the Nth button from point. -A nil value of N is understood as 1. - -When `denote-backlinks-show-context' is nil, move between files -in the backlinks buffer. - -When `denote-backlinks-show-context' is non-nil move between -matching identifiers." - (interactive "p" denote-backlinks-mode) - (unless (derived-mode-p 'denote-backlinks-mode) - (user-error "Only use this in a Denote backlinks buffer")) - (if denote-backlinks-show-context - (xref-next-line) - (forward-button n))) - -(define-obsolete-function-alias - 'denote-backlinks-prev - 'denote-backlinks-mode-previous - "2.3.0") - -(defun denote-backlinks-mode-previous (n) - "Use appropriate command for backward motion in backlinks buffer. -With N as a numeric argument, move to the Nth button from point. -A nil value of N is understood as 1. - -When `denote-backlinks-show-context' is nil, move between files -in the backlinks buffer. - -When `denote-backlinks-show-context' is non-nil move between -matching identifiers." - (interactive "p" denote-backlinks-mode) - (unless (derived-mode-p 'denote-backlinks-mode) - (user-error "Only use this in a Denote backlinks buffer")) - (if denote-backlinks-show-context - (xref-prev-line) - (backward-button n))) - -(defvar denote-backlinks-mode-map - (let ((m (make-sparse-keymap))) - (define-key m "n" #'denote-backlinks-mode-next) - (define-key m "p" #'denote-backlinks-mode-previous) - (define-key m "g" #'revert-buffer) - m) - "Keymap for `denote-backlinks-mode'.") - -(define-derived-mode denote-backlinks-mode xref--xref-buffer-mode "Backlinks" - :interactive nil - "Major mode for backlinks buffers." - (unless denote-backlinks-show-context - (font-lock-add-keywords nil denote-faces-file-name-keywords t))) - -(defun denote-link--prepare-backlinks (fetcher _alist) - "Create backlinks' buffer for the current note. -FETCHER is a function that fetches a list of xrefs. It is called -with `funcall' with no argument like `xref--fetcher'. - -In the case of `denote', `apply-partially' is used to create a -function that has already applied another function to multiple -arguments. - -ALIST is not used in favour of using -`denote-link-backlinks-display-buffer-action'." - (let* ((inhibit-read-only t) - (file (buffer-file-name)) - (file-type (denote-filetype-heuristics file)) - (id (denote-retrieve-filename-identifier-with-error file)) - (buf (format "*denote-backlinks to %s*" id)) - ;; We retrieve results in absolute form and change the absolute - ;; path to a relative path a few lines below. We could add a - ;; suitable function to project-find-functions and the results - ;; would be automatically in relative form, but eventually - ;; notes may not be all under a common directory (or project). - (xref-file-name-display 'abs) - (xref-alist (xref--analyze (funcall fetcher))) - (dir (denote-directory))) - ;; Change the GROUP of each item in xref-alist to a relative path - (mapc (lambda (x) - (setf (car x) (denote-get-file-name-relative-to-denote-directory (car x)))) - xref-alist) - (with-current-buffer (get-buffer-create buf) - (setq-local default-directory dir) - (erase-buffer) - (setq overlay-arrow-position nil) - (denote-backlinks-mode) - (goto-char (point-min)) - (when-let ((title (denote-retrieve-front-matter-title-value file file-type)) - (heading (format "Backlinks to %S (%s)" title id)) - (l (length heading))) - (insert (format "%s\n%s\n\n" heading (make-string l ?-)))) - (if denote-backlinks-show-context - (xref--insert-xrefs xref-alist) - (mapc (lambda (x) - (insert (car x)) - (make-button (line-beginning-position) (line-end-position) :type 'denote-link-backlink-button) - (newline)) - xref-alist)) - (goto-char (point-min)) - (setq-local revert-buffer-function - (lambda (_ignore-auto _noconfirm) - (when-let ((buffer-file-name file)) - (denote-link--prepare-backlinks - (apply-partially #'xref-matches-in-files id - (denote-directory-files nil :omit-current :text-only)) - nil))))) - (denote-link--display-buffer buf))) - -(define-obsolete-function-alias - 'denote-link-backlinks - 'denote-backlinks - "2.0.0") - -;;;###autoload -(defun denote-backlinks () - "Produce a buffer with backlinks to the current note. - -The backlinks' buffer shows the file name of the note linking to -the current note, as well as the context of each link. - -File names are fontified by Denote if the user option -`denote-link-fontify-backlinks' is non-nil. If this user option -is nil, the buffer is fontified by Xref. - -The placement of the backlinks' buffer is controlled by the user -option `denote-link-backlinks-display-buffer-action'. By -default, it will show up below the current window." - (interactive) - (let ((file (buffer-file-name))) - (when (denote-file-is-writable-and-supported-p file) - (let* ((id (denote-retrieve-filename-identifier-with-error file)) - (xref-show-xrefs-function #'denote-link--prepare-backlinks)) - (xref--show-xrefs - (apply-partially #'xref-matches-in-files id - (denote-directory-files nil :omit-current :text-only)) - nil))))) - -(define-obsolete-function-alias - 'denote-link-show-backlinks-buffer - 'denote-show-backlinks-buffer - "2.0.0") - -(defalias 'denote-show-backlinks-buffer 'denote-backlinks - "Alias for `denote-backlinks' command.") - -;;;;; Add links matching regexp - -(defvar denote-link--prepare-links-format "- %s\n" - "Format specifiers for `denote-link-add-links'.") - -;; NOTE 2022-06-16: There is no need to overwhelm the user with options, -;; though I expect someone to want to change the sort order. -(defvar denote-link-add-links-sort nil - "When t, add REVERSE to `sort-lines' of `denote-link-add-links'.") - -(defun denote-link--prepare-links (files current-file-type id-only &optional no-sort) - "Prepare links to FILES from CURRENT-FILE-TYPE. -When ID-ONLY is non-nil, use a generic link format. - -With optional NO-SORT do not try to sort the inserted lines. -Otherwise sort lines while accounting for `denote-link-add-links-sort'." - (with-temp-buffer - (mapc - (lambda (file) - (let ((description (denote--link-get-description file))) - (insert - (format - denote-link--prepare-links-format - (denote-format-link file description current-file-type id-only))))) - files) - (unless no-sort - (sort-lines denote-link-add-links-sort (point-min) (point-max))) - (buffer-string))) - -(define-obsolete-function-alias - 'denote-link-add-links - 'denote-add-links - "2.0.0") - -(defun denote-link--insert-links (files current-file-type &optional id-only no-sort) - "Insert at point a typographic list of links matching FILES. - -With CURRENT-FILE-TYPE as a symbol among those specified in -`denote-file-type' (or the `car' of each element in -`denote-file-types'), format the link accordingly. With a nil or -unknown non-nil value, default to the Org notation. - -With ID-ONLY as a non-nil value, produce links that consist only -of the identifier, thus deviating from CURRENT-FILE-TYPE. - -Optional NO-SORT is passed to `denote-link--prepare-links'." - (insert (denote-link--prepare-links files current-file-type id-only no-sort))) - -;;;###autoload -(defun denote-add-links (regexp &optional id-only) - "Insert links to all notes matching REGEXP. -Use this command to reference multiple files at once. -Particularly useful for the creation of metanotes (read the -manual for more on the matter). - -Optional ID-ONLY has the same meaning as in `denote-link': it -inserts links with just the identifier." - (interactive - (list - (denote-files-matching-regexp-prompt "Insert links matching REGEXP") - current-prefix-arg)) - (unless (or (denote--file-type-org-capture-p) - (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name))) - (user-error "The current file type is not recognized by Denote")) - (let ((file-type (denote-filetype-heuristics (buffer-file-name)))) - (if-let ((files (denote-directory-files regexp :omit-current)) - (beg (point))) - (progn - (denote-link--insert-links files file-type id-only) - (denote-link-buttonize-buffer beg (point))) - (message "No links matching `%s'" regexp)))) - -(defalias 'denote-link-insert-links-matching-regexp 'denote-add-links - "Alias for `denote-add-links' command.") - -(define-obsolete-function-alias - 'denote-link-add-missing-links - 'denote-add-missing-links - "2.0.0") - -(make-obsolete 'denote-add-missing-links nil "2.2.0") - -;;;;; Links from Dired marks - -;; NOTE 2022-07-21: I don't think we need a history for this one. -(defun denote-link--buffer-prompt (buffers) - "Select buffer from BUFFERS visiting Denote notes." - (let ((buffer-file-names (mapcar #'file-name-nondirectory - buffers))) - (completing-read - "Select note buffer: " - (denote--completion-table 'buffer buffer-file-names) - nil t))) - -(defun denote-link--map-over-notes () - "Return list of `denote-file-is-note-p' from Dired marked items." - (when (denote--dir-in-denote-directory-p default-directory) - (seq-filter #'denote-file-is-note-p (dired-get-marked-files)))) - -;;;###autoload -(defun denote-link-dired-marked-notes (files buffer &optional id-only) - "Insert Dired marked FILES as links in BUFFER. - -FILES are Denote notes, meaning that they have our file-naming -scheme, are writable/regular files, and use the appropriate file -type extension (per `denote-file-type'). Furthermore, the marked -files need to be inside the variable `denote-directory' or one of -its subdirectories. No other file is recognised (the list of -marked files ignores whatever does not count as a note for our -purposes). - -The BUFFER is one which visits a Denote note file. If there are -multiple buffers, prompt with completion for one among them. If -there isn't one, throw an error. - -With optional ID-ONLY as a prefix argument, insert links with -just the identifier (same principle as with `denote-link'). - -This command is meant to be used from a Dired buffer." - (interactive - (list - (denote-link--map-over-notes) - (let ((file-names (denote--buffer-file-names))) - (find-file - (cond - ((null file-names) - (user-error "No buffers visiting Denote notes")) - ((eq (length file-names) 1) - (car file-names)) - (t - (denote-link--buffer-prompt file-names))))) - current-prefix-arg) - dired-mode) - (if (null files) - (user-error "No note files to link to") - (when (y-or-n-p (format "Create links at point in %s?" buffer)) - (with-current-buffer buffer - (insert (denote-link--prepare-links - files - (denote-filetype-heuristics (buffer-file-name)) - id-only)) - (denote-link-buttonize-buffer))))) - -;;;;; Define menu - -(defvar denote--menu-contents - '("Denote" - ["Create a note" denote - :help "Create a new note in the `denote-directory'"] - ["Create a note with given file type" denote-type - :help "Create a new note with a given file type in the `denote-directory'"] - ["Create a note in subdirectory" denote-subdirectory - :help "Create a new note in a subdirectory of the `denote-directory'"] - ["Create a note with date" denote-date - :help "Create a new note with a given date in the `denote-directory'"] - ["Create a note with signature" denote-signature - :help "Create a new note with a given signature in the `denote-directory'"] - ["Open a note or create it if missing" denote-open-or-create - :help "Open an existing note in the `denote-directory' or create it if missing"] - ["Open a note or create it with the chosen command" denote-open-or-create-with-command - :help "Open an existing note or create it with the chosen command if missing"] - "---" - ["Rename a file" denote-rename-file - :help "Rename file interactively" - :enable (derived-mode-p 'dired-mode 'text-mode)] - ["Rename this file using its front matter" denote-rename-file-using-front-matter - :help "Rename the current file using its front matter as input" - :enable (derived-mode-p 'text-mode)] - ["Rename Dired marked files interactively" denote-dired-rename-files - :help "Rename marked files in Dired by prompting for all file name components" - :enable (derived-mode-p 'dired-mode)] - ["Rename Dired marked files with keywords" denote-dired-rename-marked-files-with-keywords - :help "Rename marked files in Dired by prompting for keywords" - :enable (derived-mode-p 'dired-mode)] - ["Rename Dired marked files using their front matter" denote-dired-rename-marked-files-using-front-matter - :help "Rename marked files in Dired using their front matter as input" - :enable (derived-mode-p 'dired-mode)] - "---" - ["Insert a link" denote-link - :help "Insert link to a file in the `denote-directory'" - :enable (derived-mode-p 'text-mode)] - ["Insert links with regexp" denote-add-links - :help "Insert links to files matching regexp in the `denote-directory'" - :enable (derived-mode-p 'text-mode)] - ["Insert Dired marked files as links" denote-link-dired-marked-notes - :help "Rename marked files in Dired as links in a Denote buffer" - :enable (derived-mode-p 'dired-mode)] - ["Show file backlinks" denote-backlinks - :help "Insert link to a file in the `denote-directory'" - :enable (derived-mode-p 'text-mode)] - ["Link to existing note or newly created one" denote-link-or-create - :help "Insert a link to an existing file, else create it and link to it" - :enable (derived-mode-p 'text-mode)] - ["Link to existing note or newly created one with the chosen command" denote-link-or-create-with-command - :help "Insert a link to an existing file, else create it with the given command and link to it" - :enable (derived-mode-p 'text-mode)] - ["Create note in the background and link to it directly" denote-link-after-creating - :help "Create new note and link to it from the current file" - :enable (derived-mode-p 'text-mode)] - ["Create note in the background with chosen command and link to it directly" denote-link-after-creating-with-command - :help "Create new note with the chosen command and link to it from the current file" - :enable (derived-mode-p 'text-mode)] - "---" - ["Highlight Dired file names" denote-dired-mode - :help "Apply colors to Denote file name components in Dired" - :enable (derived-mode-p 'dired-mode) - :style toggle - :selected (bound-and-true-p denote-dired-mode)]) - "Contents of the Denote menu.") - -(defun denote--menu-bar-enable () - "Enable Denote menu bar." - (define-key-after global-map [menu-bar denote] - (easy-menu-binding - (easy-menu-create-menu "Denote" denote--menu-contents) "Denote") - "Tools")) - -;; Enable Denote menu bar by default -(denote--menu-bar-enable) - -;;;###autoload -(define-minor-mode denote-menu-bar-mode "Show Denote menu bar." - :global t - :init-value t - (if denote-menu-bar-mode - (denote--menu-bar-enable) - (define-key global-map [menu-bar denote] nil))) - -(defun denote-context-menu (menu _click) - "Populate MENU with Denote commands at CLICK." - (define-key menu [denote-separator] menu-bar-separator) - (let ((easy-menu (make-sparse-keymap "Denote"))) - (easy-menu-define nil easy-menu nil - denote--menu-contents) - (dolist (item (reverse (lookup-key easy-menu [menu-bar]))) - (when (consp item) - (define-key menu (vector (car item)) (cdr item))))) - menu) - -;;;;; Register `denote:' custom Org hyperlink - -(declare-function org-link-open-as-file "ol" (path arg)) - -(defun denote-link--ol-resolve-link-to-target (link &optional full-data) - "Resolve LINK to target file, with or without additioanl search terms. -With optional FULL-DATA return a list in the form of (path id search)." - (let* ((search (and (string-match "::\\(.*\\)\\'" link) - (match-string 1 link))) - (id (if (and search (not (string-empty-p search))) - (substring link 0 (match-beginning 0)) - link)) - (path (denote-get-path-by-id id))) - (cond - (full-data - (list path id search)) - ((and search (not (string-empty-p search))) - (concat path "::" search)) - (path)))) - -;;;###autoload -(defun denote-link-ol-follow (link) - "Find file of type `denote:' matching LINK. -LINK is the identifier of the note, optionally followed by a -search option akin to that of standard Org `file:' link types. -Read Info node `(org) Search Options'. - -Uses the function `denote-directory' to establish the path to the -file." - (org-link-open-as-file - (denote-link--ol-resolve-link-to-target link) - nil)) - -;;;###autoload -(defun denote-link-ol-complete () - "Like `denote-link' but for Org integration. -This lets the user complete a link through the `org-insert-link' -interface by first selecting the `denote:' hyperlink type." - (if-let ((file (denote-file-prompt))) - (concat "denote:" (denote-retrieve-filename-identifier file)) - (user-error "No files in `denote-directory'"))) - -(declare-function org-link-store-props "ol.el" (&rest plist)) -(defvar org-store-link-plist) - -(declare-function org-entry-put "org" (pom property value)) -(declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) -(declare-function org-id-new "org-id" (&optional prefix)) - -(defun denote-link-ol-get-id () - "Get the CUSTOM_ID of the current entry. -If the entry already has a CUSTOM_ID, return it as-is, else -create a new one." - (let* ((pos (point)) - (id (org-entry-get pos "CUSTOM_ID"))) - (if (and id (stringp id) (string-match-p "\\S-" id)) - id - (setq id (org-id-new "h")) - (org-entry-put pos "CUSTOM_ID" id) - id))) - -(declare-function org-get-heading "org" (no-tags no-todo no-priority no-comment)) - -(defun denote-link-ol-get-heading () - "Get current Org heading text." - (org-get-heading :no-tags :no-todo :no-priority :no-comment)) - -(defun denote-link-format-heading-description (file-text heading-text) - "Return description for FILE-TEXT with HEADING-TEXT at the end." - (format "%s::%s" file-text heading-text)) - -;;;###autoload -(defun denote-link-ol-store () - "Handler for `org-store-link' adding support for denote: links. -Also see the user option `denote-org-store-link-to-heading'." - (when-let ((file (buffer-file-name)) - ((denote-file-is-note-p file)) - (file-id (denote-retrieve-filename-identifier file)) - (description (denote--link-get-description file))) - (let ((heading-links (and denote-org-store-link-to-heading (derived-mode-p 'org-mode)))) - (org-link-store-props - :type "denote" - :description (if heading-links - (denote-link-format-heading-description - description - (denote-link-ol-get-heading)) - description) - :link (if heading-links - (format "denote:%s::#%s" file-id (denote-link-ol-get-id)) - (concat "denote:" file-id))) - org-store-link-plist))) - -;;;###autoload -(defun denote-link-ol-export (link description format) - "Export a `denote:' link from Org files. -The LINK, DESCRIPTION, and FORMAT are handled by the export -backend." - (let* ((path-id (denote-link--ol-resolve-link-to-target link :full-data)) - (path (file-relative-name (nth 0 path-id))) - (id (nth 1 path-id)) - (search (nth 2 path-id)) - (anchor (file-name-sans-extension path)) - (desc (cond - (description) - (search (format "denote:%s::%s" id search)) - (t (concat "denote:" id))))) - (cond - ((eq format 'html) - (if search - (format "%s" anchor search desc) - (format "%s" anchor desc))) - ((eq format 'latex) (format "\\href{%s}{%s}" (replace-regexp-in-string "[\\{}$%&_#~^]" "\\\\\\&" path) desc)) - ((eq format 'texinfo) (format "@uref{%s,%s}" path desc)) - ((eq format 'ascii) (format "[%s] " desc path)) - ((eq format 'md) (format "[%s](%s)" desc path)) - (t path)))) - -;; The `eval-after-load' part with the quoted lambda is adapted from -;; Elfeed: . - -;;;###autoload -(eval-after-load 'org - `(funcall - ;; The extra quote below is necessary because uncompiled closures - ;; do not evaluate to themselves. The quote is harmless for - ;; byte-compiled function objects. - ',(lambda () - (with-no-warnings - (org-link-set-parameters - "denote" - :follow #'denote-link-ol-follow - :face 'denote-faces-link - :complete #'denote-link-ol-complete - :store #'denote-link-ol-store - :export #'denote-link-ol-export))))) - -;;;; Glue code for org-capture - -(defgroup denote-org-capture () - "Integration between Denote and Org Capture." - :group 'denote) - -(defcustom denote-org-capture-specifiers "%l\n%i\n%?" - "String with format specifiers for `org-capture-templates'. -Check that variable's documentation for the details. - -The string can include arbitrary text. It is appended to new -notes via the `denote-org-capture' function. Every new note has -the standard front matter we define." - :type 'string - :package-version '(denote . "0.1.0") - :group 'denote-org-capture) - -(defvar denote-last-path nil "Store last path.") - -;;;###autoload -(defun denote-org-capture () - "Create new note through `org-capture-templates'. -Use this as a function that returns the path to the new file. -The file is populated with Denote's front matter. It can then be -expanded with the usual specifiers or strings that -`org-capture-templates' supports. - -This function obeys `denote-prompts', but it ignores `file-type', -if present: it always sets the Org file extension for the created -note to ensure that the capture process works as intended, -especially for the desired output of the -`denote-org-capture-specifiers' (which can include arbitrary -text). - -Consult the manual for template samples." - (let (title keywords subdirectory date template signature) - (dolist (prompt denote-prompts) - (pcase prompt - ('title (setq title (denote-title-prompt - (when (use-region-p) - (buffer-substring-no-properties - (region-beginning) - (region-end)))))) - ('keywords (setq keywords (denote-keywords-prompt))) - ('subdirectory (setq subdirectory (denote-subdirectory-prompt))) - ('date (setq date (denote-date-prompt))) - ('template (setq template (denote-template-prompt))) - ('signature (setq signature (denote-signature-prompt))))) - (let* ((title (or title "")) - (date (if (or (null date) (string-empty-p date)) - (current-time) - (denote-valid-date-p date))) - (id (denote--find-first-unused-id - (denote-get-identifier date) - (denote--get-all-used-ids))) - (keywords (denote-keywords-sort keywords)) - (directory (if (denote--dir-in-denote-directory-p subdirectory) - (file-name-as-directory subdirectory) - (denote-directory))) - (template (if (stringp template) - template - (or (alist-get template denote-templates) ""))) - (signature (or signature "")) - (front-matter (denote--format-front-matter - title (denote--date nil 'org) keywords - (denote-get-identifier) 'org))) - (setq denote-last-path - (denote--path title keywords directory id 'org signature)) - (denote--keywords-add-to-history keywords) - (concat front-matter template denote-org-capture-specifiers)))) - -;; TODO 2023-12-02: Maybe simplify `denote-org-capture-with-prompts' -;; by passing a single PROMPTS that is the same value as `denote-prompts'? - -;;;###autoload -(defun denote-org-capture-with-prompts (&optional title keywords subdirectory date template) - "Like `denote-org-capture' but with optional prompt parameters. - -When called without arguments, do not prompt for anything. Just -return the front matter with title and keyword fields empty and -the date and identifier fields specified. Also make the file -name consist of only the identifier plus the Org file name -extension. - -Otherwise produce a minibuffer prompt for every non-nil value -that corresponds to the TITLE, KEYWORDS, SUBDIRECTORY, DATE, and -TEMPLATE arguments. The prompts are those used by the standard -`denote' command and all of its utility commands. - -When returning the contents that fill in the Org capture -template, the sequence is as follows: front matter, TEMPLATE, and -then the value of the user option `denote-org-capture-specifiers'. - -Important note: in the case of SUBDIRECTORY actual subdirectories -must exist---Denote does not create them. Same principle for -TEMPLATE as templates must exist and are specified in the user -option `denote-templates'." - (let ((denote-prompts '())) - (when template (push 'template denote-prompts)) - (when date (push 'date denote-prompts)) - (when subdirectory (push 'subdirectory denote-prompts)) - (when keywords (push 'keywords denote-prompts)) - (when title (push 'title denote-prompts)) - (denote-org-capture))) - -(defun denote-org-capture-delete-empty-file () - "Delete file if capture with `denote-org-capture' is aborted." - (when-let ((file denote-last-path) - ((denote--file-empty-p file))) - (delete-file denote-last-path))) - -(add-hook 'org-capture-after-finalize-hook #'denote-org-capture-delete-empty-file) - -(provide 'denote) -;;; denote.el ends here blob - f1e10ac8bc86bae9ea53691ea6bfdfc6d7af692e (mode 644) blob + /dev/null Binary files elpa/denote-2.3.5/denote.info and /dev/null differ blob - f87165c99de687bfbadf03e7d99cf25a1a5a2530 (mode 644) blob + /dev/null --- elpa/denote-2.3.5/dir +++ /dev/null @@ -1,19 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs misc features -* Denote: (denote). Simple notes with an efficient file-naming - scheme. blob - df1af93ade6eb525703582ca79f9792cecce80bf (mode 644) blob + /dev/null --- elpa/denote-2.3.5/tests/denote-test.el +++ /dev/null @@ -1,456 +0,0 @@ -;;; denote-test.el --- Unit tests for Denote -*- lexical-binding: t -*- - -;; Copyright (C) 2023 Free Software Foundation, Inc. - -;; Author: Protesilaos Stavrou -;; Maintainer: Denote Development <~protesilaos/denote@lists.sr.ht> -;; URL: https://git.sr.ht/~protesilaos/denote -;; Mailing-List: https://lists.sr.ht/~protesilaos/denote - -;; This file is NOT part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. -;; -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; WORK-IN-PROGRESS - -;;; Code: - -(require 'ert) -(require 'denote) - -(ert-deftest denote-test--denote--make-denote-directory () - "Test that `denote--make-denote-directory' creates the directory." - (should (null (denote--make-denote-directory)))) - -(ert-deftest denote-test--denote-directory () - "Test that variable `denote-directory' returns an absolute directory name." - (let ((path (denote-directory))) - (should (and (file-directory-p path) - (file-name-absolute-p path))))) - -(ert-deftest denote-test--denote--slug-no-punct () - "Test that `denote--slug-no-punct' removes punctuation from the string. -Concretely, replace with spaces anything that matches the -`denote-excluded-punctuation-regexp' and -`denote-excluded-punctuation-extra-regexp'." - (should (equal (denote--slug-no-punct "This is !@# test") - "This is test"))) - -(ert-deftest denote-test--denote--slug-hyphenate () - "Test that `denote--slug-hyphenate' hyphenates the string. -Also replace multiple hyphens with a single one and remove any -leading and trailing hyphen." - (should (equal (denote--slug-hyphenate "__ This is a test __ ") - "This-is-a-test"))) - -(ert-deftest denote-test--denote-sluggify () - "Test that `denote-sluggify' sluggifies the string. -To sluggify is to (i) downcase, (ii) hyphenate, (iii) de-punctuate, and (iv) remove spaces from the string." - (should (equal (denote-sluggify 'title " ___ !~!!$%^ This iS a tEsT ++ ?? ") - "this-is-a-test"))) - -(ert-deftest denote-test--denote--slug-put-equals () - "Test that `denote--slug-put-equals' replaces spaces/underscores with =. -Otherwise do the same as what is described in -`denote-test--denote--slug-hyphenate'. - -The use of the equals sign is for the SIGNATURE field of the -Denote file name." - (should (equal (denote--slug-put-equals "__ This is a test __ ") - "This=is=a=test"))) - -(ert-deftest denote-test--denote-sluggify-signature () - "Test that `denote-sluggify-signature' sluggifies the string for file signatures. -This is like `denote-test--denote-sluggify', except that it also -accounts for what we describe in `denote-test--denote--slug-put-equals'." - (should (equal (denote-sluggify-signature "--- ___ !~!!$%^ This -iS- a tEsT ++ ?? ") - "this=is=a=test"))) - -(ert-deftest denote-test--denote-sluggify-keyword () - "Test that `denote-sluggify-keyword' sluggifies the string while joining words. -In this context, to join words is to elimitate any space or -delimiter between them. - -Otherwise, this is like `denote-test--denote-sluggify'." - (should (equal (denote-sluggify-keyword "--- ___ !~!!$%^ This iS a - tEsT ++ ?? ") - "thisisatest"))) - -(ert-deftest denote-test--denote-sluggify-keywords () - "Test that `denote-sluggify-keywords' sluggifies a list of strings. -The function also account for the value of the user option -`denote-allow-multi-word-keywords'." - (should - (equal (denote-sluggify-keywords '("one !@# --- one" " two" "__ three __")) - '("oneone" "two" "three")))) - -(ert-deftest denote-test--denote--file-empty-p () - "Test that `denote--file-empty-p' returns non-nil on empty file." - ;; (should (null (denote--file-empty-p user-init-file)) - (should (let ((file (make-temp-file "denote-test"))) - (prog1 - (denote--file-empty-p file) - (delete-file file))))) - -(ert-deftest denote-test--denote-file-is-note-p () - "Test that `denote-file-is-note-p' checks that files is a Denote note. -For our purposes, a note must note be a directory, must satisfy -`file-regular-p', its path must be part of the variable -`denote-directory', it must have a Denote identifier in its name, -and use one of the extensions implied by `denote-file-type'." - (should (let* ((tmp (temporary-file-directory)) - (denote-directory tmp) - (file (concat tmp "20230522T154900--test__keyword.txt"))) - (with-current-buffer (find-file-noselect file) - (write-file file)) - (prog1 - (denote-file-is-note-p file) - (delete-file file))))) - -(ert-deftest denote-test--denote-file-has-identifier-p () - "Test that `denote-file-has-identifier-p' checks for a Denote identifier." - (should (denote-file-has-identifier-p "20230522T154900--test__keyword.txt")) - (should (null (denote-file-has-identifier-p "T154900--test__keyword.txt")))) - -(ert-deftest denote-test--denote-file-has-signature-p () - "Test that `denote-file-has-signature-p' checks for a Denote signature." - (should (denote-file-has-signature-p "20230522T154900==sig--test__keyword.txt")) - (should (null (denote-file-has-signature-p "20230522T154900--test__keyword.txt")))) - -(ert-deftest denote-test--denote-file-has-supported-extension-p () - "Test that `denote-file-has-supported-extension-p' matches a supported extension." - (should - (member - (file-name-extension "20230522T154900==sig--test__keyword.txt" :period) - (denote-file-type-extensions-with-encryption))) - (should - (null - (member - (file-name-extension "20230522T154900==sig--test__keyword" :period) - (denote-file-type-extensions-with-encryption))))) - -(ert-deftest denote-test--denote-file-type-extensions () - "Test that `denote-file-type-extensions' returns file extensions. -We check for the common file type extensions, though the user can -theoretically set `denote-file-types' to nil and handle things on -their own. We do not have to test for that scenario, because -such a user will be redefining large parts of Denote's behaviour -with regard to file types." - (let ((extensions (denote-file-type-extensions))) - (should (or (member ".md" extensions) - (member ".org" extensions) - (member ".txt" extensions))))) - -(ert-deftest denote-test--denote-file-type-extensions-with-encryption () - "Test that `denote-file-type-extensions-with-encryption' covers encryption. -Extend what we do in `denote-test--denote-file-type-extensions'." - (let ((extensions (denote-file-type-extensions-with-encryption))) - (should (or (member ".md" extensions) - (member ".org" extensions) - (member ".txt" extensions) - (member ".md.gpg" extensions) - (member ".org.gpg" extensions) - (member ".txt.gpg" extensions) - (member ".md.age" extensions) - (member ".org.age" extensions) - (member ".txt.age" extensions))))) - -(ert-deftest denote-test--denote-surround-with-quotes () - "Test that `denote-surround-with-quotes' returns a string in quotes." - (should (and (equal (denote-surround-with-quotes "test") "\"test\"") - (equal (denote-surround-with-quotes "") "\"\"") - (equal (denote-surround-with-quotes nil) "\"\"") - (equal (denote-surround-with-quotes 'wrong) "\"\"") - (equal (denote-surround-with-quotes '(wrong)) "\"\"")))) - -(ert-deftest denote-test--denote--format-front-matter () - "Test that `denote--format-front-matter' formats front matter correctly." - (should (and (equal (denote--format-front-matter "" "" '("") "" 'text) - (mapconcat #'identity - '("title: " - "date: " - "tags: " - "identifier: " - "---------------------------\n\n") - "\n")) - - (equal - (denote--format-front-matter - "Some test" "2023-06-05" '("one" "two") - "20230605T102234" 'text) - (mapconcat #'identity - '("title: Some test" - "date: 2023-06-05" - "tags: one two" - "identifier: 20230605T102234" - "---------------------------\n\n") - "\n")))) - - (should (and (equal (denote--format-front-matter "" "" nil "" 'org) - (mapconcat #'identity - '("#+title: " - "#+date: " - "#+filetags: " - "#+identifier: " - "\n") - "\n")) - - (equal - (denote--format-front-matter - "Some test" "2023-06-05" '("one" "two") - "20230605T102234" 'org) - (mapconcat #'identity - '("#+title: Some test" - "#+date: 2023-06-05" - "#+filetags: :one:two:" - "#+identifier: 20230605T102234" - "\n") - "\n")))) - - (should (and (equal (denote--format-front-matter "" "" nil "" 'markdown-yaml) - (mapconcat #'identity - '("---" - "title: \"\"" - "date: " - "tags: []" - "identifier: \"\"" - "---" - "\n") - "\n")) - - (equal - (denote--format-front-matter - "Some test" "2023-06-05" '("one" "two") - "20230605T102234" 'markdown-yaml) - (mapconcat #'identity - '("---" - "title: \"Some test\"" - "date: 2023-06-05" - "tags: [\"one\", \"two\"]" - "identifier: \"20230605T102234\"" - "---" - "\n") - "\n")))) - -(should (and (equal (denote--format-front-matter "" "" nil "" 'markdown-toml) - (mapconcat #'identity - '("+++" - "title = \"\"" - "date = " - "tags = []" - "identifier = \"\"" - "+++" - "\n") - "\n")) - - (equal - (denote--format-front-matter - "Some test" "2023-06-05" '("one" "two") - "20230605T102234" 'markdown-toml) - (mapconcat #'identity - '("+++" - "title = \"Some test\"" - "date = 2023-06-05" - "tags = [\"one\", \"two\"]" - "identifier = \"20230605T102234\"" - "+++" - "\n") - "\n"))))) - -(ert-deftest denote-test--denote-format-file-name () - "Test that `denote-format-file-name' returns all expected paths." - (let* ((title "Some test") - (id (format-time-string denote-id-format (denote-valid-date-p "2023-11-28 05:53:11"))) - (denote-directory "/tmp/test-denote") - (kws '("one" "two"))) - (should-error (denote-format-file-name - nil - id - kws - title - (denote--file-extension 'org) - "")) - - (should-error (denote-format-file-name - "" - id - kws - title - (denote--file-extension 'org) - "")) - - (should-error (denote-format-file-name - denote-directory ; notice this is the `let' bound value without the suffix - id - kws - title - (denote--file-extension 'org) - "")) - - (should-error (denote-format-file-name - (denote-directory) - nil - kws - title - (denote--file-extension 'org) - "")) - - (should-error (denote-format-file-name - (denote-directory) - "" - kws - title - (denote--file-extension 'org) - "")) - - (should-error (denote-format-file-name - (denote-directory) - "0123456" - kws - title - (denote--file-extension 'org) - "")) - - (should (equal (denote-format-file-name - (denote-directory) - id - kws - title - (denote--file-extension 'org) - "") - "/tmp/test-denote/20231128T055311--some-test__one_two.org")) - - (should (equal (denote-format-file-name - (denote-directory) - id - nil - "" - (denote--file-extension 'org) - "") - "/tmp/test-denote/20231128T055311.org")) - - (should (equal (denote-format-file-name - (denote-directory) - id - nil - nil - (denote--file-extension 'org) - nil) - "/tmp/test-denote/20231128T055311.org")) - - (should (equal (denote-format-file-name - (denote-directory) - id - kws - title - (denote--file-extension 'org) - "sig") - "/tmp/test-denote/20231128T055311==sig--some-test__one_two.org")))) - -(ert-deftest denote-test--denote-get-file-extension () - "Test that `denote-get-file-extension' gets the correct file extension." - (should (and (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing") "") - (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org") ".org") - (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.gpg") ".org.gpg") - (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.age") ".org.age")))) - -(ert-deftest denote-test--denote-get-file-extension-sans-encryption () - "Test that `denote-get-file-extension-sans-encryption' gets the file extension without encryption." - (should (and (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing") "") - (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org") ".org") - (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.gpg") ".org") - (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.age") ".org")))) - -(ert-deftest denote-test--denote-filetype-heuristics () - "Test that `denote-filetype-heuristics' gets the correct file type." - (should (and (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing") (caar denote-file-types)) - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org") 'org) - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.gpg") 'org) - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.age") 'org) - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing") 'org) - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt") 'text) - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.gpg") 'text) - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.age") 'text) - ;; NOTE 2023-10-11: It returns `markdown-yaml' as a fallback. In - ;; an actual file, it reads the file contents to determine what - ;; it is and can return `markdown-toml'. In principle, we should - ;; be testing this here, though I prefer to keep things simple. - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md") 'markdown-yaml) - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.gpg") 'markdown-yaml) - (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.age") 'markdown-yaml)))) - -(ert-deftest denote-test--denote-convert-file-name-keywords-to-crm () - "Ensure that `denote-convert-file-name-keywords-to-crm' returns words as comma-separated string." - (should - (and (equal (denote-convert-file-name-keywords-to-crm "_denote_keywords_testing") "denote,keywords,testing") - (equal (denote-convert-file-name-keywords-to-crm "_denote") "denote") - (equal (denote-convert-file-name-keywords-to-crm "") "")))) - -(ert-deftest denote-test--denote-get-identifier () - "Test that `denote-get-identifier' returns an identifier." - (should (and (equal (denote-get-identifier) (format-time-string denote-id-format (current-time))) - (equal (denote-get-identifier "2024-02-01 10:34") "20240201T103400") - (equal (denote-get-identifier 1705644188) "20240119T080308") - (equal (denote-get-identifier '(26026 4251)) "20240119T080307"))) - (should-error (denote-get-identifier "Invalid date"))) - -;;;; denote-journal-extras.el - -(require 'denote-journal-extras) - -(ert-deftest denote-test--denote-journal-extras-daily--title-format () - "Make sure that `denote-journal-extras-daily--title-format' yields the desired format." - (should (and - ;; These three should prompt, but I am here treating the - ;; prompt as if already returned a string. The test for - ;; the `denote-title-prompt' can be separate. - (stringp - (cl-letf (((symbol-function 'denote-title-prompt) #'identity) - (denote-journal-extras-title-format nil)) - (denote-journal-extras-daily--title-format))) - - (stringp - (cl-letf (((symbol-function 'denote-title-prompt) #'identity) - (denote-journal-extras-title-format t)) - (denote-journal-extras-daily--title-format))) - - (stringp - (cl-letf (((symbol-function 'denote-title-prompt) #'identity) - (denote-journal-extras-title-format :some-arbitrary-keyword)) - (denote-journal-extras-daily--title-format))) - - ;; And these return the following values - (string-match-p - "\\<.*?\\>" - (let ((denote-journal-extras-title-format 'day)) - (denote-journal-extras-daily--title-format))) - - (string-match-p - "\\<.*?\\> [0-9]\\{,2\\} \\<.*?\\> [0-9]\\{,4\\}" - (let ((denote-journal-extras-title-format 'day-date-month-year)) - (denote-journal-extras-daily--title-format))) - - (string-match-p - "\\<.*?\\> [0-9]\\{,2\\} \\<.*?\\> [0-9]\\{,4\\} [0-9]\\{,2\\}:[0-9]\\{,2\\} \\<.*?\\>" - (let ((denote-journal-extras-title-format 'day-date-month-year-12h)) - (denote-journal-extras-daily--title-format))) - - (string-match-p - "\\<.*?\\> [0-9]\\{,2\\} \\<.*?\\> [0-9]\\{,4\\} [0-9]\\{,2\\}:[0-9]\\{,2\\}" - (let ((denote-journal-extras-title-format 'day-date-month-year-24h)) - (denote-journal-extras-daily--title-format)))))) - -(provide 'denote-test) -;;; denote-test.el ends here blob - e8902a607b4dd36ee8197f7ce1a56da7b3c78568 (mode 644) blob + /dev/null --- elpa/denote-2.3.5.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2024-03-31T11:20:01+0200 using EDDSA \ No newline at end of file blob - 1f7c4b790168032cdf15e155f3d3f3419b908ce0 (mode 644) blob + /dev/null --- elpa/denote-menu-1.2.0/.dir-locals.el +++ /dev/null @@ -1,4 +0,0 @@ -;;; Directory Local Variables -;;; For more information see (info "(emacs) Directory Variables") - -((emacs-lisp-mode . ((indent-tabs-mode . nil)))) blob - 0b5ae8827e61e47b646cee3fba3be93679430b8c (mode 644) blob + /dev/null --- elpa/denote-menu-1.2.0/NEWS +++ /dev/null @@ -1,20 +0,0 @@ --*- mode: org;-*- - -* Version 1.2.0 - -- Added support for denote file signatures. See the - =denote-menu-show-file-signature= variable -- Added filter function =denote-menu-filter-out-keyword= to filter - /out/ denote files based on keyword. - -* Version 1.1.1 - -- Added custom user option =denote-menu-show-file-type=. -- Fixed dired export and menu bugs when when multiple files with the - same identifier but different file types (e.g org exports). -- Fixed =denote-menu-filter= not being interactive. - - -* Version 1.0.0 - -Initial release blob - 5e85f7791f1e90d41b6d5bdcf841a5506c0c5faa (mode 644) blob + /dev/null --- elpa/denote-menu-1.2.0/README-elpa +++ /dev/null @@ -1,200 +0,0 @@ - ━━━━━━━━━━━━━━━━━ - DENOTE-MENU - - Mohamed Suliman - sulimanm@tcd.ie - ━━━━━━━━━━━━━━━━━ - - - - - -1 Overview -══════════ - - `denote-menu' provides an interface for viewing your denote files that - goes beyond using the standard `dired' emacs command to view your - `denote-directory'. Using dired is a fine method for viewing your - denote files (among other things), however denote’s file naming scheme - tends to clutters the buffer with hyphens and underscores. This - package aims to declutter your view of your files by making it easy to - view the 3 main components of denote files, that is their timestamp, - title, and keywords. Derived from the builtin `tabulated-list-mode', - the `*Denote*' buffer that is created with the `list-denotes' command - is visually similar to that created by commands like `list-packages' - and `list-processes', and provides methods to filter the denote files - that are shown, as well as exporting to dired with the denote files - that are currently shown for them to be operated upon further. In this - way, `denote-menu' adheres to the core tenants of the denote package - itself. - - It is /predictable/ as it makes use of existing emacs functionality to - display files in a tabulated way similar to the package menu. It is - /composable/, integrating well with other emacs packages (denote, in - this case) and builtin functionality, opting to not reinvent the wheel - as to how the data is displayed. The scope of this package is narrow: - displaying and filtering denote files in a visually appealing and - intuitive manner. `denote-menu' is also /flexible/ and /hackable/, - providing a simple API to create your own filters, and integrates well - with dired by providing the `denote-menu-export-to-dired' command, - which allows for further action on denote files beyond just viewing - and filtering them. - - - - -2 Installation -══════════════ - - `denote-menu' is available on the GNU ELPA package archive. To - install, simply run - - ┌──── - │ M-x package-install RET denote-menu RET - └──── - - - This package requires Denote `v2.0.0' or above. - - -3 Usage -═══════ - - Assuming that you have `denote-directory' set to a directory that has - denote files, simply run `M-x list-denotes' to open the `*Denote*' - buffer. You will be presented with a tabulated list of your denote - files whose filenames match the `denote-menu-initial-regex' regular - expression. By default this is set to match all denote files in the - `denote-directory'. - - The tabulated list includes 3 columns, one for the timestamp, title, - and keywords of each denote file. The timestamp column includes a - button that when followed will open the corresponding denote file - using `find-file'. - - -3.1 Filtering by regular expression -─────────────────────────────────── - - To filter the denote files shown by a regular expression, run `M-x - denote-menu-filter'. This will prompt for a regular expression and - will update the buffer to list only those denote files whose filenames - match. Running `denote-menu-filter' again will further filter down the - list. This is akin to running `% m' inside a `dired' buffer. - - -3.2 Filtering by keyword -──────────────────────── - - To filter the denote files shown to those that are tagged with - specific keywords, run `M-x denote-menu-filter-by-keyword'. This - command will prompt for a list of comma separated keywords (with - completion) and filter the list to those denote files that are tagged - with at least one of the inputted keywords. To filter /out/ any denote - files by keyword, run `M-x denote-menu-filter-out-keyword'. - - -3.3 Defining your own filters -───────────────────────────── - - There are two ways to define your own filters: - 1. Write an interactive function that sets `denote-menu-current-regex' - to be a regular expression that matches your desired set of denote - files, and then calls `denote-menu-update-entries'. For example, if - I would like to a filter that filters out those denote files that - were not tagged with the “biblio” keyword, I would add the - following to my emacs configuration: - ┌──── - │ (defun my/denote-menu-filter-biblio-only () - │ (interactive) - │ (setq denote-menu-current-regex "_biblio") - │ (denote-menu-update-entries)) - └──── - - 2. Write an interactive function that sets `tabulated-list-entries' to - a be a function that maps each desired denote file path to an entry - using `denote-menu--path-to-entry' function, and calls - `revert-buffer'. For example, if the variable - `my-matching-denote-paths' contains a list of file paths of the - desired denote files, then your filter function would look - something like the following: - ┌──── - │ (defun my/denote-menu-filter-custom () - │ (interactive) - │ (let ((my-matching-denote-paths '("/home/namilus/zettelkasten/20220719T135304--this-is-my-first-note__meta.org"))) - │ (setq tabulated-list-entries (lambda () (mapcar #'denote-menu--path-to-entry my-matching-denote-paths))) - │ (revert-buffer))) - └──── - - -3.4 Clearing filters -──────────────────── - - To clear the filters and revert back to the - `denote-menu-initial-regex', run `M-x denote-menu-clear-filters'. - - -3.5 Exporting to `dired' -──────────────────────── - - Adhering to the tenets of predictability and composability, - `denote-menu' provides the command `denote-menu-export-to-dired' to - allow further action on these files that is permitted in dired e.g - copying, moving, compressing, etc. We do not reinvent the wheel here - but instead defer to what already exists. - - When in the `*Denote*' buffer running `M-x - denote-menu-export-to-dired' will open a `dired' buffer in the same - window with those denote files that were displayed in the `*Denote*' - buffer already marked. - - -4 Sample configuration -══════════════════════ - - The user options for `denote-menu' are: - `denote-menu-date-column-width' - A number value for the width of the date column. Defaults to 17. - `denote-menu-signature-column-width' - A number value for the width of the signature column. Defaults - to 10. - `denote-menu-title-column-width' - A number value for the width of the title column. Defaults to - 85. - `denote-menu-keywords-column-width' - A number value for the width of the keywords column. Defaults to - 30. This value is irrelevant as it is the final column and will - take up the remaining width of the buffer. - `denote-menu-show-file-type' - If non-nil, appends the file type of the current denote file to - the title. - `denote-menu-show-file-signature' - If non-nil, the column for file signature is added. - `denote-menu-initial-regex' - A string that is the regular expression that is used to - initially populate the `*Denote*' buffer with matching - entries. This could allow for potential workflows such as having - a dedicated buffer to display your journal denote files (e.g - those tagged with the “journal” keyword), etc. Defaults to the - `.' regular expression. - `denote-menu-action' - A function that takes as argument the current denote file path - and performs an action on it. Defaults to `(lambda (path) - (find-file path))'. This function is then called whenever the - button in the timestamp column is followed. - - - A sample user configuration is given below that sets appropriate - keybindings for the commands described in the previous section: - - ┌──── - │ (require 'denote-menu) - │ - │ (global-set-key (kbd "C-c z") #'list-denotes) - │ - │ (define-key denote-menu-mode-map (kbd "c") #'denote-menu-clear-filters) - │ (define-key denote-menu-mode-map (kbd "/ r") #'denote-menu-filter) - │ (define-key denote-menu-mode-map (kbd "/ k") #'denote-menu-filter-by-keyword) - │ (define-key denote-menu-mode-map (kbd "/ o") #'denote-menu-filter-out-keyword) - │ (define-key denote-menu-mode-map (kbd "e") #'denote-menu-export-to-dired) - └──── blob - 40386d34ad9d62586f4397a701591fb1c8e87ad2 (mode 644) blob + /dev/null Binary files elpa/denote-menu-1.2.0/README.info and /dev/null differ blob - 2f27a17eaf2ae4bc3bda5f51fe92570dd0f2c0f0 (mode 644) blob + /dev/null --- elpa/denote-menu-1.2.0/README.org +++ /dev/null @@ -1,160 +0,0 @@ -#+title: denote-menu -#+author: Mohamed Suliman -#+email: sulimanm@tcd.ie -#+language: en -#+options: ':t toc:nil author:t email:t num:t - -#+html: GNU ELPA - -* Overview -=denote-menu= provides an interface for viewing your denote files that -goes beyond using the standard =dired= emacs command to view your -=denote-directory=. Using dired is a fine method for viewing your -denote files (among other things), however denote's file naming scheme -tends to clutters the buffer with hyphens and underscores. This -package aims to declutter your view of your files by making it easy to -view the 3 main components of denote files, that is their timestamp, -title, and keywords. Derived from the builtin =tabulated-list-mode=, -the =*Denote*= buffer that is created with the =list-denotes= command -is visually similar to that created by commands like =list-packages= -and =list-processes=, and provides methods to filter the denote files -that are shown, as well as exporting to dired with the denote files -that are currently shown for them to be operated upon further. In this -way, =denote-menu= adheres to the core tenants of the denote package -itself. - -It is /predictable/ as it makes use of existing emacs functionality to -display files in a tabulated way similar to the package menu. It is -/composable/, integrating well with other emacs packages (denote, in -this case) and builtin functionality, opting to not reinvent the wheel -as to how the data is displayed. The scope of this package is narrow: -displaying and filtering denote files in a visually appealing and -intuitive manner. =denote-menu= is also /flexible/ and /hackable/, -providing a simple API to create your own filters, and integrates well -with dired by providing the =denote-menu-export-to-dired= command, -which allows for further action on denote files beyond just viewing and -filtering them. - -#+attr_html: :width 750px -[[file:screenshots/screenshot.png]] - -* Installation -=denote-menu= is available on the GNU ELPA package archive. To install, -simply run - -#+begin_example -M-x package-install RET denote-menu RET -#+end_example - - -This package requires Denote =v2.0.0= or above. -* Usage -Assuming that you have =denote-directory= set to a directory that has -denote files, simply run =M-x list-denotes= to open the =*Denote*= -buffer. You will be presented with a tabulated list of your denote -files whose filenames match the =denote-menu-initial-regex= regular -expression. By default this is set to match all denote files in the -=denote-directory=. - -The tabulated list includes 3 columns, one for the timestamp, title, -and keywords of each denote file. The timestamp column includes a -button that when followed will open the corresponding denote file -using =find-file=. - -** Filtering by regular expression -To filter the denote files shown by a regular expression, run =M-x -denote-menu-filter=. This will prompt for a regular expression and -will update the buffer to list only those denote files whose filenames -match. Running =denote-menu-filter= again will further filter down the -list. This is akin to running =% m= inside a =dired= buffer. -** Filtering by keyword -To filter the denote files shown to those that are tagged with -specific keywords, run =M-x denote-menu-filter-by-keyword=. This -command will prompt for a list of comma separated keywords (with -completion) and filter the list to those denote files that are tagged -with at least one of the inputted keywords. To filter /out/ any denote -files by keyword, run =M-x denote-menu-filter-out-keyword=. -** Defining your own filters -There are two ways to define your own filters: -1. Write an interactive function that sets =denote-menu-current-regex= - to be a regular expression that matches your desired set of denote - files, and then calls =denote-menu-update-entries=. For example, if - I would like to a filter that filters out those denote files that - were not tagged with the "biblio" keyword, I would add the following to my - emacs configuration: - #+begin_src emacs-lisp -(defun my/denote-menu-filter-biblio-only () - (interactive) - (setq denote-menu-current-regex "_biblio") - (denote-menu-update-entries)) - #+end_src - -2. Write an interactive function that sets =tabulated-list-entries= to - a be a function that maps each desired denote file path to an entry - using =denote-menu--path-to-entry= function, and calls - =revert-buffer=. For example, if the variable - =my-matching-denote-paths= contains a list of file paths of the - desired denote files, then your filter function would look something like the following: - #+begin_src emacs-lisp -(defun my/denote-menu-filter-custom () - (interactive) - (let ((my-matching-denote-paths '("/home/namilus/zettelkasten/20220719T135304--this-is-my-first-note__meta.org"))) - (setq tabulated-list-entries (lambda () (mapcar #'denote-menu--path-to-entry my-matching-denote-paths))) - (revert-buffer))) - #+end_src -** Clearing filters - To clear the filters and revert back to the -=denote-menu-initial-regex=, run =M-x denote-menu-clear-filters=. -** Exporting to =dired= -Adhering to the tenets of predictability and composability, -=denote-menu= provides the command =denote-menu-export-to-dired= to -allow further action on these files that is permitted in dired e.g -copying, moving, compressing, etc. We do not reinvent the wheel here -but instead defer to what already exists. - -When in the =*Denote*= buffer running =M-x -denote-menu-export-to-dired= will open a =dired= buffer in the same -window with those denote files that were displayed in the =*Denote*= -buffer already marked. -* Sample configuration -The user options for =denote-menu= are: -- =denote-menu-date-column-width= :: A number value for the width of - the date column. Defaults to 17. -- =denote-menu-signature-column-width= :: A number value for the width - of the signature column. Defaults to 10. -- =denote-menu-title-column-width= :: A number value for the width of - the title column. Defaults to 85. -- =denote-menu-keywords-column-width= :: A number value for the width - of the keywords column. Defaults to 30. This value is irrelevant as - it is the final column and will take up the remaining width of the buffer. -- =denote-menu-show-file-type= :: If non-nil, appends the file type of - the current denote file to the title. -- =denote-menu-show-file-signature= :: If non-nil, the column for file - signature is added. -- =denote-menu-initial-regex= :: A string that is the regular - expression that is used to initially populate the =*Denote*= buffer - with matching entries. This could allow for potential workflows such - as having a dedicated buffer to display your journal denote files - (e.g those tagged with the "journal" keyword), etc. Defaults to the - =.= regular expression. -- =denote-menu-action= :: A function that takes as argument the - current denote file path and performs an action on it. Defaults to - =(lambda (path) (find-file path))=. This function is then called - whenever the button in the timestamp column is followed. - - -A sample user configuration is given below that sets appropriate -keybindings for the commands described in the previous section: - -#+begin_src emacs-lisp -(require 'denote-menu) - -(global-set-key (kbd "C-c z") #'list-denotes) - -(define-key denote-menu-mode-map (kbd "c") #'denote-menu-clear-filters) -(define-key denote-menu-mode-map (kbd "/ r") #'denote-menu-filter) -(define-key denote-menu-mode-map (kbd "/ k") #'denote-menu-filter-by-keyword) -(define-key denote-menu-mode-map (kbd "/ o") #'denote-menu-filter-out-keyword) -(define-key denote-menu-mode-map (kbd "e") #'denote-menu-export-to-dired) -#+end_src - blob - 7e03f7f2dc27470da2d5cf0a1095c226b32f7857 (mode 644) blob + /dev/null --- elpa/denote-menu-1.2.0/denote-menu-autoloads.el +++ /dev/null @@ -1,30 +0,0 @@ -;;; denote-menu-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from denote-menu.el - -(autoload 'denote-menu-list-notes "denote-menu" "\ -Display list of Denote files in variable `denote-directory'." t) -(register-definition-prefixes "denote-menu" '("denote-menu-" "list-denotes")) - -;;; End of scraped data - -(provide 'denote-menu-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; denote-menu-autoloads.el ends here blob - a605d271693618bc4000744db1278509072e26d4 (mode 644) blob + /dev/null --- elpa/denote-menu-1.2.0/denote-menu-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from denote-menu.el -*- no-byte-compile: t -*- -(define-package "denote-menu" "1.2.0" "View denote files in a tabulated list." '((emacs "28.1") (denote "2.0.0")) :commit "6d97b6be0511420dca27b294844bdaa5fa72f753" :authors '(("Mohamed Suliman" . "sulimanm@tcd.ie")) :maintainer '("Mohamed Suliman" . "sulimanm@tcd.ie") :url "https://github.com/namilus/denote-menu") blob - 9a317959753527540f3f75e9fa62b9e3e1807e9a (mode 644) blob + /dev/null --- elpa/denote-menu-1.2.0/denote-menu.el +++ /dev/null @@ -1,314 +0,0 @@ -;;; denote-menu.el --- View denote files in a tabulated list. -*- lexical-binding: t -*- - -;; Copyright (C) 2023 Free Software Foundation, Inc. - -;; Author: Mohamed Suliman -;; Version: 1.2.0 -;; URL: https://github.com/namilus/denote-menu -;; Package-Requires: ((emacs "28.1") (denote "2.0.0")) - -;; This file is NOT part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. -;; -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; `denote-menu' is an extension to the elpa package `denote' and provides -;; an interface for viewing your denote files that goes beyond using the -;; standard `dired' emacs command to view your `denote-directory'. Using -;; dired is a fine method for viewing your denote files (among other -;; things), however denote's file naming scheme tends to clutters the -;; buffer with hyphens and underscores. This package aims to declutter your -;; view of your files by making it easy to view the 3 main components of -;; denote files, that is their timestamp, title, and keywords. Derived from -;; the builtin `tabulated-list-mode', the `*Denote*' buffer that is created -;; with the `list-denotes' command is visually similar to that created by -;; commands like `list-packages' and `list-processes', and provides methods -;; to filter the denote files that are shown, as well as exporting to dired -;; with the denote files that are currently shown for them to be operated -;; upon further. - -;;; Code: - -(require 'tabulated-list) -(require 'denote) -(require 'dired) -(require 'seq) - -(defgroup denote-menu () - "View Denote files" - :group 'files) - -(defcustom denote-menu-date-column-width 17 - "Width for the date column." - :type 'number - :group 'denote-menu) - -(defcustom denote-menu-signature-column-width 10 - "Width for the date column." - :type 'number - :group 'denote-menu) - -(defcustom denote-menu-title-column-width 85 - "Width for the title column." - :type 'number - :group 'denote-menu) - -(defcustom denote-menu-keywords-column-width 30 - "Width for the keywords column." - :type 'number - :group 'denote-menu) - -(defcustom denote-menu-action (lambda (path) (find-file path)) - "Function to execute when a denote file button action is -invoked. Takes a single argument which is the path of the -denote file corresponding to the button." - :type 'function - :group 'denote-menu) - -(defcustom denote-menu-initial-regex "." - "Regex used to initially populate the buffer with matching denote files." - :type 'string - :group 'denote-menu) - -(defcustom denote-menu-show-file-type t - "Whether to show the denote file type." - :type 'boolean - :group 'denote-menu) - -(defcustom denote-menu-show-file-signature nil - "Whether to show the denote file signature." - :type 'boolean - :group 'denote-menu) - -(defvar denote-menu-current-regex denote-menu-initial-regex - "The current regex used to match denote filenames.") - -;;;###autoload -(defun denote-menu-list-notes () - "Display list of Denote files in variable `denote-directory'." - (interactive) - ;; kill any existing *Denote* buffer - (let ((denote-menu-buffer-name (format "*Denote %s*" denote-directory))) - (when (get-buffer denote-menu-buffer-name) - (kill-buffer denote-menu-buffer-name)) - (let ((buffer (get-buffer-create denote-menu-buffer-name))) - (with-current-buffer buffer - (setq buffer-file-coding-system 'utf-8) - (setq denote-menu-current-regex denote-menu-initial-regex) - (denote-menu-mode)) - - (pop-to-buffer-same-window buffer)))) - -(defalias 'list-denotes 'denote-menu-list-notes - "Alias of `denote-menu-list-notes' command.") - -(defun denote-menu-update-entries () - "Sets `tabulated-list-entries' to a function that maps currently -displayed denote file names matching the value of -`denote-menu-current-regex' to a tabulated list entry following -the defined form. Then updates the buffer." - (if tabulated-list-entries - (progn - (let - ((current-entry-paths (denote-menu--entries-to-paths))) - (setq tabulated-list-entries - (lambda () - (let ((matching-denote-files - (denote-menu-files-matching-regexp current-entry-paths denote-menu-current-regex))) - (mapcar #'denote-menu--path-to-entry matching-denote-files)))))) - (setq tabulated-list-entries - (lambda () - (let ((matching-denote-files - (denote-directory-files-matching-regexp denote-menu-current-regex))) - (mapcar #'denote-menu--path-to-entry matching-denote-files))))) - - (revert-buffer)) - -(defun denote-menu--entries-to-filenames () - "Return list of file names present in the *Denote* buffer." - (mapcar (lambda (entry) - (let* ((list-entry-identifier (car entry)) - (list-entry-denote-identifier (car (split-string list-entry-identifier "-"))) - (list-entry-denote-file-type (cadr (split-string list-entry-identifier "-")))) - (file-name-nondirectory (denote-menu-get-path-by-id list-entry-denote-identifier - list-entry-denote-file-type)))) - (funcall tabulated-list-entries))) - -(defun denote-menu--entries-to-paths () - "Return list of file paths present in the *Denote* buffer." - (mapcar (lambda (entry) - (let* ((list-entry-identifier (car entry)) - (list-entry-denote-identifier (car (split-string list-entry-identifier "-"))) - (list-entry-denote-file-type (cadr (split-string list-entry-identifier "-")))) - (denote-menu-get-path-by-id list-entry-denote-identifier list-entry-denote-file-type))) - (funcall tabulated-list-entries))) - -(defun denote-menu-get-path-by-id (id file-type) - "Return absolute path of denote file with ID timestamp and -FILE-TYPE in `denote-directory-files'." - (let* ((files (denote-directory-files)) - (matching-files-with-id (seq-filter (lambda (f) (and (string-prefix-p id (file-name-nondirectory f)))) files))) - (car (seq-filter (lambda (f) (string-match-p (concat "\\." file-type) f)) matching-files-with-id)))) - -(defun denote-menu-files-matching-regexp (files regexp) - "Return list of files matching REGEXP from FILES." - (seq-filter (lambda (f) (string-match-p regexp f)) files)) - -(defun denote-menu--path-to-unique-identifier (path) - "Convert PATH to a unique identifier to be used for -`tabulated-list-entries'. Done by taking the denote identifier of -PATH and appending the filename extension." - (let ((path-identifier (denote-retrieve-filename-identifier path)) - (extension (file-name-extension path))) - (format "%s-%s" path-identifier extension))) - -(defun denote-menu--path-to-entry (path) - "Convert PATH to an entry matching the form of `tabulated-list-entries'." - (if denote-menu-show-file-signature - `(,(denote-menu--path-to-unique-identifier path) - [(,(denote-menu-date path) . (action ,(lambda (button) (funcall denote-menu-action path)))) - ,(denote-menu-signature path) - ,(denote-menu-title path) - ,(propertize (format "%s" (denote-extract-keywords-from-path path)) 'face 'italic)]) - - `(,(denote-menu--path-to-unique-identifier path) - [(,(denote-menu-date path) . (action ,(lambda (button) (funcall denote-menu-action path)))) - ,(denote-menu-title path) - ,(propertize (format "%s" (denote-extract-keywords-from-path path)) 'face 'italic)]))) - -(defun denote-menu-date (path) - "Return human readable date from denote PATH identifier." - (let* ((timestamp (split-string (denote-retrieve-filename-identifier path) "T")) - (date (car timestamp)) - (year (substring date 0 4)) - (month (substring date 4 6)) - (day (substring date 6 8)) - - (time (cadr timestamp)) - (hour (substring time 0 2)) - (seconds (substring time 2 4))) - - (format "%s-%s-%s %s:%s" year month day hour seconds))) - -(defun denote-menu-signature (path) - "Return file signature from denote PATH identifier." - (let ((signature (denote-retrieve-filename-signature path))) - (if signature - signature - (propertize " " 'face 'font-lock-comment-face)))) - -(defun denote-menu-type (path) - "Return file type of PATH" - (file-name-extension (file-name-nondirectory path))) - -(defun denote-menu-title (path) - "Return title of PATH. -If the denote file PATH has no title, return the string \"(No -Title)\". Otherwise return PATH's title. - -Determine whether a denote file has a title based on the -following rule derived from the file naming scheme: - -1. If the path does not have a \"--\", it has no title." - - (let* ((title (if (or (not (string-match-p "--" path))) - (propertize "(No Title)" 'face 'font-lock-comment-face) - (denote-retrieve-filename-title path))) - (file-type (propertize (concat "." (denote-menu-type path)) 'face 'font-lock-keyword-face))) - (if denote-menu-show-file-type - (concat title " " file-type) - title))) - -(defun denote-menu-filter (regexp) - "Filter `tabulated-list-entries' matching REGEXP. -When called interactively, prompt for REGEXP. - -Revert the *Denotes* buffer to include only the matching entries." - (interactive (list (read-regexp "Filter regex: "))) - (setq denote-menu-current-regex regexp) - (denote-menu-update-entries)) - -(defun denote-menu-filter-by-keyword (keywords) - "Prompt for KEYWORDS and filters the list accordingly. -When called from Lisp, KEYWORDS is a list of strings." - (interactive (list (denote-keywords-prompt))) - (let ((regex (denote-menu--keywords-to-regex keywords))) - (setq denote-menu-current-regex regex) - (denote-menu-update-entries))) - -(defun denote-menu--keywords-to-regex (keywords) - "Converts KEYWORDS into a regex that matches denote files that -contain at least one of the keywords." - (concat "\\(" (mapconcat (lambda (keyword) (format "_%s" keyword)) keywords "\\|") "\\)")) - -(defun denote-menu-filter-out-keyword (keywords) - "Prompt for KEYWORDS and filters out of the list those denote -files that contain one of the keywords. When called from Lisp, - KEYWORDS is a list of strings." - (interactive (list (denote-keywords-prompt))) - ;; need to get a list of the denote files that do not include the - ;; keywords and set tabulated entries to be those. - (let* ((regex (denote-menu--keywords-to-regex keywords)) - (non-matching-files (seq-filter - (lambda (f) - (not (string-match-p regex (denote-get-file-name-relative-to-denote-directory f)))) - (denote-menu--entries-to-paths)))) - (setq tabulated-list-entries - (lambda () - (mapcar #'denote-menu--path-to-entry non-matching-files)))) - (revert-buffer)) - -(defun denote-menu-clear-filters () - "Reset filters to `denote-menu-initial-regex' and update buffer." - (interactive) - (setq denote-menu-current-regex denote-menu-initial-regex) - (setq tabulated-list-entries nil) - (denote-menu-update-entries) ) - -(defun denote-menu-export-to-dired () - "Switch to variable `denote-directory' and mark filtered *Denotes* -files." - (interactive) - (let ((files-to-mark (denote-menu--entries-to-filenames))) - (dired denote-directory) - (revert-buffer) - (dired-unmark-all-marks) - (dired-mark-if - (and (not (looking-at-p dired-re-dot)) - (not (eolp)) ; empty line - (let ((fn (dired-get-filename t t))) - (and fn (member fn files-to-mark)))) - "matching file"))) - -(define-derived-mode denote-menu-mode tabulated-list-mode "Denote Menu" - "Major mode for browsing a list of Denote files." - :interactive nil - (if denote-menu-show-file-signature - (setq tabulated-list-format `[("Date" ,denote-menu-date-column-width t) - ("Signature" ,denote-menu-signature-column-width nil) - ("Title" ,denote-menu-title-column-width nil) - ("Keywords" ,denote-menu-keywords-column-width nil)]) - - (setq tabulated-list-format `[("Date" ,denote-menu-date-column-width t) - ("Title" ,denote-menu-title-column-width nil) - ("Keywords" ,denote-menu-keywords-column-width nil)])) - - (denote-menu-update-entries) - (setq tabulated-list-sort-key '("Date" . t)) - (tabulated-list-init-header) - (tabulated-list-print)) - -(provide 'denote-menu) -;;; denote-menu.el ends here blob - e640bffd0f2097d0b5a11f8e19783888fddff881 (mode 644) blob + /dev/null --- elpa/denote-menu-1.2.0/dir +++ /dev/null @@ -1,15 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: blob - a2e9b139c4186fb1a90b5504851ee098d88fe82b (mode 644) blob + /dev/null Binary files elpa/denote-menu-1.2.0/screenshots/screenshot.png and /dev/null differ blob - 03e8b786b1f8b32b50cd2f379bfa1770812739c1 (mode 644) blob + /dev/null --- elpa/denote-menu-1.2.0.signed +++ /dev/null @@ -1,2 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2023-09-27T23:05:02+0200 using RSA -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2023-09-27T23:05:02+0200 using EDDSA \ No newline at end of file blob - b178e74dbf4d26e57e170d4fcb79cfe61bbc6468 (mode 644) blob + /dev/null --- elpa/denote-refs-0.1.2/.dir-locals.el +++ /dev/null @@ -1,5 +0,0 @@ -;;; Directory Local Variables -*- no-byte-compile: t -*- -;;; For more information see (info "(emacs) Directory Variables") - -((nil . ((fill-column . 70) - (indent-tabs-mode . nil)))) blob - f288702d2fa16d3cdf0035b15a9fcbc552cd88e7 (mode 644) blob + /dev/null --- elpa/denote-refs-0.1.2/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program 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 of the License, or - (at your option) any later version. - - This program 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 this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. blob - eae00fa14e9d1a7956e2674ac6c11d9a0cdcd319 (mode 644) blob + /dev/null --- elpa/denote-refs-0.1.2/README-elpa +++ /dev/null @@ -1,45 +0,0 @@ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - DENOTE-REFS - SHOW LINKS AND BACKLINKS IN DENOTE - NOTES - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - -Denote-Refs shows the list of linked file and backlinks to current file. -This list is shown just below the front matter of your note. To enable -do `M-x denote-refs-mode'. You can also enable it in your -`denote-directory' with `.dir-locals.el'. - - -1 Installation -══════════════ - - Denote-Refs isn't available on any ELPA right now. So, you have to - follow one of the following methods: - - -1.1 Quelpa -────────── - - ┌──── - │ (quelpa '(denote-refs - │ :fetcher git - │ :url "https://codeberg.org/akib/emacs-denote-refs.git")) - └──── - - -1.2 Straight.el -─────────────── - - ┌──── - │ (straight-use-package - │ '(denote-refs - │ :type git - │ :repo "https://codeberg.org/akib/emacs-denote-refs.git")) - └──── - - -1.3 Manual -────────── - - Download the `denote-refs.el' file and put it in your `load-path'. - You need to have Denote installed. blob - ad141abf9531e12ba670e9da3fe696013fbf6e69 (mode 644) blob + /dev/null --- elpa/denote-refs-0.1.2/README.org +++ /dev/null @@ -1,33 +0,0 @@ -#+title: Denote-Refs - Show links and backlinks in Denote notes - -Denote-Refs shows the list of linked file and backlinks to current -file. This list is shown just below the front matter of your note. -To enable do =M-x denote-refs-mode=. You can also enable it in your -~denote-directory~ with =.dir-locals.el=. - -* Installation - -Denote-Refs isn't available on any ELPA right now. So, you have to -follow one of the following methods: - -** Quelpa - -#+begin_src emacs-lisp -(quelpa '(denote-refs - :fetcher git - :url "https://codeberg.org/akib/emacs-denote-refs.git")) -#+end_src - -** Straight.el - -#+begin_src emacs-lisp -(straight-use-package - '(denote-refs - :type git - :repo "https://codeberg.org/akib/emacs-denote-refs.git")) -#+end_src - -** Manual - -Download the ~denote-refs.el~ file and put it in your ~load-path~. -You need to have Denote installed. blob - 7304adc4e6cc4274901e0b6990523bfc81269bd9 (mode 644) blob + /dev/null --- elpa/denote-refs-0.1.2/denote-refs-autoloads.el +++ /dev/null @@ -1,46 +0,0 @@ -;;; denote-refs-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from denote-refs.el - -(autoload 'denote-refs-mode "denote-refs" "\ -Toggle showing links and backlinks in Denote notes. - -This is a minor mode. If called interactively, toggle the -`Denote-Refs mode' mode. If the prefix argument is positive, -enable the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `denote-refs-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "denote-refs" '("denote-refs-")) - -;;; End of scraped data - -(provide 'denote-refs-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; denote-refs-autoloads.el ends here blob - d4ad26846fe34ac9d8abd9004b6fe5a4337fef21 (mode 644) blob + /dev/null --- elpa/denote-refs-0.1.2/denote-refs-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from denote-refs.el -*- no-byte-compile: t -*- -(define-package "denote-refs" "0.1.2" "Show links and backlinks in Denote notes" '((emacs "28.1") (denote "1.1.0")) :commit "9ae49c8770a83eed92c58be45a4c96fa927cbe6f" :authors '(("Akib Azmain Turja" . "akib@disroot.org")) :maintainer '("Akib Azmain Turja" . "akib@disroot.org") :keywords '("hypermedia" "outlines" "files") :url "https://codeberg.org/akib/emacs-denote-refs") blob - 4e54b432594dcae7d3aecee572419ebd626bdbce (mode 644) blob + /dev/null --- elpa/denote-refs-0.1.2/denote-refs.el +++ /dev/null @@ -1,340 +0,0 @@ -;;; denote-refs.el --- Show links and backlinks in Denote notes -*- lexical-binding: t; -*- - -;; Copyright (C) 2022 Akib Azmain Turja. - -;; Author: Akib Azmain Turja -;; Created: 2022-12-18 -;; Version: 0.1.2 -;; Package-Requires: ((emacs "28.1") (denote "1.1.0")) -;; Keywords: hypermedia, outlines, files -;; URL: https://codeberg.org/akib/emacs-denote-refs - -;; This file is not part of GNU Emacs. - -;; 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 program 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. - -;; For a full copy of the GNU General Public License -;; see . - -;;; Commentary: - -;; Denote-Refs shows the list of linked file and backlinks to current -;; file. This list is shown just below the front matter of your note. -;; To enable do M-x denote-refs-mode. You can also enable it in your -;; `denote-directory' with .dir-locals.el. - -;;; Code: - -(require 'denote) -(require 'subr-x) - -(defgroup denote-refs nil - "Show links and backlinks in Denote notes." - :group 'denote - :link '(url-link "https://codeberg.org/akib/emacs-denote-refs") - :prefix "denote-refs-") - -(defcustom denote-refs-update-delay '(0.2 1 60) - "Idle delay before updating reference lists. - -The value is a list of form (FIRST INIT MAINTAIN). FIRST the delay -before initializing the reference lists just after enabling the mode. -INIT the delay before initializing the reference lists for the first -time, used if the initialization was interrupted. MAINTAIN the delay -before updating the reference lists to keep the lists to updated." - :type '(list (number :tag "Delay after mode enabled") - (number :tag "Delay before initialization") - (number :tag "Delay after initialized"))) - -(defcustom denote-refs-sections '(links backlinks) - "The sections to show. - -Available sections are `links' and `backlinks', which shows the list -of linked file and the list of backlinks respectively." - :type '(repeat (choice (const :tag "Links" links) - (const :tag "Backlinks" backlinks)))) - -(defvar denote-refs--links 'not-ready - "Alist of linked files. - -The key is the path relative to user option `denote-directory', and -the key is the absolute path.") - -(defvar denote-refs--backlinks 'not-ready - "Alist of backlinks. - -The key is the path relative to user option `denote-directory', and -the key is the absolute path.") - -(defvar denote-refs--schedule-idle-update-timer nil - "Timer to schedule updating references while idle.") - -(defvar denote-refs--idle-update-timers nil - "Timer to update references while idle.") - -(defun denote-refs--render (section) - "Render SECTION." - (let ((refs (pcase section - ('links denote-refs--links) - ('backlinks denote-refs--backlinks)))) - (cond - ;; There's no comment syntax in `text-mode', so just follow - ;; `org-mode'. - ((derived-mode-p 'org-mode 'text-mode) - ;; Insert references count. - (insert (if (or (eq refs 'not-ready) - (eq refs 'error)) - (format "# ... %s\n" (if (eq section 'links) - "links" - "backlinks")) - (format "# %i %s%s\n" (length refs) - (if (eq section 'links) - "link" - "backlink") - (pcase (length refs) - (0 "") - (1 ":") - (_ "s:"))))) - ;; Insert reference list. - (when (listp refs) - (dolist (ref refs) - (insert "# ") - (insert-button (car ref) - 'help-echo (cdr ref) - 'face 'denote-faces-link - 'action (lambda (_) - (funcall denote-link-button-action - (cdr ref)))) - (insert ?\n)))) - ((derived-mode-p 'markdown-mode) - ;; Insert references count. - (insert (if (or (eq refs 'not-ready) - (eq refs 'error)) - (format "\n" (if (eq section 'links) - "links" - "backlinks")) - (format "") - (1 ":") - (_ "s:"))))) - ;; Insert reference list. - (when (listp refs) - (while refs - (let ((ref (pop refs))) - (insert " ") - (insert-button - (car ref) - 'help-echo (cdr ref) - 'face 'denote-faces-link - 'action (lambda (_) - (funcall denote-link-button-action - (cdr ref)))) - (unless refs - (insert " -->")) - (insert ?\n)))))))) - -(defun denote-refs--goto-end-of-front-matter () - "Go to the end of front matter of the note." - ;; All default front matters end with at least an empty line. But - ;; advanced users can change that. So we keep this code in separate - ;; function for them to advice. - (goto-char (point-min)) - (search-forward "\n\n")) - -(defun denote-refs--remove () - "Remove the references shown." - ;; We ignore errors, because `denote-refs--goto-end-of-front-matter' - ;; might fail. - (ignore-errors - (save-excursion - (denote-refs--goto-end-of-front-matter) - (when (get-text-property (point) 'denote-refs--sections) - (let ((end (or (next-single-property-change - (point) 'denote-refs--sections) - (point-max)))) - (when (< end (point-max)) - (setq end (1+ end))) - (let ((inhibit-read-only t)) - (with-silent-modifications - (delete-region (point) end)))))))) - -(defun denote-refs--show () - "Show references." - ;; We ignore errors, because `denote-refs--goto-end-of-front-matter' - ;; might fail. - (ignore-errors - (denote-refs--remove) - (save-excursion - (denote-refs--goto-end-of-front-matter) - (let ((begin (point)) - (inhibit-read-only t)) - (with-silent-modifications - (dolist (section denote-refs-sections) - (pcase-exhaustive section - ('links - (denote-refs--render 'links)) - ('backlinks - (denote-refs--render 'backlinks)))) - (put-text-property begin (point) 'read-only t) - (put-text-property begin (point) 'denote-refs--sections t) - (insert ?\n)))))) - -(defun denote-refs--make-path-relative (path) - "Return a cons of relative and absolute version of PATH. - -The car is PATH relative to user option `denote-directory'." - (cons (string-remove-prefix (denote-directory) path) path)) - -(defun denote-refs--fetch () - "Fetch reference information." - (dolist (section (seq-uniq denote-refs-sections)) - (pcase-exhaustive section - ('links - (setq denote-refs--links - (condition-case-unless-debug nil - (and (buffer-file-name) - (file-exists-p (buffer-file-name)) - (mapcar #'denote-refs--make-path-relative - (delete-dups - (denote-link--expand-identifiers - (denote--link-in-context-regexp - (denote-filetype-heuristics - (buffer-file-name))))))) - (error 'error)))) - ('backlinks - (setq denote-refs--backlinks - (condition-case-unless-debug nil - (and (buffer-file-name) - (file-exists-p (buffer-file-name)) - (mapcar - #'denote-refs--make-path-relative - (delete (buffer-file-name) - (denote--retrieve-files-in-xrefs - (denote-retrieve-filename-identifier - (buffer-file-name)))))) - (error 'error))))))) - -(defun denote-refs-update () - "Update Denote references shown." - (interactive) - (denote-refs--fetch) - (denote-refs--show)) - -(defun denote-refs--idle-update (buffer) - "Update Denote references shown on BUFFER, but don't block." - (when (buffer-live-p buffer) - (with-current-buffer buffer - (while-no-input - (denote-refs-update)) - (denote-refs--show)))) - -(defun denote-refs-update-all () - "Update Denote references shown on all buffers." - (interactive) - (dolist (buffer (buffer-list)) - (when (buffer-local-value 'denote-refs-mode buffer) - (with-current-buffer buffer - (denote-refs-update))))) - -(defun denote-refs--fix-xref--collect-matches (fn hit &rest args) - "Advice around `xref--collect-match' to ignore reference lists. - -FN is the original definition of `xref--collect-matches', HIT and ARGS -are it's arguments." - (let* ((file (cadr hit)) - (file (and file (concat xref--hits-remote-id file))) - (buf (xref--find-file-buffer file))) - (if (and buf (buffer-local-value 'denote-refs-mode buf)) - (progn - (with-current-buffer buf - (denote-refs--remove)) - (unwind-protect - (apply fn hit args) - (with-current-buffer buf - (denote-refs--show)))) - (apply fn hit args)))) - -(defun denote-refs--schedule-idle-update () - "Schedule updating Denote references shown." - (mapc #'cancel-timer denote-refs--idle-update-timers) - (setq denote-refs--idle-update-timers nil) - (and (eq (while-no-input - (dolist (buffer (buffer-list)) - (when (buffer-local-value 'denote-refs-mode buffer) - (with-current-buffer buffer - (push - (run-with-idle-timer - (if (or (eq denote-refs--links 'not-ready) - (eq denote-refs--backlinks 'not-ready)) - (cadr denote-refs-update-delay) - (caddr denote-refs-update-delay)) - nil #'denote-refs--idle-update buffer) - denote-refs--idle-update-timers)))) - 'finish) - 'finish) - (not denote-refs--idle-update-timers) - (progn - (advice-remove #'xref--collect-matches - #'denote-refs--fix-xref--collect-matches) - (cancel-timer denote-refs--schedule-idle-update-timer)))) - -(defun denote-refs--before-write-region (_ _) - "Make sure `write-region' doesn't write the reference lists." - (let ((buf (get-buffer-create " *denote-refs-tmp-write-region*")) - (str (buffer-string))) - (set-buffer buf) - (let ((inhibit-read-only t)) - (erase-buffer)) - (insert str) - (denote-refs--remove))) - -;;;###autoload -(define-minor-mode denote-refs-mode - "Toggle showing links and backlinks in Denote notes." - :lighter " Denote-Refs" - (let ((locals '(denote-refs--links denote-refs--backlinks))) - (if denote-refs-mode - (progn - (mapc #'make-local-variable locals) - (denote-refs--show) - (add-hook 'write-region-annotate-functions - #'denote-refs--before-write-region nil t) - (add-hook 'org-capture-prepare-finalize-hook - #'denote-refs--remove nil t) - ;; This runs just once, so we don't bother to keep track of - ;; it. ;) - (run-with-idle-timer - (car denote-refs-update-delay) nil - #'denote-refs--idle-update (current-buffer)) - (advice-add #'xref--collect-matches :around - #'denote-refs--fix-xref--collect-matches) - ;; This timer takes care of reverting the advice and also - ;; canceling the timer itself. - (when denote-refs--schedule-idle-update-timer - (cancel-timer denote-refs--schedule-idle-update-timer)) - (setq denote-refs--schedule-idle-update-timer - (run-with-idle-timer - (min (cadr denote-refs-update-delay) - (caddr denote-refs-update-delay)) - t #'denote-refs--schedule-idle-update))) - (denote-refs--remove) - (remove-hook 'before-save-hook #'denote-refs--remove t) - (remove-hook 'after-save-hook #'denote-refs--show t) - (remove-hook 'org-capture-prepare-finalize-hook - #'denote-refs--remove t) - (mapc #'kill-local-variable locals)))) - -(provide 'denote-refs) -;;; denote-refs.el ends here blob - 9cff66e3e8058f3e086e126bd7d313c0d8abaf78 (mode 644) blob + /dev/null --- elpa/denote-refs-0.1.2.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2023-01-15T23:05:08+0100 using RSA \ No newline at end of file blob - bce919da772baf30ec60a7a779ffc5d496f112cb (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/.dir-locals.el +++ /dev/null @@ -1,5 +0,0 @@ -((nil . ((indent-tabs-mode . nil) - (fill-column . 80) - (sentence-end-double-space . t) - (emacs-lisp-docstring-fill-column . 75))) - (makefile-mode . ((indent-tabs-mode . t)))) blob - fc72b335b0723077fb8a57d264e6cfce6032dbf2 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/.elpaignore +++ /dev/null @@ -1,2 +0,0 @@ -README.md -screenshot* blob - 94a9ed024d3859793618152ea559a168bbcbb5e2 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program 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 of the License, or - (at your option) any later version. - - This program 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 this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. blob - 4b7da8243a2e779dbf84083f55e55271e8e44ae8 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -EMACS ?= emacs -SOURCES=diff-hl.el -SOURCES+=diff-hl-amend.el -SOURCES+=diff-hl-dired.el -SOURCES+=diff-hl-flydiff.el -SOURCES+=diff-hl-inline-popup.el -SOURCES+=diff-hl-margin.el -SOURCES+=diff-hl-show-hunk-posframe.el -SOURCES+=diff-hl-show-hunk.el - -ARTIFACTS=$(patsubst %.el, %.elc, $(SOURCES)) - -RM ?= rm -f - -all: compile test - -test: - $(EMACS) -batch -L . -l test/diff-hl-test.el -f diff-hl-run-tests - -compile: - $(EMACS) -batch -L . -f batch-byte-compile $(SOURCES) - -clean: - $(RM) $(ARTIFACTS) - -.PHONY: all test compile plain clean blob - f05686ec8b00f8d23e4ed7eda731d2bb579d3f4f (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl-amend.el +++ /dev/null @@ -1,69 +0,0 @@ -;; Copyright (C) 2012-2013, 2020 Free Software Foundation, Inc. -*- lexical-binding: t -*- - -;; Author: Dmitry Gutov -;; URL: https://github.com/dgutov/diff-hl - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;; Toggle in the current buffer with `M-x diff-hl-amend-mode'. -;; Toggle in all buffers with `M-x global-diff-hl-amend-mode'. - -;;; Code: - -(require 'diff-hl) - -;;;###autoload -(define-minor-mode diff-hl-amend-mode - "Show changes against the second-last revision in `diff-hl-mode'. -Most useful with backends that support rewriting local commits, -and most importantly, \"amending\" the most recent one. -Currently only supports Git, Mercurial and Bazaar." - :lighter " Amend" - (if diff-hl-amend-mode - (progn - (diff-hl-amend-setup) - (add-hook 'after-revert-hook 'diff-hl-amend-setup nil t)) - (remove-hook 'after-revert-hook 'diff-hl-amend-setup t) - (kill-local-variable 'diff-hl-reference-revision)) - (when diff-hl-mode - (diff-hl-update))) - -(defun diff-hl-amend-setup () - (let ((backend (vc-backend buffer-file-name))) - (when backend - (setq-local diff-hl-reference-revision - (cl-case backend - (Git - "HEAD^") - (Hg - "-2") - (Bzr - "revno:-2")))))) - -;;;###autoload -(define-globalized-minor-mode global-diff-hl-amend-mode diff-hl-amend-mode - turn-on-diff-hl-amend-mode - :group 'diff-hl) - -(defun turn-on-diff-hl-amend-mode () - "Turn on `diff-hl-amend-mode' in a buffer if appropriate." - (and buffer-file-name (diff-hl-amend-mode 1))) - -(provide 'diff-hl-amend) - -;;; diff-hl-amend.el ends here blob - 0268da00fb16504a97a6326ded7b1ed837543199 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl-autoloads.el +++ /dev/null @@ -1,348 +0,0 @@ -;;; diff-hl-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from diff-hl.el - -(autoload 'diff-hl-mode "diff-hl" "\ -Toggle VC diff highlighting. - -This is a minor mode. If called interactively, toggle the -`Diff-Hl mode' mode. If the prefix argument is positive, enable -the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `diff-hl-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(autoload 'turn-on-diff-hl-mode "diff-hl" "\ -Turn on `diff-hl-mode' or `diff-hl-dir-mode' in a buffer if appropriate.") -(autoload 'diff-hl--global-turn-on "diff-hl" "\ -Call `turn-on-diff-hl-mode' if the current major mode is applicable.") -(autoload 'diff-hl-set-reference-rev "diff-hl" "\ -Set the reference revision globally to REV. -When called interactively, REV read with completion. - -The default value chosen using one of methods below: - -- In a log view buffer, it uses the revision of current entry. -Call `vc-print-log' or `vc-print-root-log' first to open a log -view buffer. -- In a VC annotate buffer, it uses the revision of current line. -- In other situations, it uses the symbol at point. - -Notice that this sets the reference revision globally, so in -files from other repositories, `diff-hl-mode' will not highlight -changes correctly, until you run `diff-hl-reset-reference-rev'. - -Also notice that this will disable `diff-hl-amend-mode' in -buffers that enables it, since `diff-hl-amend-mode' overrides its -effect. - -(fn REV)" t) -(autoload 'diff-hl-reset-reference-rev "diff-hl" "\ -Reset the reference revision globally to the most recent one." t) -(put 'global-diff-hl-mode 'globalized-minor-mode t) -(defvar global-diff-hl-mode nil "\ -Non-nil if Global Diff-Hl mode is enabled. -See the `global-diff-hl-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `global-diff-hl-mode'.") -(custom-autoload 'global-diff-hl-mode "diff-hl" nil) -(autoload 'global-diff-hl-mode "diff-hl" "\ -Toggle Diff-Hl mode in all buffers. -With prefix ARG, enable Global Diff-Hl mode if ARG is positive; -otherwise, disable it. - -If called from Lisp, toggle the mode if ARG is `toggle'. -Enable the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -Diff-Hl mode is enabled in all buffers where `diff-hl--global-turn-on' -would do it. - -See `diff-hl-mode' for more information on Diff-Hl mode. - -(fn &optional ARG)" t) -(register-definition-prefixes "diff-hl" '("diff-hl-")) - - -;;; Generated autoloads from diff-hl-amend.el - -(autoload 'diff-hl-amend-mode "diff-hl-amend" "\ -Show changes against the second-last revision in `diff-hl-mode'. - -Most useful with backends that support rewriting local commits, -and most importantly, \"amending\" the most recent one. -Currently only supports Git, Mercurial and Bazaar. - -This is a minor mode. If called interactively, toggle the -`Diff-Hl-Amend mode' mode. If the prefix argument is positive, -enable the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `diff-hl-amend-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(put 'global-diff-hl-amend-mode 'globalized-minor-mode t) -(defvar global-diff-hl-amend-mode nil "\ -Non-nil if Global Diff-Hl-Amend mode is enabled. -See the `global-diff-hl-amend-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `global-diff-hl-amend-mode'.") -(custom-autoload 'global-diff-hl-amend-mode "diff-hl-amend" nil) -(autoload 'global-diff-hl-amend-mode "diff-hl-amend" "\ -Toggle Diff-Hl-Amend mode in all buffers. -With prefix ARG, enable Global Diff-Hl-Amend mode if ARG is positive; -otherwise, disable it. - -If called from Lisp, toggle the mode if ARG is `toggle'. -Enable the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -Diff-Hl-Amend mode is enabled in all buffers where -`turn-on-diff-hl-amend-mode' would do it. - -See `diff-hl-amend-mode' for more information on Diff-Hl-Amend mode. - -(fn &optional ARG)" t) -(register-definition-prefixes "diff-hl-amend" '("diff-hl-amend-setup" "turn-on-diff-hl-amend-mode")) - - -;;; Generated autoloads from diff-hl-dired.el - -(autoload 'diff-hl-dired-mode "diff-hl-dired" "\ -Toggle VC diff highlighting on the side of a Dired window. - -This is a minor mode. If called interactively, toggle the -`Diff-Hl-Dired mode' mode. If the prefix argument is positive, -enable the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `diff-hl-dired-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(autoload 'diff-hl-dired-mode-unless-remote "diff-hl-dired") -(register-definition-prefixes "diff-hl-dired" '("diff-hl-dired-")) - - -;;; Generated autoloads from diff-hl-flydiff.el - -(defvar diff-hl-flydiff-mode nil "\ -Non-nil if Diff-Hl-Flydiff mode is enabled. -See the `diff-hl-flydiff-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `diff-hl-flydiff-mode'.") -(custom-autoload 'diff-hl-flydiff-mode "diff-hl-flydiff" nil) -(autoload 'diff-hl-flydiff-mode "diff-hl-flydiff" "\ -Perform highlighting on-the-fly. - -This is a global minor mode. It alters how `diff-hl-mode' works. - -This is a global minor mode. If called interactively, toggle the -`Diff-Hl-Flydiff mode' mode. If the prefix argument is positive, -enable the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='diff-hl-flydiff-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "diff-hl-flydiff" '("diff-hl-flydiff")) - - -;;; Generated autoloads from diff-hl-inline-popup.el - -(autoload 'diff-hl-inline-popup-hide "diff-hl-inline-popup" "\ -Hide the current inline popup." t) -(autoload 'diff-hl-inline-popup-show "diff-hl-inline-popup" "\ -Create a phantom overlay to show the inline popup, with some -content LINES, and a HEADER and a FOOTER, at POINT. KEYMAP is -added to the current keymaps. CLOSE-HOOK is called when the popup -is closed. - -(fn LINES &optional HEADER FOOTER KEYMAP CLOSE-HOOK POINT HEIGHT)") -(register-definition-prefixes "diff-hl-inline-popup" '("diff-hl-inline-popup-")) - - -;;; Generated autoloads from diff-hl-margin.el - -(defvar diff-hl-margin-mode nil "\ -Non-nil if Diff-Hl-Margin mode is enabled. -See the `diff-hl-margin-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `diff-hl-margin-mode'.") -(custom-autoload 'diff-hl-margin-mode "diff-hl-margin" nil) -(autoload 'diff-hl-margin-mode "diff-hl-margin" "\ -Toggle displaying `diff-hl-mode' highlights on the margin. - -This is a global minor mode. If called interactively, toggle the -`Diff-Hl-Margin mode' mode. If the prefix argument is positive, -enable the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='diff-hl-margin-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(autoload 'diff-hl-margin-local-mode "diff-hl-margin" "\ -Toggle displaying `diff-hl-mode' highlights on the margin locally. - -You probably shouldn't use this function directly. - -This is a minor mode. If called interactively, toggle the -`Diff-Hl-Margin-Local mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `diff-hl-margin-local-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "diff-hl-margin" '("diff-hl-")) - - -;;; Generated autoloads from diff-hl-show-hunk.el - -(autoload 'diff-hl-show-hunk-inline-popup "diff-hl-show-hunk" "\ -Implementation to show the hunk in a inline popup. -BUFFER is a buffer with the hunk. - -(fn BUFFER &optional IGNORED-LINE)") -(autoload 'diff-hl-show-hunk-previous "diff-hl-show-hunk" "\ -Go to previous hunk/change and show it." t) -(autoload 'diff-hl-show-hunk-next "diff-hl-show-hunk" "\ -Go to next hunk/change and show it." t) -(autoload 'diff-hl-show-hunk "diff-hl-show-hunk" "\ -Show the VC diff hunk at point. -The backend is determined by `diff-hl-show-hunk-function'." t) -(autoload 'diff-hl-show-hunk-mouse-mode "diff-hl-show-hunk" "\ -Enables the margin and fringe to show a posframe/popup with vc diffs when clicked. - -By default, the popup shows only the current hunk, and -the line of the hunk that matches the current position is -highlighted. The face, border and other visual preferences are -customizable. It can be also invoked with the command -`diff-hl-show-hunk' -\\{diff-hl-show-hunk-mouse-mode-map} - -This is a minor mode. If called interactively, toggle the -`Diff-Hl-Show-Hunk-Mouse mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `diff-hl-show-hunk-mouse-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(put 'global-diff-hl-show-hunk-mouse-mode 'globalized-minor-mode t) -(defvar global-diff-hl-show-hunk-mouse-mode nil "\ -Non-nil if Global Diff-Hl-Show-Hunk-Mouse mode is enabled. -See the `global-diff-hl-show-hunk-mouse-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `global-diff-hl-show-hunk-mouse-mode'.") -(custom-autoload 'global-diff-hl-show-hunk-mouse-mode "diff-hl-show-hunk" nil) -(autoload 'global-diff-hl-show-hunk-mouse-mode "diff-hl-show-hunk" "\ -Toggle Diff-Hl-Show-Hunk-Mouse mode in all buffers. -With prefix ARG, enable Global Diff-Hl-Show-Hunk-Mouse mode if ARG is -positive; otherwise, disable it. - -If called from Lisp, toggle the mode if ARG is `toggle'. -Enable the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -Diff-Hl-Show-Hunk-Mouse mode is enabled in all buffers where -`diff-hl-show-hunk-mouse-mode' would do it. - -See `diff-hl-show-hunk-mouse-mode' for more information on -Diff-Hl-Show-Hunk-Mouse mode. - -(fn &optional ARG)" t) -(register-definition-prefixes "diff-hl-show-hunk" '("diff-hl-show-hunk-")) - - -;;; Generated autoloads from diff-hl-show-hunk-posframe.el - -(autoload 'diff-hl-show-hunk-posframe "diff-hl-show-hunk-posframe" "\ -Implementation to show the hunk in a posframe. - -(fn BUFFER &optional LINE)") -(register-definition-prefixes "diff-hl-show-hunk-posframe" '("diff-hl-show-hunk-")) - -;;; End of scraped data - -(provide 'diff-hl-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; diff-hl-autoloads.el ends here blob - 06f1d6fab03caa155d300b10208afc19ffa0c730 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl-dired.el +++ /dev/null @@ -1,185 +0,0 @@ -;;; diff-hl-dired.el --- Highlight changed files in Dired -*- lexical-binding: t -*- - -;; Copyright (C) 2012-2017, 2023 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;; To enable in all Dired buffers, add this to your init file: -;; -;; (add-hook 'dired-mode-hook 'diff-hl-dired-mode) -;; -;; or -;; -;; (add-hook 'dired-mode-hook 'diff-hl-dired-mode-unless-remote) -;; -;; to do it only in local Dired buffers. - -;;; Code: - -(require 'diff-hl) -(require 'dired) -(require 'vc-hooks) - -(defvar diff-hl-dired-process-buffer nil) - -(defgroup diff-hl-dired nil - "VC diff highlighting on the side of a Dired window." - :group 'diff-hl) - -(defface diff-hl-dired-insert - '((default :inherit diff-hl-insert)) - "Face used to highlight added files.") - -(defface diff-hl-dired-delete - '((default :inherit diff-hl-delete)) - "Face used to highlight directories with deleted files.") - -(defface diff-hl-dired-change - '((default :inherit diff-hl-change)) - "Face used to highlight changed files.") - -(defface diff-hl-dired-unknown - '((default :inherit dired-ignored)) - "Face used to highlight unregistered files.") - -(defface diff-hl-dired-ignored - '((default :inherit dired-ignored)) - "Face used to highlight unregistered files.") - -(defcustom diff-hl-dired-extra-indicators t - "Non-nil to indicate ignored files." - :type 'boolean) - -(defcustom diff-hl-dired-ignored-backends '(RCS) - "VC backends to ignore. -The directories registered to one of these backends won't have -status indicators." - :type `(repeat (choice ,@(mapcar - (lambda (name) - `(const :tag ,(symbol-name name) ,name)) - vc-handled-backends)))) - -;;;###autoload -(define-minor-mode diff-hl-dired-mode - "Toggle VC diff highlighting on the side of a Dired window." - :lighter "" - (if diff-hl-dired-mode - (progn - (diff-hl-maybe-define-bitmaps) - (set (make-local-variable 'diff-hl-dired-process-buffer) nil) - (add-hook 'dired-after-readin-hook 'diff-hl-dired-update nil t)) - (remove-hook 'dired-after-readin-hook 'diff-hl-dired-update t) - (diff-hl-dired-clear))) - -(defun diff-hl-dired-update () - "Highlight the Dired buffer." - (let ((backend (ignore-errors (vc-responsible-backend default-directory))) - (def-dir default-directory) - (buffer (current-buffer)) - dirs-alist files-alist) - (when (and backend (not (memq backend diff-hl-dired-ignored-backends))) - (diff-hl-dired-clear) - (if (buffer-live-p diff-hl-dired-process-buffer) - (let ((proc (get-buffer-process diff-hl-dired-process-buffer))) - (when proc (kill-process proc))) - (setq diff-hl-dired-process-buffer - (generate-new-buffer " *diff-hl-dired* tmp status"))) - (with-current-buffer diff-hl-dired-process-buffer - (setq default-directory (expand-file-name def-dir)) - (erase-buffer) - (diff-hl-dired-status-files - backend def-dir - (when diff-hl-dired-extra-indicators - (cl-loop for file in (directory-files def-dir) - unless (member file '("." ".." ".hg")) - collect file)) - (lambda (entries &optional more-to-come) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (dolist (entry entries) - (cl-destructuring-bind (file state &rest r) entry - ;; Work around http://debbugs.gnu.org/18605 - (setq file (replace-regexp-in-string "\\` " "" file)) - (let ((type (plist-get - '( edited change added insert removed delete - unregistered unknown ignored ignored) - state))) - (if (string-match "\\`\\([^/]+\\)/" file) - (let* ((dir (match-string 1 file)) - (value (cdr (assoc dir dirs-alist)))) - (unless (eq value type) - (cond - ((eq state 'up-to-date)) - ((null value) - (push (cons dir type) dirs-alist)) - ((not (eq type 'ignored)) - (setcdr (assoc dir dirs-alist) 'change))))) - (push (cons file type) files-alist))))) - (unless more-to-come - (diff-hl-dired-highlight-items - (append dirs-alist files-alist)))) - (unless more-to-come - (kill-buffer diff-hl-dired-process-buffer)))) - ))))) - -(defun diff-hl-dired-status-files (backend dir files update-function) - "Using version control BACKEND, return list of (FILE STATE EXTRA) entries -for DIR containing FILES. Call UPDATE-FUNCTION as entries are added." - (if (version< "25" emacs-version) - (vc-call-backend backend 'dir-status-files dir files update-function) - (vc-call-backend backend 'dir-status-files dir files nil update-function))) - -(when (version< emacs-version "24.4.51.5") - ;; Work around http://debbugs.gnu.org/19386 - (defadvice vc-git-dir-status-goto-stage (around - diff-hl-dired-skip-up-to-date - (stage files update-function) - activate) - (when (eq stage 'ls-files-up-to-date) - (setq stage 'diff-index)) - ad-do-it)) - -(defun diff-hl-dired-highlight-items (alist) - "Highlight ALIST containing (FILE . TYPE) elements." - (dolist (pair alist) - (let ((file (car pair)) - (type (cdr pair))) - (save-excursion - (goto-char (point-min)) - (when (and type (dired-goto-file-1 - file (expand-file-name file) nil)) - (let* ((diff-hl-fringe-bmp-function 'diff-hl-fringe-bmp-from-type) - (diff-hl-fringe-face-function 'diff-hl-dired-face-from-type) - (o (diff-hl-add-highlighting type 'single))) - (overlay-put o 'modification-hooks '(diff-hl-overlay-modified)) - (overlay-put o 'diff-hl-dired-type type) - )))))) - -(defun diff-hl-dired-face-from-type (type _pos) - (intern (format "diff-hl-dired-%s" type))) - -(defalias 'diff-hl-dired-clear 'diff-hl-remove-overlays) - -;;;###autoload -(defun diff-hl-dired-mode-unless-remote () - (unless (file-remote-p default-directory) - (diff-hl-dired-mode))) - -(provide 'diff-hl-dired) - -;;; diff-hl-dired.el ends here blob - bb3d47748b70829e60a4c24bf93427f3aa7b2d0f (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl-flydiff.el +++ /dev/null @@ -1,83 +0,0 @@ -;; Copyright (C) 2015-2021 Free Software Foundation, Inc. -*- lexical-binding: t -*- - -;; Author: Jonathan Hayase -;; URL: https://github.com/dgutov/diff-hl - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;; This mode enables diffing on-the-fly (i.e. without saving the buffer first) -;; Toggle in all buffers with M-x diff-hl-flydiff-mode - -;;; Code: - -(require 'diff-hl) -(require 'diff) - -(defgroup diff-hl-flydiff nil - "Highlight changes on the fly" - :group 'diff-hl) - -(defcustom diff-hl-flydiff-delay 0.3 - "The idle delay in seconds before highlighting is updated." - :type 'number) - -(defvar diff-hl-flydiff-modified-tick nil) -(defvar diff-hl-flydiff-timer nil) -(make-variable-buffer-local 'diff-hl-flydiff-modified-tick) - -(defun diff-hl-flydiff-changes-buffer (file &optional backend) - (setq diff-hl-flydiff-modified-tick (buffer-chars-modified-tick)) - (diff-hl-diff-buffer-with-reference file " *diff-hl-diff*" backend)) - -(defun diff-hl-flydiff-update () - (unless (or - (not diff-hl-mode) - (eq diff-hl-flydiff-modified-tick (buffer-chars-modified-tick)) - (not buffer-file-name) - (file-remote-p default-directory) - (not (file-exists-p buffer-file-name))) - (diff-hl-update))) - -(defun diff-hl-flydiff/modified-p (_state) - (buffer-modified-p)) - -;;;###autoload -(define-minor-mode diff-hl-flydiff-mode - "Perform highlighting on-the-fly. -This is a global minor mode. It alters how `diff-hl-mode' works." - :lighter "" :global t - (if diff-hl-flydiff-mode - (progn - (advice-add 'diff-hl-overlay-modified :override #'ignore) - - (advice-add 'diff-hl-modified-p :before-until - #'diff-hl-flydiff/modified-p) - (advice-add 'diff-hl-changes-buffer :override - #'diff-hl-flydiff-changes-buffer) - (setq diff-hl-flydiff-timer - (run-with-idle-timer diff-hl-flydiff-delay t #'diff-hl-flydiff-update))) - - (advice-remove 'diff-hl-overlay-modified #'ignore) - - (advice-remove 'diff-hl-modified-p #'diff-hl-flydiff/modified-p) - (advice-remove 'diff-hl-changes-buffer #'diff-hl-flydiff-changes-buffer) - - (and diff-hl-flydiff-timer - (cancel-timer diff-hl-flydiff-timer)))) - -(provide 'diff-hl-flydiff) blob - bee6ce10e35c9ac3ace2f358342771a43cc7ea0f (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl-inline-popup.el +++ /dev/null @@ -1,284 +0,0 @@ -;;; diff-hl-inline-popup.el --- inline popup using phantom overlays -*- lexical-binding: t -*- - -;; Copyright (C) 2020-2021 Free Software Foundation, Inc. - -;; Author: Álvaro González - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: -;; Shows inline popups using phantom overlays. The lines of the popup -;; can be scrolled. -;;; Code: - -(require 'subr-x) - -(defvar diff-hl-inline-popup--current-popup nil "The overlay of the current inline popup.") -(defvar diff-hl-inline-popup--current-lines nil "A list of the lines to show in the popup.") -(defvar diff-hl-inline-popup--current-index nil "First line showed in popup.") -(defvar diff-hl-inline-popup--invokinkg-command nil "Command that invoked the popup.") -(defvar diff-hl-inline-popup--current-footer nil "String to be displayed in the footer.") -(defvar diff-hl-inline-popup--current-header nil "String to be displayed in the header.") -(defvar diff-hl-inline-popup--height nil "Height of the popup.") -(defvar diff-hl-inline-popup--current-custom-keymap nil "Keymap to be added to the keymap of the inline popup.") -(defvar diff-hl-inline-popup--close-hook nil "Function to be called when the popup closes.") - -(make-variable-buffer-local 'diff-hl-inline-popup--current-popup) -(make-variable-buffer-local 'diff-hl-inline-popup--current-lines) -(make-variable-buffer-local 'diff-hl-inline-popup--current-index) -(make-variable-buffer-local 'diff-hl-inline-popup--current-header) -(make-variable-buffer-local 'diff-hl-inline-popup--current-footer) -(make-variable-buffer-local 'diff-hl-inline-popup--invokinkg-command) -(make-variable-buffer-local 'diff-hl-inline-popup--current-custom-keymap) -(make-variable-buffer-local 'diff-hl-inline-popup--height) -(make-variable-buffer-local 'diff-hl-inline-popup--close-hook) - -(defun diff-hl-inline-popup--splice (list offset length) - "Compute a sublist of LIST starting at OFFSET, of LENGTH." - (butlast - (nthcdr offset list) - (- (length list) length offset))) - -(defun diff-hl-inline-popup--ensure-enough-lines (pos content-height) - "Ensure there is enough lines below POS to show the inline popup with CONTENT-HEIGHT height." - (let* ((line (line-number-at-pos pos)) - (end (line-number-at-pos (window-end nil t))) - (height (+ 6 content-height)) - (overflow (- (+ line height) end))) - (when (< 0 overflow) - (run-with-timer 0.1 nil #'scroll-up overflow)))) - -(defun diff-hl-inline-popup--compute-content-height (&optional content-size) - "Compute the height of the inline popup. -Default for CONTENT-SIZE is the size of the current lines" - (let ((content-size (or content-size (length diff-hl-inline-popup--current-lines))) - (max-size (- (/(window-height) 2) 3))) - (min content-size max-size))) - -(defun diff-hl-inline-popup--compute-content-lines (lines index window-size) - "Compute the lines to show in the popup, from LINES starting at INDEX with a WINDOW-SIZE." - (let* ((len (length lines)) - (window-size (min window-size len)) - (index (min index (- len window-size)))) - (diff-hl-inline-popup--splice lines index window-size))) - -(defun diff-hl-inline-popup--compute-header (width &optional header) - "Compute the header of the popup, with some WIDTH, and some optional HEADER text." - (let* ((scroll-indicator (if (eq diff-hl-inline-popup--current-index 0) " " " ⬆ ")) - (header (or header "")) - (new-width (- width (length header) (length scroll-indicator))) - (header (if (< new-width 0) "" header)) - (new-width (- width (length header) (length scroll-indicator))) - (line (propertize (concat (diff-hl-inline-popup--separator new-width) - header scroll-indicator ) - 'face '(:underline t)))) - (concat line "\n") )) - -(defun diff-hl-inline-popup--compute-footer (width &optional footer) - "Compute the header of the popup, with some WIDTH, and some optional FOOTER text." - (let* ((scroll-indicator (if (>= diff-hl-inline-popup--current-index - (- (length diff-hl-inline-popup--current-lines) - diff-hl-inline-popup--height)) - " " - " ⬇ ")) - (footer (or footer "")) - (new-width (- width (length footer) (length scroll-indicator))) - (footer (if (< new-width 0) "" footer)) - (new-width (- width (length footer) (length scroll-indicator))) - (blank-line (if (display-graphic-p) - "" - (concat "\n" (propertize (diff-hl-inline-popup--separator width) - 'face '(:underline t))))) - (line (propertize (concat (diff-hl-inline-popup--separator new-width) - footer scroll-indicator) - 'face '(:overline t)))) - (concat blank-line "\n" line))) - -(defun diff-hl-inline-popup--separator (width &optional sep) - "Return the horizontal separator with character SEP and a WIDTH." - (let ((sep (or sep ?\s))) - (make-string width sep))) - -(defun diff-hl-inline-popup--available-width () - "Compute the available width in chars." - (let ((magic-adjust 3)) - (if (not (display-graphic-p)) - (let* ((linumber-width (line-number-display-width nil)) - (width (- (window-body-width) linumber-width magic-adjust))) - width) - (let* ((font-width (window-font-width)) - (window-width (window-body-width nil t)) - (linenumber-width (line-number-display-width t)) - (available-pixels (- window-width linenumber-width)) - (width (- (/ available-pixels font-width) magic-adjust))) - - ;; https://emacs.stackexchange.com/questions/5495/how-can-i-determine-the-width-of-characters-on-the-screen - width)))) - -(defun diff-hl-inline-popup--compute-popup-str (lines index window-size header footer) - "Compute the string that represents the popup. -There are some content LINES starting at INDEX, with a WINDOW-SIZE. HEADER and -FOOTER are showed at start and end." - (let* ((width (diff-hl-inline-popup--available-width)) - (content-lines (diff-hl-inline-popup--compute-content-lines lines index window-size)) - (header (diff-hl-inline-popup--compute-header width header)) - (footer (diff-hl-inline-popup--compute-footer width footer))) - (concat header (string-join content-lines "\n") footer "\n"))) - -(defun diff-hl-inline-popup-scroll-to (index) - "Scroll the inline popup to make visible the line at position INDEX." - (when diff-hl-inline-popup--current-popup - (setq diff-hl-inline-popup--current-index (max 0 (min index (- (length diff-hl-inline-popup--current-lines) diff-hl-inline-popup--height)))) - (let* ((str (diff-hl-inline-popup--compute-popup-str - diff-hl-inline-popup--current-lines - diff-hl-inline-popup--current-index - diff-hl-inline-popup--height - diff-hl-inline-popup--current-header - diff-hl-inline-popup--current-footer))) - ;; https://debbugs.gnu.org/38563, `company--replacement-string'. - (add-face-text-property 0 (length str) 'default t str) - (overlay-put diff-hl-inline-popup--current-popup 'after-string str)))) - -(defun diff-hl-inline-popup--popup-down() - "Scrolls one line down." - (interactive) - (diff-hl-inline-popup-scroll-to (1+ diff-hl-inline-popup--current-index) )) - -(defun diff-hl-inline-popup--popup-up() - "Scrolls one line up." - (interactive) - (diff-hl-inline-popup-scroll-to (1- diff-hl-inline-popup--current-index) )) - -(defun diff-hl-inline-popup--popup-pagedown() - "Scrolls one page down." - (interactive) - (diff-hl-inline-popup-scroll-to (+ diff-hl-inline-popup--current-index diff-hl-inline-popup--height) )) - -(defun diff-hl-inline-popup--popup-pageup() - "Scrolls one page up." - (interactive) - (diff-hl-inline-popup-scroll-to (- diff-hl-inline-popup--current-index diff-hl-inline-popup--height) )) - -(defvar diff-hl-inline-popup-transient-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "") #'diff-hl-inline-popup--popup-pageup) - (define-key map (kbd "M-v") #'diff-hl-inline-popup--popup-pageup) - (define-key map (kbd "") #'diff-hl-inline-popup--popup-pagedown) - (define-key map (kbd "C-v") #'diff-hl-inline-popup--popup-pagedown) - (define-key map (kbd "") #'diff-hl-inline-popup--popup-up) - (define-key map (kbd "C-p") #'diff-hl-inline-popup--popup-up) - (define-key map (kbd "") #'diff-hl-inline-popup--popup-down) - (define-key map (kbd "C-n") #'diff-hl-inline-popup--popup-down) - (define-key map (kbd "C-g") #'diff-hl-inline-popup-hide) - (define-key map [escape] #'diff-hl-inline-popup-hide) - (define-key map (kbd "q") #'diff-hl-inline-popup-hide) - ;;http://ergoemacs.org/emacs/emacs_mouse_wheel_config.html - (define-key map (kbd "") #'diff-hl-inline-popup--popup-up) - (define-key map (kbd "") #'diff-hl-inline-popup--popup-up) - (define-key map (kbd "") #'diff-hl-inline-popup--popup-down) - (define-key map (kbd "") #'diff-hl-inline-popup--popup-down) - map) - "Keymap for command `diff-hl-inline-popup-transient-mode'. -Capture all the vertical movement of the point, and converts it -to scroll in the popup") - -(defun diff-hl-inline-popup--ignorable-command-p (command) - "Decide if COMMAND is a command allowed while showing an inline popup." - ;; https://emacs.stackexchange.com/questions/653/how-can-i-find-out-in-which-keymap-a-key-is-bound - (let ((keys (where-is-internal command (list diff-hl-inline-popup--current-custom-keymap - diff-hl-inline-popup-transient-mode-map ) t)) - (invoking (eq command diff-hl-inline-popup--invokinkg-command))) - (or keys invoking))) - -(defun diff-hl-inline-popup--post-command-hook () - "Called each time a command is executed." - (let ((allowed-command (or - (string-match-p "diff-hl-inline-popup-" (symbol-name this-command)) - (diff-hl-inline-popup--ignorable-command-p this-command)))) - (unless allowed-command - (diff-hl-inline-popup-hide)))) - -(define-minor-mode diff-hl-inline-popup-transient-mode - "Temporal minor mode to control an inline popup" - :global nil - (remove-hook 'post-command-hook #'diff-hl-inline-popup--post-command-hook t) - (set-keymap-parent diff-hl-inline-popup-transient-mode-map nil) - - (when diff-hl-inline-popup-transient-mode - (set-keymap-parent diff-hl-inline-popup-transient-mode-map - diff-hl-inline-popup--current-custom-keymap) - (add-hook 'post-command-hook #'diff-hl-inline-popup--post-command-hook 0 t))) - -;;;###autoload -(defun diff-hl-inline-popup-hide() - "Hide the current inline popup." - (interactive) - (when diff-hl-inline-popup-transient-mode - (diff-hl-inline-popup-transient-mode -1)) - (when diff-hl-inline-popup--close-hook - (funcall diff-hl-inline-popup--close-hook) - (setq diff-hl-inline-popup--close-hook nil)) - (when diff-hl-inline-popup--current-popup - (delete-overlay diff-hl-inline-popup--current-popup) - (setq diff-hl-inline-popup--current-popup nil))) - -;;;###autoload -(defun diff-hl-inline-popup-show (lines &optional header footer keymap close-hook point height) - "Create a phantom overlay to show the inline popup, with some -content LINES, and a HEADER and a FOOTER, at POINT. KEYMAP is -added to the current keymaps. CLOSE-HOOK is called when the popup -is closed." - (when diff-hl-inline-popup--current-popup - (delete-overlay diff-hl-inline-popup--current-popup) - (setq diff-hl-inline-popup--current-popup nil)) - - (when (< (diff-hl-inline-popup--compute-content-height 99) 2) - (user-error "There is no enough vertical space to show the inline popup")) - (let* ((the-point (or point (line-end-position))) - (the-buffer (current-buffer)) - (overlay (make-overlay the-point the-point the-buffer))) - (overlay-put overlay 'phantom t) - (overlay-put overlay 'diff-hl-inline-popup t) - (setq diff-hl-inline-popup--current-popup overlay) - - (setq diff-hl-inline-popup--current-lines - (mapcar (lambda (s) (replace-regexp-in-string "\n" " " s)) lines)) - (setq diff-hl-inline-popup--current-header header) - (setq diff-hl-inline-popup--current-footer footer) - (setq diff-hl-inline-popup--invokinkg-command this-command) - (setq diff-hl-inline-popup--current-custom-keymap keymap) - (setq diff-hl-inline-popup--close-hook close-hook) - (setq diff-hl-inline-popup--height (diff-hl-inline-popup--compute-content-height height)) - (setq diff-hl-inline-popup--height (min diff-hl-inline-popup--height - (length diff-hl-inline-popup--current-lines))) - ;; (diff-hl-inline-popup--ensure-enough-lines point diff-hl-inline-popup--height) - (diff-hl-inline-popup-transient-mode 1) - (diff-hl-inline-popup-scroll-to 0) - overlay)) - -(defun diff-hl-inline-popup--hide-all () - "Testing purposes, use in case some inline popups get stuck in a buffer." - (interactive) - (when diff-hl-inline-popup-transient-mode - (diff-hl-inline-popup-transient-mode -1)) - (setq diff-hl-inline-popup--current-popup nil) - (let* ((all-overlays (overlays-in (point-min) (point-max))) - (overlays (cl-remove-if-not (lambda (o)(overlay-get o 'diff-hl-inline-popup)) all-overlays))) - (dolist (o overlays) - (delete-overlay o)))) - -(provide 'diff-hl-inline-popup) -;;; diff-hl-inline-popup ends here blob - c3fffde14e25f9287623f0fb4f34f23cf2f13d32 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl-margin.el +++ /dev/null @@ -1,157 +0,0 @@ -;;; diff-hl-margin.el --- Highlight buffer changes on margins -*- lexical-binding: t -*- - -;; Copyright (C) 2012-2017 Free Software Foundation, Inc. - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;; This is a global mode, it modifies `diff-hl-mode' to use the margin -;; instead of the fringe. To toggle, type `M-x diff-hl-margin-mode'. -;; -;; Compared to the default behavior, this makes `diff-hl-mode' -;; indicators show up even when Emacs is running in a terminal. -;; -;; On the flip side, the indicators look simpler, and they are -;; incompatible with `linum-mode' or any other mode that uses the -;; margin. -;; -;; You might want to enable it conditionally in your init file -;; depending on whether Emacs is running in graphical mode: -;; -;; (unless (window-system) (diff-hl-margin-mode)) - -(require 'cl-lib) -(require 'diff-hl) -(require 'diff-hl-dired) - -(defvar diff-hl-margin-old-highlight-function nil) - -(defvar diff-hl-margin-old-width nil) - -(defgroup diff-hl-margin nil - "Highlight buffer changes on margin" - :group 'diff-hl) - -(defface diff-hl-margin-insert - '((default :inherit diff-hl-insert)) - "Face used to highlight inserted lines on the margin.") - -(defface diff-hl-margin-delete - '((default :inherit diff-hl-delete)) - "Face used to highlight deleted lines on the margin.") - -(defface diff-hl-margin-change - '((default :inherit diff-hl-change)) - "Face used to highlight changed lines on the margin.") - -(defface diff-hl-margin-ignored - '((default :inherit dired-ignored)) - "Face used to highlight changed lines on the margin.") - -(defface diff-hl-margin-unknown - '((default :inherit dired-ignored)) - "Face used to highlight changed lines on the margin.") - -(defcustom diff-hl-margin-symbols-alist - '((insert . "+") (delete . "-") (change . "!") - (unknown . "?") (ignored . "i")) - "Associative list from symbols to strings." - :type '(alist :key-type symbol - :value-type string - :options (insert delete change unknown ignored)) - :set (lambda (symbol value) - (defvar diff-hl-margin-spec-cache) - (set-default symbol value) - (setq diff-hl-margin-spec-cache nil))) - -;;;###autoload -(define-minor-mode diff-hl-margin-mode - "Toggle displaying `diff-hl-mode' highlights on the margin." - :lighter "" :global t - (if diff-hl-margin-mode - (progn - (add-hook 'diff-hl-mode-on-hook 'diff-hl-margin-local-mode) - (add-hook 'diff-hl-mode-off-hook 'diff-hl-margin-local-mode-off) - (add-hook 'diff-hl-dired-mode-on-hook 'diff-hl-margin-local-mode) - (add-hook 'diff-hl-dired-mode-off-hook 'diff-hl-margin-local-mode-off)) - (remove-hook 'diff-hl-mode-on-hook 'diff-hl-margin-local-mode) - (remove-hook 'diff-hl-mode-off-hook 'diff-hl-margin-local-mode-off) - (remove-hook 'diff-hl-dired-mode-on-hook 'diff-hl-margin-local-mode) - (remove-hook 'diff-hl-dired-mode-off-hook 'diff-hl-margin-local-mode-off)) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (cond - (diff-hl-mode - (diff-hl-margin-local-mode (if diff-hl-margin-mode 1 -1)) - (diff-hl-update)) - (diff-hl-dired-mode - (diff-hl-margin-local-mode (if diff-hl-margin-mode 1 -1)) - (diff-hl-dired-update)))))) - -;;;###autoload -(define-minor-mode diff-hl-margin-local-mode - "Toggle displaying `diff-hl-mode' highlights on the margin locally. -You probably shouldn't use this function directly." - :lighter "" - (let ((width-var (intern (format "%s-margin-width" diff-hl-side)))) - (if diff-hl-margin-local-mode - (progn - (setq-local diff-hl-margin-old-highlight-function - diff-hl-highlight-function) - (setq-local diff-hl-highlight-function - #'diff-hl-highlight-on-margin) - (setq-local diff-hl-margin-old-width (symbol-value width-var)) - (set width-var 1)) - (when diff-hl-margin-old-highlight-function - (setq diff-hl-highlight-function diff-hl-margin-old-highlight-function - diff-hl-margin-old-highlight-function nil)) - (set width-var diff-hl-margin-old-width) - (kill-local-variable 'diff-hl-margin-old-width))) - (dolist (win (get-buffer-window-list)) - (set-window-buffer win (current-buffer)))) - -(defun diff-hl-margin-local-mode-off () - (diff-hl-margin-local-mode -1)) - -(defvar diff-hl-margin-spec-cache nil) - -(defun diff-hl-margin-spec-cache () - (or diff-hl-margin-spec-cache - (setq diff-hl-margin-spec-cache - (diff-hl-margin-build-spec-cache)))) - -(defun diff-hl-margin-build-spec-cache () - (cl-loop for (type . char) in diff-hl-margin-symbols-alist - nconc - (cl-loop for side in '(left right) - collect - (cons - (cons type side) - (propertize - " " 'display - `((margin ,(intern (format "%s-margin" side))) - ,(propertize char 'face - (intern (format "diff-hl-margin-%s" type))))))))) - -(defun diff-hl-highlight-on-margin (ovl type _shape) - (let ((spec (cdr (assoc (cons type diff-hl-side) - (diff-hl-margin-spec-cache))))) - (overlay-put ovl 'before-string spec))) - -(provide 'diff-hl-margin) - -;;; diff-hl-margin.el ends here blob - 1fd0587df480ffb92dd936070ec54781a6deb1e7 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from diff-hl.el -*- no-byte-compile: t -*- -(define-package "diff-hl" "1.9.2" "Highlight uncommitted changes using VC" '((cl-lib "0.2") (emacs "25.1")) :commit "d20f16bf5eadd66e775f215e800f25caddae8cb5" :authors '(("Dmitry Gutov" . "dgutov@yandex.ru")) :maintainer '("Dmitry Gutov" . "dgutov@yandex.ru") :keywords '("vc" "diff") :url "https://github.com/dgutov/diff-hl") blob - 9254e1c7a036a9f82016243c3f744f1fbf605cbb (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl-show-hunk-posframe.el +++ /dev/null @@ -1,238 +0,0 @@ -;;; diff-hl-show-hunk-posframe.el --- posframe backend for diff-hl-show-hunk -*- lexical-binding: t -*- - -;; Copyright (C) 2020-2021 Free Software Foundation, Inc. - -;; Author: Álvaro González - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: -;; -;; This provides `diff-hl-show-hunk-posframe' than can be used as -;; `diff-hl-show-hunk-function'. `posframe' is a runtime dependency, -;; it is not required by this package, but it should be installed. -;; -;;; Code: - -(require 'diff-hl-show-hunk) - -;; This package uses some runtime dependencies, so we need to declare -;; the external functions and variables -(declare-function posframe-workable-p "posframe") -(declare-function posframe-show "posframe") -(defvar posframe-mouse-banish) - -(defgroup diff-hl-show-hunk-posframe nil - "Show vc diffs in a posframe." - :group 'diff-hl-show-hunk) - -(defcustom diff-hl-show-hunk-posframe-show-header-line t - "Show some useful buttons at the top of the diff-hl posframe." - :type 'boolean) - -(defcustom diff-hl-show-hunk-posframe-internal-border-width 2 - "Internal border width of the posframe." - :type 'integer) - -(defcustom diff-hl-show-hunk-posframe-internal-border-color "#00ffff" - "Internal border color of the posframe." - :type 'color) - -(defcustom diff-hl-show-hunk-posframe-poshandler nil - "Poshandler of the posframe (see `posframe-show`)." - :type '(choice function - (const :tag "None" nil))) - -(defcustom diff-hl-show-hunk-posframe-parameters nil - "The frame parameters used by helm-posframe." - :type '(choice string - (const :tag "None" nil))) - -(defface diff-hl-show-hunk-posframe '((t nil)) - "Face for the posframe buffer. -Customize it to change the base properties of the text.") - -(defface diff-hl-show-hunk-posframe-button-face '((t . (:height 0.9))) - "Face for the posframe buttons" ) - -(defvar diff-hl-show-hunk--frame nil "The postframe frame used in function `diff-hl-show-hunk-posframe'.") -(defvar diff-hl-show-hunk--original-frame nil "The frame from which the hunk is shown.") - -(defun diff-hl-show-hunk--posframe-hide () - "Hide the posframe and clean up buffer." - (interactive) - (diff-hl-show-hunk-posframe--transient-mode -1) - (when (frame-live-p diff-hl-show-hunk--frame) - (make-frame-invisible diff-hl-show-hunk--frame)) - (when diff-hl-show-hunk--original-frame - (when (frame-live-p diff-hl-show-hunk--original-frame) - (let ((frame diff-hl-show-hunk--original-frame)) - (select-frame-set-input-focus frame) - ;; In Gnome, sometimes the input focus is not restored to the - ;; original frame, so we try harder in a while - (run-with-timer 0.1 nil (lambda () (select-frame-set-input-focus frame))))) - (setq diff-hl-show-hunk--original-frame nil))) - -(defvar diff-hl-show-hunk-posframe--transient-mode-map - (let ((map (make-sparse-keymap))) - (define-key map [escape] #'diff-hl-show-hunk-hide) - (define-key map (kbd "q") #'diff-hl-show-hunk-hide) - (define-key map (kbd "C-g") #'diff-hl-show-hunk-hide) - (set-keymap-parent map diff-hl-show-hunk-map) - map) - "Keymap for command `diff-hl-show-hunk-posframe--transient-mode'.") - -(define-minor-mode diff-hl-show-hunk-posframe--transient-mode - "Temporal minor mode to control diff-hl posframe." - :lighter "" - :global t - (if diff-hl-show-hunk-posframe--transient-mode - (add-hook 'post-command-hook #'diff-hl-show-hunk--posframe-post-command-hook nil) - (remove-hook 'post-command-hook #'diff-hl-show-hunk--posframe-post-command-hook nil))) - -(defun diff-hl-show-hunk--posframe-post-command-hook () - "Called for each command while in `diff-hl-show-hunk-posframe--transient-mode." - (let* ((allowed-command (or - (diff-hl-show-hunk-ignorable-command-p this-command) - (and (symbolp this-command) - (string-match-p "diff-hl-" (symbol-name this-command))))) - (event-in-frame (eq last-event-frame diff-hl-show-hunk--frame)) - (has-focus (and (frame-live-p diff-hl-show-hunk--frame) - (functionp 'frame-focus-state) - (eq (frame-focus-state diff-hl-show-hunk--frame) t))) - (still-visible (or event-in-frame allowed-command has-focus))) - (unless still-visible - (diff-hl-show-hunk--posframe-hide)))) - -(defun diff-hl-show-hunk--posframe-button (text help-echo action) - "Make a string implementing a button with TEXT and a HELP-ECHO. -The button calls an ACTION." - (concat - (propertize (concat " " text " ") - 'help-echo (if action help-echo "Not available") - 'face 'diff-hl-show-hunk-posframe-button-face - 'mouse-face (when action '(:box (:style released-button))) - 'keymap (when action - (let ((map (make-sparse-keymap))) - (define-key map (kbd " ") action) - map))) - " ")) - -(defun diff-hl-show-hunk-posframe--header-line () - "Make the header line of the posframe." - (concat - (diff-hl-show-hunk--posframe-button - "⨯ Close" - "Close (\\[diff-hl-show-hunk-hide])" - #'diff-hl-show-hunk-hide) - (diff-hl-show-hunk--posframe-button - "⬆ Previous change" - "Previous change in hunk (\\[diff-hl-show-hunk-previous])" - #'diff-hl-show-hunk-previous) - - (diff-hl-show-hunk--posframe-button - "⬇ Next change" - "Next change in hunk (\\[diff-hl-show-hunk-next])" - #'diff-hl-show-hunk-next) - - (diff-hl-show-hunk--posframe-button - "⊚ Copy original" - "Copy original (\\[diff-hl-show-hunk-copy-original-text])" - #'diff-hl-show-hunk-copy-original-text) - - (diff-hl-show-hunk--posframe-button - "♻ Revert hunk" - "Revert hunk (\\[diff-hl-show-hunk-revert-hunk])" - #'diff-hl-show-hunk-revert-hunk) - - (unless diff-hl-show-staged-changes - (diff-hl-show-hunk--posframe-button - "⊕ Stage hunk" - "Stage hunk (\\[diff-hl-show-hunk-stage-hunk])" - #'diff-hl-show-hunk-stage-hunk)) - )) - -;;;###autoload -(defun diff-hl-show-hunk-posframe (buffer &optional _line) - "Implementation to show the hunk in a posframe." - - (unless (require 'posframe nil t) - (user-error - (concat - "`diff-hl-show-hunk-posframe' requires the `posframe' package." - " Please install it or customize `diff-hl-show-hunk-function'."))) - - (unless (posframe-workable-p) - (user-error - "Package `posframe' is not workable. Please customize diff-hl-show-hunk-function")) - - (diff-hl-show-hunk--posframe-hide) - (setq diff-hl-show-hunk--hide-function #'diff-hl-show-hunk--posframe-hide) - - ;; put an overlay to override read-only-mode keymap - (with-current-buffer buffer - ;; Change face size - (buffer-face-set 'diff-hl-show-hunk-posframe) - - (let ((full-overlay (make-overlay 1 (1+ (buffer-size))))) - (overlay-put full-overlay - 'keymap diff-hl-show-hunk-posframe--transient-mode-map))) - - (setq posframe-mouse-banish nil) - (setq diff-hl-show-hunk--original-frame last-event-frame) - - (let* ((hunk-overlay diff-hl-show-hunk--original-overlay) - (position (overlay-end hunk-overlay))) - (setq - diff-hl-show-hunk--frame - (posframe-show buffer - :position position - :poshandler diff-hl-show-hunk-posframe-poshandler - :internal-border-width diff-hl-show-hunk-posframe-internal-border-width - :accept-focus t - ;; internal-border-color Doesn't always work, if not customize internal-border face - :internal-border-color diff-hl-show-hunk-posframe-internal-border-color - :hidehandler nil - ;; Sometimes, header-line is not taken into account, so put a min height and a min width - :min-height (when diff-hl-show-hunk-posframe-show-header-line 10) - :min-width (when diff-hl-show-hunk-posframe-show-header-line - (length (diff-hl-show-hunk-posframe--header-line))) - :respect-header-line diff-hl-show-hunk-posframe-show-header-line - :respect-tab-line nil - :respect-mode-line nil - :override-parameters diff-hl-show-hunk-posframe-parameters))) - - (set-frame-parameter diff-hl-show-hunk--frame 'drag-internal-border t) - (set-frame-parameter diff-hl-show-hunk--frame 'drag-with-header-line t) - - (with-selected-frame diff-hl-show-hunk--frame - (with-current-buffer buffer - (diff-hl-show-hunk-posframe--transient-mode 1) - (when diff-hl-show-hunk-posframe-show-header-line - (setq header-line-format (diff-hl-show-hunk-posframe--header-line))) - (goto-char (point-min)) - (setq buffer-quit-function #'diff-hl-show-hunk--posframe-hide) - (select-window (window-main-window diff-hl-show-hunk--frame)) - - ;; Make cursor visible (mainly for selecting text in posframe) - (setq cursor-type 'box) - - ;; Recenter arround point - (recenter))) - (select-frame-set-input-focus diff-hl-show-hunk--frame)) - -(provide 'diff-hl-show-hunk-posframe) -;;; diff-hl-show-hunk-posframe.el ends here blob - c3adcb539d64098053287a9947dc34a596690746 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl-show-hunk.el +++ /dev/null @@ -1,419 +0,0 @@ -;;; diff-hl-show-hunk.el --- Integrate popup/posframe and diff-hl-diff-goto-hunk -*- lexical-binding: t -*- - -;; Copyright (C) 2020-2021 Free Software Foundation, Inc. - -;; Author: Álvaro González - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;; `diff-hl-show-hunk' shows a popup with the modification hunk at point. -;; `diff-hl-show-hunk-function' points to the backend used to show the -;; hunk. Its default value is `diff-hl-show-hunk-inline-popup', that -;; shows diffs inline using overlay. There is another built-in backend: -;; `diff-hl-show-hunk-posframe' (based on posframe). -;; -;; `diff-hl-show-hunk-mouse-mode' adds interaction on clicking in the -;; margin or the fringe (shows the current hunk as well). -;; -;; To use it in all buffers: -;; -;; (global-diff-hl-show-hunk-mouse-mode) - -;;; Code: - -(require 'diff-hl-inline-popup) -(require 'diff-hl) - -(defvar diff-hl-show-hunk-mouse-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd " ") 'diff-hl-show-hunk--click) - (define-key map (kbd " ") 'diff-hl-show-hunk--click) - (define-key map (kbd " ") 'diff-hl-show-hunk--click) - (define-key map (kbd " ") 'diff-hl-show-hunk--click) - map) - "Keymap for command `diff-hl-show-hunk-mouse-mode'.") - -(defvar diff-hl-show-hunk-buffer-name "*diff-hl-show-hunk-buffer*" - "Name of the buffer used by diff-hl-show-hunk.") - -(defvar diff-hl-show-hunk-diff-buffer-name "*diff-hl-show-hunk-diff-buffer*" - "Name of the buffer used by diff-hl-show-hunk to show the diff.") - -(defvar diff-hl-show-hunk--original-window nil - "The vc window of which the hunk is shown.") - -(defvar diff-hl-show-hunk--original-buffer nil - "The vc buffer of which the hunk is shown.") - -(defvar diff-hl-show-hunk--original-content nil - "The original content of the hunk.") - -(defvar diff-hl-show-hunk--original-overlay nil - "Copy of the diff-hl hunk overlay.") - -(defgroup diff-hl-show-hunk nil - "Show vc diffs in a posframe or popup." - :group 'diff-hl) - -(defconst diff-hl-show-hunk-boundary "^@@.*@@") -(defconst diff-hl-show-hunk--no-lines-removed-message (list "<>")) - -(defcustom diff-hl-show-hunk-inline-popup-hide-hunk nil - "If t, inline-popup is shown over the hunk, hiding it." - :type 'boolean) - -(defcustom diff-hl-show-hunk-inline-popup-smart-lines t - "If t, inline-popup tries to show only the deleted lines of the -hunk. The added lines are shown when scrolling the popup. If -the hunk consist only on added lines, then -`diff-hl-show-hunk--no-lines-removed-message' it is shown." - :type 'boolean) - -(defcustom diff-hl-show-hunk-function 'diff-hl-show-hunk-inline-popup - "The function used to render the hunk. -The function receives as first parameter a buffer with the -contents of the hunk, and as second parameter the line number -corresponding to the clicked line in the original buffer." - :type '(choice - (const :tag "Show inline" diff-hl-show-hunk-inline-popup) - (const :tag "Show using posframe" diff-hl-show-hunk-posframe))) - -(defvar diff-hl-show-hunk--hide-function nil - "Function to call to close the shown hunk.") - -(defun diff-hl-show-hunk-hide () - "Hide the current shown hunk." - (interactive) - (if (and diff-hl-show-hunk--original-window (window-live-p diff-hl-show-hunk--original-window)) - (select-window diff-hl-show-hunk--original-window)) - (setq diff-hl-show-hunk--original-window nil) - (if (buffer-live-p diff-hl-show-hunk--original-buffer) - (switch-to-buffer diff-hl-show-hunk--original-buffer)) - (setq diff-hl-show-hunk--original-buffer nil) - (with-current-buffer (get-buffer-create diff-hl-show-hunk-buffer-name) - (read-only-mode -1) - (erase-buffer)) - (bury-buffer diff-hl-show-hunk-buffer-name) - (when (get-buffer diff-hl-show-hunk-diff-buffer-name) - (bury-buffer diff-hl-show-hunk-diff-buffer-name)) - (when diff-hl-show-hunk--hide-function - (let ((hidefunc diff-hl-show-hunk--hide-function)) - (setq diff-hl-show-hunk--hide-function nil) - (funcall hidefunc))) - (when diff-hl-show-hunk--original-overlay - (diff-hl-show-hunk--goto-hunk-overlay diff-hl-show-hunk--original-overlay)) - (when diff-hl-show-hunk--original-overlay - (delete-overlay diff-hl-show-hunk--original-overlay)) - (setq diff-hl-show-hunk--original-overlay nil)) - -(defun diff-hl-show-hunk-ignorable-command-p (command) - "Decide if COMMAND is a command allowed while showing the current hunk." - (member command '(ignore diff-hl-show-hunk handle-switch-frame diff-hl-show-hunk--click))) - -(defun diff-hl-show-hunk--compute-diffs () - "Compute diffs using functions of diff-hl. -Then put the differences inside a special buffer and set the -point in that buffer to the corresponding line of the original -buffer." - (defvar vc-sentinel-movepoint) - (let* ((buffer (or (buffer-base-buffer) (current-buffer))) - (line (line-number-at-pos)) - (dest-buffer diff-hl-show-hunk-diff-buffer-name)) - (with-current-buffer buffer - (diff-hl-diff-buffer-with-reference (buffer-file-name buffer) dest-buffer) - (switch-to-buffer dest-buffer) - (diff-hl-diff-skip-to line) - (setq vc-sentinel-movepoint (point))) - dest-buffer)) - -(defun diff-hl-show-hunk--get-original-lines (content) - "Extracts the lines starting with '-' from CONTENT and save them." - (let* ((lines (split-string content "[\n\r]+" ))) - (cl-remove-if-not (lambda (l) (string-match-p "^-.*" l)) lines))) - -(defun diff-hl-show-hunk--fill-original-content (content) - "Extracts the lines starting with '-' from CONTENT and save them." - (let* ((original-lines (diff-hl-show-hunk--get-original-lines content)) - (original-lines (mapcar (lambda (l) (substring l 1)) original-lines)) - (content (string-join original-lines "\n"))) - (setq diff-hl-show-hunk--original-content content))) - -(defun diff-hl-show-hunk-buffer () - "Create the buffer with the contents of the hunk at point. -The buffer has the point in the corresponding line of the hunk. -Returns a list with the buffer and the line number of the clicked line." - (let ((content) - (point-in-buffer) - (line) - (line-overlay) - ;; https://emacs.stackexchange.com/questions/35680/stop-emacs-from-updating-display - (inhibit-redisplay t) - (buffer (get-buffer-create diff-hl-show-hunk-buffer-name))) - - ;; Get differences - (save-window-excursion - (save-excursion - (with-current-buffer (diff-hl-show-hunk--compute-diffs) - (setq content (buffer-substring-no-properties (point-min) (point-max))) - (setq point-in-buffer (point))))) - - (with-current-buffer buffer - (read-only-mode -1) - (erase-buffer) - (insert content) - - ;; Highlight the clicked line - (goto-char point-in-buffer) - (setq line-overlay (make-overlay (line-beginning-position) - (min (point-max) - (1+ (line-end-position))))) - - ;; diff-mode - (diff-mode) - (read-only-mode 1) - - ;; Find the hunk and narrow to it - (re-search-backward diff-hl-show-hunk-boundary nil 1) - (forward-line 1) - (let* ((start (point))) - (re-search-forward diff-hl-show-hunk-boundary nil 1) - (move-beginning-of-line nil) - (narrow-to-region start (point))) - - ;; Store original content - (let ((content (buffer-string))) - (diff-hl-show-hunk--fill-original-content content)) - - ;; Come back to the clicked line - (goto-char (overlay-start line-overlay)) - (setq line (line-number-at-pos))) - - (list buffer line))) - -(defun diff-hl-show-hunk--click (event) - "Called when user clicks on margins. EVENT is click information." - (interactive "e") - ;; Go the click's position. - (posn-set-point (event-start event)) - (diff-hl-show-hunk)) - -(defvar diff-hl-show-hunk-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "p") #'diff-hl-show-hunk-previous) - (define-key map (kbd "n") #'diff-hl-show-hunk-next) - (define-key map (kbd "c") #'diff-hl-show-hunk-copy-original-text) - (define-key map (kbd "r") #'diff-hl-show-hunk-revert-hunk) - (define-key map (kbd "[") #'diff-hl-show-hunk-previous) - (define-key map (kbd "]") #'diff-hl-show-hunk-next) - (define-key map (kbd "{") #'diff-hl-show-hunk-previous) - (define-key map (kbd "}") #'diff-hl-show-hunk-next) - (define-key map (kbd "S") #'diff-hl-show-hunk-stage-hunk) - map)) - -(defvar diff-hl-show-hunk--hide-function) - -;;;###autoload -(defun diff-hl-show-hunk-inline-popup (buffer &optional _ignored-line) - "Implementation to show the hunk in a inline popup. -BUFFER is a buffer with the hunk." - (diff-hl-inline-popup-hide) - (setq diff-hl-show-hunk--hide-function #'diff-hl-inline-popup-hide) - (let* ((lines (split-string (with-current-buffer buffer (buffer-string)) "[\n\r]+" )) - (smart-lines diff-hl-show-hunk-inline-popup-smart-lines) - (original-lines-number (cl-count-if (lambda (s) (string-prefix-p "-" s)) lines)) - (lines (if (string= (car (last lines)) "" ) (butlast lines) lines)) - (lines (if (and (eq original-lines-number 0) smart-lines) - diff-hl-show-hunk--no-lines-removed-message - lines)) - (overlay diff-hl-show-hunk--original-overlay) - (type (overlay-get overlay 'diff-hl-hunk-type)) - (point (if (eq type 'delete) (overlay-start overlay) (overlay-end overlay))) - (propertize-line (lambda (l) - (propertize l 'face - (cond ((string-prefix-p "+" l) - 'diff-added) - ((string-prefix-p "-" l) - 'diff-removed))))) - (propertized-lines (mapcar propertize-line lines))) - - (save-excursion - ;; Save point in case the hunk is hidden, so next/previous works as expected - ;; If the hunk is delete type, then don't hide the hunk - ;; (because the hunk is located in a non deleted line) - (when (and diff-hl-show-hunk-inline-popup-hide-hunk - (not (eq type 'delete))) - (let* ((invisible-overlay (make-overlay (overlay-start overlay) - (overlay-end overlay)))) - ;; Make new overlay, since the diff-hl overlay can be changed by diff-hl-flydiff - (overlay-put invisible-overlay 'invisible t) - ;; Change default hide popup function, to make the overlay visible - (setq diff-hl-show-hunk--hide-function - (lambda () - (overlay-put invisible-overlay 'invisible nil) - (delete-overlay invisible-overlay) - (diff-hl-inline-popup-hide))))) - (diff-hl-show-hunk--goto-hunk-overlay overlay) - (let ((height - (when smart-lines - (when (not (eq 0 original-lines-number)) - original-lines-number))) - (footer "(q)Quit (p)Previous (n)Next (r)Revert (c)Copy original")) - (unless diff-hl-show-staged-changes - (setq footer (concat footer " (S)Stage"))) - (diff-hl-inline-popup-show - propertized-lines - (if (and (boundp 'diff-hl-reference-revision) diff-hl-reference-revision) - (concat "Diff with " diff-hl-reference-revision) - "Diff with HEAD") - footer - diff-hl-show-hunk-map - #'diff-hl-show-hunk-hide - point - height)) - ))) - -(defun diff-hl-show-hunk-copy-original-text () - "Extracts all the lines from BUFFER starting with '-' to the kill ring." - (interactive) - (kill-new diff-hl-show-hunk--original-content) - (message "Original hunk content added to kill-ring")) - -(defun diff-hl-show-hunk-revert-hunk () - "Dismiss the popup and revert the current diff hunk." - (interactive) - (diff-hl-show-hunk-hide) - (let (diff-hl-ask-before-revert-hunk) - (diff-hl-revert-hunk))) - -(defun diff-hl-show-hunk-stage-hunk () - "Dismiss the popup and stage the current hunk." - (interactive) - (diff-hl-show-hunk-hide) - (diff-hl-stage-current-hunk)) - -;;;###autoload -(defun diff-hl-show-hunk-previous () - "Go to previous hunk/change and show it." - (interactive) - (let* ((point (if diff-hl-show-hunk--original-overlay - (overlay-start diff-hl-show-hunk--original-overlay) - nil)) - (previous-overlay (diff-hl-show-hunk--next-hunk t point))) - (if (not previous-overlay) - (message "There is no previous change") - (diff-hl-show-hunk-hide) - (diff-hl-show-hunk--goto-hunk-overlay previous-overlay) - (recenter) - (diff-hl-show-hunk)))) - -(defun diff-hl-show-hunk--next-hunk (backward point) - "Same as `diff-hl-search-next-hunk', but in the current buffer -of `diff-hl-show-hunk'." - (with-current-buffer (or diff-hl-show-hunk--original-buffer (current-buffer)) - (diff-hl-search-next-hunk backward point))) - -(defun diff-hl-show-hunk--goto-hunk-overlay (overlay) - "Tries to display the whole overlay, and place the point at the -end of the OVERLAY, so posframe/inline is placed below the hunk." - (when (and (overlayp overlay) (overlay-buffer overlay)) - (let ((pt (point))) - (goto-char (overlay-start overlay)) - (cond - ((< (point) (window-start)) - (set-window-start nil (point))) - ((> (point) pt) - (redisplay)))) - (goto-char (1- (overlay-end overlay))))) - -;;;###autoload -(defun diff-hl-show-hunk-next () - "Go to next hunk/change and show it." - (interactive) - (let* ((point (if diff-hl-show-hunk--original-overlay - (overlay-start diff-hl-show-hunk--original-overlay) - nil)) - (next-overlay (diff-hl-show-hunk--next-hunk nil point))) - (if (not next-overlay) - (message "There is no next change") - (diff-hl-show-hunk-hide) - (diff-hl-show-hunk--goto-hunk-overlay next-overlay) - (recenter) - (diff-hl-show-hunk)))) - -;;;###autoload -(defun diff-hl-show-hunk () - "Show the VC diff hunk at point. -The backend is determined by `diff-hl-show-hunk-function'." - (interactive) - - ;; Close any previous hunk - (save-excursion - (diff-hl-show-hunk-hide)) - - (unless (vc-backend buffer-file-name) - (user-error "The buffer is not under version control")) - - (diff-hl-find-current-hunk) - - (setq diff-hl-show-hunk--original-overlay nil) - - ;; Store begining and end of hunk overlay - (let ((overlay (diff-hl-hunk-overlay-at (point)))) - (when overlay - (let ((start (overlay-start overlay)) - (end (overlay-end overlay)) - (type (overlay-get overlay 'diff-hl-hunk-type))) - (setq diff-hl-show-hunk--original-overlay (make-overlay start end)) - (overlay-put diff-hl-show-hunk--original-overlay 'diff-hl-hunk-type type))) - - (unless overlay - (user-error "Not in a hunk"))) - - (cond - ((not diff-hl-show-hunk-function) - (message "Please configure `diff-hl-show-hunk-function'") - (diff-hl-diff-goto-hunk)) - ((let ((buffer-and-line (diff-hl-show-hunk-buffer))) - (setq diff-hl-show-hunk--original-buffer (current-buffer)) - (setq diff-hl-show-hunk--original-window (selected-window)) - (apply diff-hl-show-hunk-function buffer-and-line)) - ;; We could fall back to `diff-hl-diff-goto-hunk', but the - ;; current default should work in all environments (both GUI - ;; and terminal), and if something goes wrong we better show - ;; the error to the user. - ))) - -;;;###autoload -(define-minor-mode diff-hl-show-hunk-mouse-mode - "Enables the margin and fringe to show a posframe/popup with vc diffs when clicked. -By default, the popup shows only the current hunk, and -the line of the hunk that matches the current position is -highlighted. The face, border and other visual preferences are -customizable. It can be also invoked with the command -`diff-hl-show-hunk' -\\{diff-hl-show-hunk-mouse-mode-map}" - :group 'diff-hl-show-hunk - :lighter "") - -;;;###autoload -(define-globalized-minor-mode global-diff-hl-show-hunk-mouse-mode - diff-hl-show-hunk-mouse-mode - diff-hl-show-hunk-mouse-mode) - -(provide 'diff-hl-show-hunk) -;;; diff-hl-show-hunk.el ends here blob - cdd831f7900b0ce07e44ba884585c6924c96254d (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/diff-hl.el +++ /dev/null @@ -1,1128 +0,0 @@ -;;; diff-hl.el --- Highlight uncommitted changes using VC -*- lexical-binding: t -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc. - -;; Author: Dmitry Gutov -;; URL: https://github.com/dgutov/diff-hl -;; Keywords: vc, diff -;; Version: 1.9.2 -;; Package-Requires: ((cl-lib "0.2") (emacs "25.1")) - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;; `diff-hl-mode' highlights uncommitted changes on the side of the -;; window (using the fringe, by default), allows you to jump between -;; the hunks and revert them selectively. - -;; Provided commands: -;; -;; `diff-hl-diff-goto-hunk' C-x v = -;; `diff-hl-revert-hunk' C-x v n -;; `diff-hl-previous-hunk' C-x v [ -;; `diff-hl-next-hunk' C-x v ] -;; `diff-hl-show-hunk' C-x v * -;; `diff-hl-stage-current-hunk' C-x v S -;; `diff-hl-set-reference-rev' -;; `diff-hl-reset-reference-rev' -;; `diff-hl-unstage-file' -;; -;; The mode takes advantage of `smartrep' if it is installed. -;; -;; Alternatively, it integrates with `repeat-mode' (Emacs 28+). - -;; Add either of the following to your init file. -;; -;; To use it in all buffers: -;; -;; (global-diff-hl-mode) -;; -;; Only in `prog-mode' buffers, with `vc-dir' integration: -;; -;; (add-hook 'prog-mode-hook 'turn-on-diff-hl-mode) -;; (add-hook 'vc-dir-mode-hook 'turn-on-diff-hl-mode) - -;;; Code: - -(require 'fringe) -(require 'diff-mode) -(require 'vc) -(require 'vc-dir) -(require 'log-view) - -(eval-when-compile - (require 'cl-lib) - (require 'vc-git) - (require 'vc-hg) - (require 'face-remap) - (declare-function smartrep-define-key 'smartrep)) - -(defgroup diff-hl nil - "VC diff highlighting on the side of a window" - :group 'vc) - -(defface diff-hl-insert - '((default :inherit diff-added) - (((class color)) :foreground "green4")) - "Face used to highlight inserted lines." - :group 'diff-hl) - -(defface diff-hl-delete - '((default :inherit diff-removed) - (((class color)) :foreground "red3")) - "Face used to highlight deleted lines." - :group 'diff-hl) - -(defface diff-hl-change - '((default :foreground "blue3") - (((class color) (min-colors 88) (background light)) - :background "#ddddff") - (((class color) (min-colors 88) (background dark)) - :background "#333355")) - "Face used to highlight changed lines." - :group 'diff-hl) - -(defcustom diff-hl-command-prefix (kbd "C-x v") - "The prefix for all `diff-hl' commands." - :group 'diff-hl - :type 'string) - -(defcustom diff-hl-draw-borders t - "Non-nil to draw borders around fringe indicators." - :group 'diff-hl - :type 'boolean) - -(defcustom diff-hl-disable-on-remote nil - "Non-nil will disable `diff-hl' in remote buffers." - :group 'diff-hl - :type 'boolean) - -(defcustom diff-hl-ask-before-revert-hunk t - "Non-nil to ask for confirmation before revert a hunk." - :group 'diff-hl - :type 'boolean) - -(defcustom diff-hl-highlight-function 'diff-hl-highlight-on-fringe - "Function to highlight the current line. Its arguments are - overlay, change type and position within a hunk." - :group 'diff-hl - :type 'function) - -(defcustom diff-hl-fringe-bmp-function 'diff-hl-fringe-bmp-from-pos - "Function to choose the fringe bitmap for a given change type - and position within a hunk. Should accept two arguments." - :group 'diff-hl - :type '(choice (const diff-hl-fringe-bmp-from-pos) - (const diff-hl-fringe-bmp-from-type) - function)) - -(defcustom diff-hl-fringe-face-function 'diff-hl-fringe-face-from-type - "Function to choose the fringe face for a given change type - and position within a hunk. Should accept two arguments." - :group 'diff-hl - :type 'function) - -(defcustom diff-hl-side 'left - "Which side to use for indicators." - :type '(choice (const left) - (const right)) - :initialize 'custom-initialize-default - :set (lambda (var value) - (let ((on (bound-and-true-p global-diff-hl-mode))) - (when on (global-diff-hl-mode -1)) - (set-default var value) - (when on (global-diff-hl-mode 1))))) - -(defcustom diff-hl-highlight-revert-hunk-function - #'diff-hl-revert-narrow-to-hunk - "Function to emphasize the current hunk in `diff-hl-revert-hunk'. -The function is called at the beginning of the hunk and is passed -the end position as its only argument." - :type '(choice (const :tag "Do nothing" ignore) - (const :tag "Highlight the first column" - diff-hl-revert-highlight-first-column) - (const :tag "Narrow to the hunk" - diff-hl-revert-narrow-to-hunk))) - -(defcustom diff-hl-global-modes '(not image-mode) - "Modes for which `diff-hl-mode' is automagically turned on. -This affects the behavior of `global-diff-hl-mode'. -If nil, no modes have `diff-hl-mode' automatically turned on. -If t, all modes have `diff-hl-mode' enabled. -If a list, it should be a list of `major-mode' symbol names for -which it should be automatically turned on. The sense of the list -is negated if it begins with `not'. As such, the default value - (not image-mode) -means that `diff-hl-mode' is turned on in all modes except for -`image-mode' buffers. Previously, `diff-hl-mode' caused worse -performance when viewing such files in certain conditions." - :type '(choice (const :tag "none" nil) - (const :tag "all" t) - (set :menu-tag "mode specific" :tag "modes" - :value (not) - (const :tag "Except" not) - (repeat :inline t (symbol :tag "mode")))) - :group 'diff-hl) - -(defcustom diff-hl-show-staged-changes t - "Whether to include staged changes in the indicators. -Only affects Git, it's the only backend that has staging area." - :type 'boolean) - -(defcustom diff-hl-goto-hunk-old-revisions nil - "When non-nil, `diff-hl-diff-goto-hunk' will always try to -navigate to the line in the diff that corresponds to the current -line in the file buffer (or as close as it can get to it). - -When this variable is nil (default), `diff-hl-diff-goto-hunk' -only does that when called without the prefix argument, or when -the NEW revision is not specified (meaning, the diff is against -the current version of the file)." - :type 'boolean) - -(defvar diff-hl-reference-revision nil - "Revision to diff against. nil means the most recent one.") - -(defun diff-hl-define-bitmaps () - (let* ((scale (if (and (boundp 'text-scale-mode-amount) - (numberp text-scale-mode-amount)) - (expt text-scale-mode-step text-scale-mode-amount) - 1)) - (spacing (or (and (display-graphic-p) (default-value 'line-spacing)) 0)) - (h (+ (ceiling (* (frame-char-height) scale)) - (if (floatp spacing) - (truncate (* (frame-char-height) spacing)) - spacing))) - (w (min (frame-parameter nil (intern (format "%s-fringe" diff-hl-side))) - 16)) - (_ (when (zerop w) (setq w 16))) - (middle (make-vector h (expt 2 (1- w)))) - (ones (1- (expt 2 w))) - (top (copy-sequence middle)) - (bottom (copy-sequence middle)) - (single (copy-sequence middle))) - (aset top 0 ones) - (aset bottom (1- h) ones) - (aset single 0 ones) - (aset single (1- h) ones) - (define-fringe-bitmap 'diff-hl-bmp-top top h w 'top) - (define-fringe-bitmap 'diff-hl-bmp-middle middle h w 'center) - (define-fringe-bitmap 'diff-hl-bmp-bottom bottom h w 'bottom) - (define-fringe-bitmap 'diff-hl-bmp-single single h w 'top) - (define-fringe-bitmap 'diff-hl-bmp-i [3 3 0 3 3 3 3 3 3 3] nil 2 'center) - (let* ((w2 (* (/ w 2) 2)) - ;; When fringes are disabled, it's easier to fix up the width, - ;; instead of doing nothing (#20). - (w2 (if (zerop w2) 2 w2)) - (delete-row (- (expt 2 (1- w2)) 2)) - (middle-pos (1- (/ w2 2))) - (middle-bit (expt 2 middle-pos)) - (insert-bmp (make-vector w2 (* 3 middle-bit)))) - (define-fringe-bitmap 'diff-hl-bmp-delete (make-vector 2 delete-row) w2 w2) - (aset insert-bmp 0 0) - (aset insert-bmp middle-pos delete-row) - (aset insert-bmp (1+ middle-pos) delete-row) - (aset insert-bmp (1- w2) 0) - (define-fringe-bitmap 'diff-hl-bmp-insert insert-bmp w2 w2) - ))) - -(defun diff-hl-maybe-define-bitmaps () - (when (window-system) ;; No fringes in the console. - (unless (fringe-bitmap-p 'diff-hl-bmp-empty) - (diff-hl-define-bitmaps) - (define-fringe-bitmap 'diff-hl-bmp-empty [0] 1 1 'center)))) - -(defun diff-hl-maybe-redefine-bitmaps () - (when (window-system) - (diff-hl-define-bitmaps))) - -(defvar diff-hl-spec-cache (make-hash-table :test 'equal)) - -(defun diff-hl-fringe-spec (type pos side) - (let* ((key (list type pos side - diff-hl-fringe-face-function - diff-hl-fringe-bmp-function)) - (val (gethash key diff-hl-spec-cache))) - (unless val - (let* ((face-sym (funcall diff-hl-fringe-face-function type pos)) - (bmp-sym (funcall diff-hl-fringe-bmp-function type pos))) - (setq val (propertize " " 'display `((,(intern (format "%s-fringe" side)) - ,bmp-sym ,face-sym)))) - (puthash key val diff-hl-spec-cache))) - val)) - -(defun diff-hl-fringe-face-from-type (type _pos) - (intern (format "diff-hl-%s" type))) - -(defun diff-hl-fringe-bmp-from-pos (_type pos) - (intern (format "diff-hl-bmp-%s" pos))) - -(defun diff-hl-fringe-bmp-from-type (type _pos) - (cl-case type - (unknown 'question-mark) - (change 'exclamation-mark) - (ignored 'diff-hl-bmp-i) - (t (intern (format "diff-hl-bmp-%s" type))))) - -(defvar vc-svn-diff-switches) -(defvar vc-fossil-diff-switches) - -(defmacro diff-hl-with-diff-switches (body) - `(let ((vc-git-diff-switches - ;; https://github.com/dgutov/diff-hl/issues/67 - (cons "-U0" - ;; https://github.com/dgutov/diff-hl/issues/9 - (and (boundp 'vc-git-diff-switches) - (listp vc-git-diff-switches) - (cl-remove-if-not - (lambda (arg) - (member arg '("--histogram" "--patience" "--minimal"))) - vc-git-diff-switches)))) - (vc-hg-diff-switches nil) - (vc-svn-diff-switches nil) - (vc-fossil-diff-switches '("-c" "0")) - (vc-diff-switches '("-U0")) - ,@(when (boundp 'vc-disable-async-diff) - '((vc-disable-async-diff t)))) - ,body)) - -(defun diff-hl-modified-p (state) - (or (memq state '(edited conflict)) - (and (eq state 'up-to-date) - ;; VC state is stale in after-revert-hook. - (or revert-buffer-in-progress-p - ;; Diffing against an older revision. - diff-hl-reference-revision)))) - -(declare-function vc-git-command "vc-git") - -(defun diff-hl-changes-buffer (file backend) - (diff-hl-with-diff-switches - (diff-hl-diff-against-reference file backend " *diff-hl* "))) - -(defun diff-hl-diff-against-reference (file backend buffer) - (if (and (eq backend 'Git) - (not diff-hl-reference-revision) - (not diff-hl-show-staged-changes)) - (apply #'vc-git-command buffer 1 - (list file) - "diff-files" - (cons "-p" (vc-switches 'git 'diff))) - (condition-case err - (vc-call-backend backend 'diff (list file) - diff-hl-reference-revision nil - buffer) - (error - ;; https://github.com/dgutov/diff-hl/issues/117 - (when (string-match-p "\\`Failed (status 128)" (error-message-string err)) - (vc-call-backend backend 'diff (list file) - "4b825dc642cb6eb9a060e54bf8d69288fbee4904" - nil - buffer))))) - buffer) - -(defun diff-hl-changes () - (let* ((file buffer-file-name) - (backend (vc-backend file))) - (when backend - (let ((state (vc-state file backend))) - (cond - ((diff-hl-modified-p state) - (diff-hl-changes-from-buffer - (diff-hl-changes-buffer file backend))) - ((eq state 'added) - `((1 ,(line-number-at-pos (point-max)) insert))) - ((eq state 'removed) - `((1 ,(line-number-at-pos (point-max)) delete)))))))) - -(defun diff-hl-changes-from-buffer (buf) - (with-current-buffer buf - (let (res) - (goto-char (point-min)) - (unless (eobp) - ;; TODO: When 27.1 is the minimum requirement, we can drop - ;; these bindings: that version, in addition to switching over - ;; to the diff-refine var, also added the - ;; called-interactively-p check, so refinement can't be - ;; triggered by code calling the navigation functions, only by - ;; direct interactive invocations. - (ignore-errors - (with-no-warnings - (let (diff-auto-refine-mode) - (diff-beginning-of-hunk t)))) - (while (looking-at diff-hunk-header-re-unified) - (let ((line (string-to-number (match-string 3))) - (len (let ((m (match-string 4))) - (if m (string-to-number m) 1))) - (beg (point))) - (with-no-warnings - (let (diff-auto-refine-mode) - (diff-end-of-hunk))) - (let* ((inserts (diff-count-matches "^\\+" beg (point))) - (deletes (diff-count-matches "^-" beg (point))) - (type (cond ((zerop deletes) 'insert) - ((zerop inserts) 'delete) - (t 'change)))) - (when (eq type 'delete) - (setq len 1) - (cl-incf line)) - (push (list line len type) res))))) - (nreverse res)))) - -(defun diff-hl-update () - (let ((changes (diff-hl-changes)) - (current-line 1)) - (diff-hl-remove-overlays) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (dolist (c changes) - (cl-destructuring-bind (line len type) c - (forward-line (- line current-line)) - (setq current-line line) - (let ((hunk-beg (point))) - (while (cl-plusp len) - (diff-hl-add-highlighting - type - (cond - ((not diff-hl-draw-borders) 'empty) - ((and (= len 1) (= line current-line)) 'single) - ((= len 1) 'bottom) - ((= line current-line) 'top) - (t 'middle))) - (forward-line 1) - (cl-incf current-line) - (cl-decf len)) - (let ((h (make-overlay hunk-beg (point))) - (hook '(diff-hl-overlay-modified))) - (overlay-put h 'diff-hl t) - (overlay-put h 'diff-hl-hunk t) - (overlay-put h 'diff-hl-hunk-type type) - (overlay-put h 'modification-hooks hook) - (overlay-put h 'insert-in-front-hooks hook) - (overlay-put h 'insert-behind-hooks hook))))))))) - -(defvar-local diff-hl--modified-tick nil) - -(put 'diff-hl--modified-tick 'permanent-local t) - -(defun diff-hl-update-once () - (unless (equal diff-hl--modified-tick (buffer-chars-modified-tick)) - (diff-hl-update) - (setq diff-hl--modified-tick (buffer-chars-modified-tick)))) - -(defun diff-hl-add-highlighting (type shape) - (let ((o (make-overlay (point) (point)))) - (overlay-put o 'diff-hl t) - (funcall diff-hl-highlight-function o type shape) - o)) - -(defun diff-hl-highlight-on-fringe (ovl type shape) - (overlay-put ovl 'before-string (diff-hl-fringe-spec type shape - diff-hl-side))) - -(defun diff-hl-remove-overlays (&optional beg end) - (save-restriction - (widen) - (dolist (o (overlays-in (or beg (point-min)) (or end (point-max)))) - (when (overlay-get o 'diff-hl) (delete-overlay o))))) - -(defun diff-hl-overlay-modified (ov after-p _beg _end &optional _length) - "Delete the hunk overlay and all our line overlays inside it." - (unless after-p - (when (overlay-buffer ov) - (diff-hl-remove-overlays (overlay-start ov) (overlay-end ov)) - (delete-overlay ov)))) - -(defvar diff-hl-timer nil) - -(defun diff-hl-edit (_beg _end _len) - "DTRT when we've `undo'-ne the buffer into unmodified state." - (when undo-in-progress - (when diff-hl-timer - (cancel-timer diff-hl-timer)) - (setq diff-hl-timer - (run-with-idle-timer 0.01 nil #'diff-hl-after-undo (current-buffer))))) - -(defun diff-hl-after-undo (buffer) - (with-current-buffer buffer - (unless (buffer-modified-p) - (diff-hl-update)))) - -(defun diff-hl-after-revert () - (defvar revert-buffer-preserve-modes) - (when revert-buffer-preserve-modes - (diff-hl-update))) - -(defun diff-hl-diff-goto-hunk-1 (historic) - (defvar vc-sentinel-movepoint) - (vc-buffer-sync) - (let* ((line (line-number-at-pos)) - (buffer (current-buffer)) - (rev1 diff-hl-reference-revision) - rev2) - (when historic - (let ((revs (diff-hl-diff-read-revisions rev1))) - (setq rev1 (car revs) - rev2 (cdr revs)))) - (vc-diff-internal t (vc-deduce-fileset) rev1 rev2 t) - (vc-run-delayed (if (< (line-number-at-pos (point-max)) 3) - (with-current-buffer buffer (diff-hl-remove-overlays)) - (when (or (not rev2) diff-hl-goto-hunk-old-revisions) - (diff-hl-diff-skip-to line)) - (setq vc-sentinel-movepoint (point)))))) - -(defun diff-hl-diff-goto-hunk (&optional historic) - "Run VC diff command and go to the line corresponding to the current." - (interactive (list current-prefix-arg)) - (with-current-buffer (or (buffer-base-buffer) (current-buffer)) - (diff-hl-diff-goto-hunk-1 historic))) - -(defun diff-hl-diff-read-revisions (rev1-default) - (let* ((file buffer-file-name) - (files (list file)) - (backend (vc-backend file)) - (rev2-default nil)) - (cond - ;; if the file is not up-to-date, use working revision as older revision - ((not (vc-up-to-date-p file)) - (setq rev1-default - (or rev1-default - (vc-working-revision file)))) - ((not rev1-default) - (setq rev1-default (ignore-errors ;If `previous-revision' doesn't work. - (vc-call-backend backend 'previous-revision file - (vc-working-revision file)))) - (when (string= rev1-default "") (setq rev1-default nil)))) - ;; finally read the revisions - (let* ((rev1-prompt (if rev1-default - (concat "Older revision (default " - rev1-default "): ") - "Older revision: ")) - (rev2-prompt (concat "Newer revision (default " - (or rev2-default "current source") "): ")) - (rev1 (vc-read-revision rev1-prompt files backend rev1-default)) - (rev2 (vc-read-revision rev2-prompt files backend rev2-default))) - (when (string= rev1 "") (setq rev1 nil)) - (when (string= rev2 "") (setq rev2 nil)) - (cons rev1 rev2)))) - -(defun diff-hl-diff-skip-to (line) - "In `diff-mode', skip to the hunk and line corresponding to LINE -in the source file, or the last line of the hunk above it." - (goto-char (point-min)) ; Counteract any similar behavior in diff-mode. - (diff-hunk-next) - (let (found) - (while (and (looking-at diff-hunk-header-re-unified) (not found)) - (let ((hunk-line (string-to-number (match-string 3))) - (len (let ((m (match-string 4))) - (if m (string-to-number m) 1)))) - (if (> line (+ hunk-line len)) - (diff-hunk-next) - (setq found t) - (if (< line hunk-line) - ;; Retreat to the previous hunk. - (forward-line -1) - (let ((to-go (1+ (- line hunk-line)))) - (while (cl-plusp to-go) - (forward-line 1) - (unless (looking-at "^-") - (cl-decf to-go)))))))))) - -(defface diff-hl-reverted-hunk-highlight - '((default :inverse-video t)) - "Face used to highlight the first column of the hunk to be reverted.") - -(defun diff-hl-revert-highlight-first-column (end) - (re-search-forward "^[+-]") - (forward-line 0) - (setq end (diff-hl-split-away-changes 0)) - (let ((inhibit-read-only t)) - (save-excursion - (while (< (point) end) - (font-lock-prepend-text-property (point) (1+ (point)) 'font-lock-face - 'diff-hl-reverted-hunk-highlight) - (forward-line 1))))) - -(defun diff-hl-revert-narrow-to-hunk (end) - (narrow-to-region (point) end)) - -(defun diff-hl-revert-hunk-1 () - (save-restriction - (widen) - (vc-buffer-sync) - (let* ((diff-buffer (get-buffer-create - (generate-new-buffer-name "*diff-hl*"))) - (buffer (current-buffer)) - (line (save-excursion - (diff-hl-find-current-hunk) - (line-number-at-pos))) - (file buffer-file-name) - (backend (vc-backend file))) - (unwind-protect - (progn - (vc-setup-buffer diff-buffer) - (diff-hl-diff-against-reference file backend diff-buffer) - (set-buffer diff-buffer) - (diff-mode) - (setq-local diff-vc-backend backend) - (setq-local diff-vc-revisions (list diff-hl-reference-revision nil)) - (setq buffer-read-only t) - (pop-to-buffer diff-buffer) - (vc-run-delayed - (vc-diff-finish diff-buffer nil) - (let (beg-line end-line m-beg m-end) - (when (eobp) - (with-current-buffer buffer (diff-hl-remove-overlays)) - (user-error "Buffer is up-to-date")) - (with-no-warnings - (let (diff-auto-refine-mode) - (diff-hl-diff-skip-to line))) - (setq m-end (diff-hl-split-away-changes 3)) - (setq m-beg (point-marker)) - (funcall diff-hl-highlight-revert-hunk-function m-end) - (setq beg-line (line-number-at-pos m-beg) - end-line (line-number-at-pos m-end)) - (let ((wbh (window-body-height))) - (if (>= wbh (- end-line beg-line)) - (recenter (/ (+ wbh (- beg-line end-line) 2) 2)) - (recenter 1))) - (with-no-warnings - (when diff-auto-refine-mode - (diff-refine-hunk))) - (if diff-hl-ask-before-revert-hunk - (unless (yes-or-no-p (format "Revert current hunk in %s? " - file)) - (user-error "Revert canceled"))) - (let ((diff-advance-after-apply-hunk nil)) - (save-window-excursion - (diff-apply-hunk t))) - (with-current-buffer buffer - (save-buffer)) - (message "Hunk reverted")))) - (quit-windows-on diff-buffer t))))) - -(defun diff-hl-split-away-changes (max-context) - "Split away the minimal hunk at point from the rest of the hunk. - -The minimal hunk is the hunk a diff program would produce if -asked for 0 lines of context. Add MAX-CONTEXT lines of context at -most (stop when encounter another minimal hunk). - -Move point to the beginning of the delineated hunk and return -its end position." - (let (end-marker) - (save-excursion - (while (looking-at "[-+]") (forward-line 1)) - (dotimes (_i max-context) - (unless (looking-at "@\\|[-+]") - (forward-line 1))) - (setq end-marker (point-marker)) - (unless (or (eobp) - (looking-at "@")) - (diff-split-hunk))) - (unless (looking-at "[-+]") (forward-line -1)) - (while (looking-at "[-+]") (forward-line -1)) - (dotimes (_i max-context) - (unless (looking-at "@\\|[-+]") - (forward-line -1))) - (unless (looking-at "@") - (forward-line 1) - (diff-split-hunk)) - end-marker)) - -(defun diff-hl-revert-hunk () - "Revert the diff hunk with changes at or above the point." - (interactive) - (with-current-buffer (or (buffer-base-buffer) (current-buffer)) - (diff-hl-revert-hunk-1))) - -(defun diff-hl-hunk-overlay-at (pos) - (cl-loop for o in (overlays-in pos (1+ pos)) - when (overlay-get o 'diff-hl-hunk) - return o)) - -(defun diff-hl-search-next-hunk (&optional backward point) - "Search the next hunk in the current buffer, or previous if BACKWARD." - (save-excursion - (when point - (goto-char point)) - (catch 'found - (while (not (if backward (bobp) (eobp))) - (goto-char (if backward - (previous-overlay-change (point)) - (next-overlay-change (point)))) - (let ((o (diff-hl-hunk-overlay-at (point)))) - (when (and o (= (overlay-start o) (point))) - (throw 'found o))))))) - -(defun diff-hl-next-hunk (&optional backward) - "Go to the beginning of the next hunk in the current buffer." - (interactive) - (let ((overlay (diff-hl-search-next-hunk backward))) - (if overlay - (goto-char (overlay-start overlay)) - (user-error "No further hunks found")))) - -(defun diff-hl-previous-hunk () - "Go to the beginning of the previous hunk in the current buffer." - (interactive) - (diff-hl-next-hunk t)) - -(defun diff-hl-find-current-hunk () - (let (o) - (cond - ((diff-hl-hunk-overlay-at (point))) - ((setq o (diff-hl-search-next-hunk t)) - (goto-char (overlay-start o))) - (t - (diff-hl-next-hunk))))) - -(defun diff-hl-mark-hunk () - (interactive) - (let ((hunk (diff-hl-hunk-overlay-at (point)))) - (unless hunk - (user-error "No hunk at point")) - (goto-char (overlay-start hunk)) - (push-mark (overlay-end hunk) nil t))) - -(defun diff-hl--ensure-staging-supported () - (let ((backend (vc-backend buffer-file-name))) - (unless (eq backend 'Git) - (user-error "Only Git supports staging; this file is controlled by %s" backend)))) - -(defun diff-hl-stage-current-hunk () - "Stage the hunk at or near point. - -Only supported with Git." - (interactive) - (diff-hl--ensure-staging-supported) - (diff-hl-find-current-hunk) - (let* ((line (line-number-at-pos)) - (file buffer-file-name) - (dest-buffer (get-buffer-create " *diff-hl-stage*")) - (orig-buffer (current-buffer)) - (file-base (shell-quote-argument (file-name-nondirectory file))) - success) - (with-current-buffer dest-buffer - (let ((inhibit-read-only t)) - (erase-buffer))) - (diff-hl-diff-buffer-with-reference file dest-buffer nil 3) - (with-current-buffer dest-buffer - (with-no-warnings - (let (diff-auto-refine-mode) - (diff-hl-diff-skip-to line))) - (let ((inhibit-read-only t)) - (diff-hl-split-away-changes 3) - (save-excursion - (diff-end-of-hunk) - (delete-region (point) (point-max))) - (diff-beginning-of-hunk) - (delete-region (point-min) (point)) - ;; diff-no-select creates a very ugly header; Git rejects it - (insert (format "diff a/%s b/%s\n" file-base file-base)) - (insert (format "--- a/%s\n" file-base)) - (insert (format "+++ b/%s\n" file-base))) - (let ((patchfile (make-temp-file "diff-hl-stage-patch"))) - (write-region (point-min) (point-max) patchfile - nil 'silent) - (unwind-protect - (with-current-buffer orig-buffer - (with-output-to-string - (vc-git-command standard-output 0 - patchfile - "apply" "--cached" )) - (setq success t)) - (delete-file patchfile)))) - (when success - (if diff-hl-show-staged-changes - (message (concat "Hunk staged; customize `diff-hl-show-staged-changes'" - " to highlight only unstages changes")) - (message "Hunk staged")) - (unless diff-hl-show-staged-changes - (diff-hl-update))))) - -(defun diff-hl-unstage-file () - "Unstage all changes in the current file. - -Only supported with Git." - (interactive) - (unless buffer-file-name - (user-error "No current file")) - (diff-hl--ensure-staging-supported) - (vc-git-command nil 0 buffer-file-name "reset") - (message "Unstaged all") - (unless diff-hl-show-staged-changes - (diff-hl-update))) - -(defvar diff-hl-command-map - (let ((map (make-sparse-keymap))) - (define-key map "n" 'diff-hl-revert-hunk) - (define-key map "[" 'diff-hl-previous-hunk) - (define-key map "]" 'diff-hl-next-hunk) - (define-key map "*" 'diff-hl-show-hunk) - (define-key map "{" 'diff-hl-show-hunk-previous) - (define-key map "}" 'diff-hl-show-hunk-next) - (define-key map "S" 'diff-hl-stage-current-hunk) - map)) -(fset 'diff-hl-command-map diff-hl-command-map) - -(defvar diff-hl-lighter "" - "Mode line lighter for Diff Hl. - -The value of this variable is a mode line template as in -`mode-line-format'.") - -;;;###autoload -(define-minor-mode diff-hl-mode - "Toggle VC diff highlighting." - :lighter diff-hl-lighter - :keymap `(([remap vc-diff] . diff-hl-diff-goto-hunk) - (,diff-hl-command-prefix . diff-hl-command-map)) - (if diff-hl-mode - (progn - (diff-hl-maybe-define-bitmaps) - (add-hook 'after-save-hook 'diff-hl-update nil t) - (add-hook 'after-change-functions 'diff-hl-edit nil t) - (add-hook (if vc-mode - ;; Defer until the end of this hook, so that its - ;; elements can modify the update behavior. - 'diff-hl-mode-on-hook - ;; If we're only opening the file now, - ;; `vc-find-file-hook' likely hasn't run yet, so - ;; let's wait until the state information is - ;; saved, in order not to fetch it twice. - 'find-file-hook) - 'diff-hl-update-once t t) - ;; Never removed because it acts globally. - (add-hook 'vc-checkin-hook 'diff-hl-after-checkin) - (add-hook 'after-revert-hook 'diff-hl-after-revert nil t) - ;; Magit does call `auto-revert-handler', but it usually - ;; doesn't do much, because `buffer-stale--default-function' - ;; doesn't care about changed VC state. - ;; https://github.com/magit/magit/issues/603 - (add-hook 'magit-revert-buffer-hook 'diff-hl-update nil t) - ;; Magit versions 2.0-2.3 don't do the above and call this - ;; instead, but only when they don't call `revert-buffer': - (add-hook 'magit-not-reverted-hook 'diff-hl-update nil t) - (add-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps nil t)) - (remove-hook 'after-save-hook 'diff-hl-update t) - (remove-hook 'after-change-functions 'diff-hl-edit t) - (remove-hook 'find-file-hook 'diff-hl-update t) - (remove-hook 'after-revert-hook 'diff-hl-update t) - (remove-hook 'magit-revert-buffer-hook 'diff-hl-update t) - (remove-hook 'magit-not-reverted-hook 'diff-hl-update t) - (remove-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps t) - (diff-hl-remove-overlays))) - -(defun diff-hl-after-checkin () - (let ((fileset (vc-deduce-fileset t))) - (dolist (file (nth 1 fileset)) - (let ((buf (find-buffer-visiting file))) - (when buf - (with-current-buffer buf - (when diff-hl-mode - (diff-hl-update)))))))) - -(defvar diff-hl-repeat-exceptions '(diff-hl-show-hunk - diff-hl-show-hunk-previous - diff-hl-show-hunk-next)) - -(when (require 'smartrep nil t) - (let (smart-keys) - (cl-labels ((scan (map) - (map-keymap - (lambda (event binding) - (if (consp binding) - (scan binding) - (when (and (characterp event) - (not (memq binding diff-hl-repeat-exceptions))) - (push (cons (string event) binding) smart-keys)))) - map))) - (scan diff-hl-command-map) - (smartrep-define-key diff-hl-mode-map diff-hl-command-prefix smart-keys)))) - -;; Integrate with `repeat-mode' in Emacs 28 (https://debbugs.gnu.org/47566) -;; -;; While smartrep feels solid, it looks kinda abandoned. And the -;; chances of it being put into GNU ELPA are slim too. -(map-keymap - (lambda (_key cmd) - (unless (memq cmd diff-hl-repeat-exceptions) - (put cmd 'repeat-map 'diff-hl-command-map))) - diff-hl-command-map) - -(declare-function magit-toplevel "magit-git") -(declare-function magit-unstaged-files "magit-git") - -(defvar diff-hl--magit-unstaged-files nil) - -(defun diff-hl-magit-pre-refresh () - (unless (and diff-hl-disable-on-remote - (file-remote-p default-directory)) - (setq diff-hl--magit-unstaged-files (magit-unstaged-files t)))) - -(defun diff-hl-magit-post-refresh () - (unless (and diff-hl-disable-on-remote - (file-remote-p default-directory)) - (let* ((topdir (magit-toplevel)) - (modified-files - (mapcar (lambda (file) (expand-file-name file topdir)) - (delete-consecutive-dups - (sort - (nconc (magit-unstaged-files t) - diff-hl--magit-unstaged-files) - #'string<)))) - (unmodified-states '(up-to-date ignored unregistered))) - (setq diff-hl--magit-unstaged-files nil) - (dolist (buf (buffer-list)) - (when (and (buffer-local-value 'diff-hl-mode buf) - (not (buffer-modified-p buf)) - ;; Solve the "cloned indirect buffer" problem - ;; (diff-hl-mode could be non-nil there, even if - ;; buffer-file-name is nil): - (buffer-file-name buf) - (file-in-directory-p (buffer-file-name buf) topdir) - (file-exists-p (buffer-file-name buf))) - (with-current-buffer buf - (let* ((file buffer-file-name) - (backend (vc-backend file))) - (when backend - (cond - ((member file modified-files) - (when (memq (vc-state file) unmodified-states) - (vc-state-refresh file backend)) - (diff-hl-update)) - ((not (memq (vc-state file backend) unmodified-states)) - (vc-state-refresh file backend) - (diff-hl-update))))))))))) - -(defun diff-hl-dir-update () - (dolist (pair (if (vc-dir-marked-files) - (vc-dir-marked-only-files-and-states) - (vc-dir-child-files-and-states))) - (when (eq 'up-to-date (cdr pair)) - (let ((buffer (find-buffer-visiting (car pair)))) - (when buffer - (with-current-buffer buffer - (diff-hl-remove-overlays))))))) - -(define-minor-mode diff-hl-dir-mode - "Toggle `diff-hl-mode' integration in a `vc-dir-mode' buffer." - :lighter "" - (if diff-hl-dir-mode - (add-hook 'vc-checkin-hook 'diff-hl-dir-update t t) - (remove-hook 'vc-checkin-hook 'diff-hl-dir-update t))) - -(defun diff-hl-make-temp-file-name (file rev &optional manual) - "Return a backup file name for REV or the current version of FILE. -If MANUAL is non-nil it means that a name for backups created by -the user should be returned." - (let* ((auto-save-file-name-transforms - `((".*" ,temporary-file-directory t))) - (buffer-file-name file)) - (expand-file-name - (concat (make-auto-save-file-name) - ".~" (subst-char-in-string - ?/ ?_ rev) - (unless manual ".") "~") - temporary-file-directory))) - -(defun diff-hl-create-revision (file revision) - "Read REVISION of FILE into a buffer and return the buffer." - (let ((automatic-backup (diff-hl-make-temp-file-name file revision)) - (filebuf (get-file-buffer file)) - (filename (diff-hl-make-temp-file-name file revision 'manual))) - (unless (file-exists-p filename) - (if (file-exists-p automatic-backup) - (rename-file automatic-backup filename nil) - (with-current-buffer filebuf - (let ((coding-system-for-read 'no-conversion) - (coding-system-for-write 'no-conversion)) - (condition-case nil - (with-temp-file filename - (let ((outbuf (current-buffer))) - ;; Change buffer to get local value of - ;; vc-checkout-switches. - (with-current-buffer filebuf - (vc-call find-revision file revision outbuf)))) - (error - (when (file-exists-p filename) - (delete-file filename)))))))) - filename)) - -(defun diff-hl-working-revision (file &optional backend) - "Like vc-working-revision, but always up-to-date" - (vc-file-setprop file 'vc-working-revision - (vc-call-backend (or backend (vc-backend file)) - 'working-revision file))) - -(declare-function diff-no-select "diff") - -(defun diff-hl-diff-buffer-with-reference (file &optional dest-buffer backend context-lines) - "Compute the diff between the current buffer contents and reference in BACKEND. -The diffs are computed in the buffer DEST-BUFFER. This requires -the `diff-program' to be in your `exec-path'. -CONTEXT-LINES is the size of the unified diff context, defaults to 0." - (require 'diff) - (vc-ensure-vc-buffer) - (save-current-buffer - (let* ((dest-buffer (or dest-buffer "*diff-hl-diff-buffer-with-reference*")) - (backend (or backend (vc-backend file))) - (temporary-file-directory - (if (file-directory-p "/dev/shm/") - "/dev/shm/" - temporary-file-directory)) - (rev - (if (and (eq backend 'Git) - (not diff-hl-reference-revision) - (not diff-hl-show-staged-changes)) - (diff-hl-git-index-revision - file - (diff-hl-git-index-object-name file)) - (diff-hl-create-revision - file - (or diff-hl-reference-revision - (diff-hl-working-revision file backend))))) - (switches (format "-U %d --strip-trailing-cr" (or context-lines 0)))) - (diff-no-select rev (current-buffer) switches 'noasync - (get-buffer-create dest-buffer)) - (with-current-buffer dest-buffer - (let ((inhibit-read-only t)) - ;; Function `diff-sentinel' adds a final line, so remove it - (delete-matching-lines "^Diff finished.*"))) - (get-buffer-create dest-buffer)))) - -;; TODO: Cache based on .git/index's mtime, maybe. -(defun diff-hl-git-index-object-name (file) - (with-temp-buffer - (vc-git-command (current-buffer) 0 file "ls-files" "-s") - (and - (goto-char (point-min)) - (re-search-forward "^[0-9]+ \\([0-9a-f]+\\)") - (match-string-no-properties 1)))) - -(defun diff-hl-git-index-revision (file object-name) - (let ((filename (diff-hl-make-temp-file-name file - (concat ":" object-name) - 'manual)) - (filebuf (get-file-buffer file))) - (unless (file-exists-p filename) - (with-current-buffer filebuf - (let ((coding-system-for-read 'no-conversion) - (coding-system-for-write 'no-conversion)) - (condition-case nil - (with-temp-file filename - (let ((outbuf (current-buffer))) - ;; Change buffer to be inside the repo. - (with-current-buffer filebuf - (vc-git-command outbuf 0 nil - "cat-file" "blob" object-name)))) - (error - (when (file-exists-p filename) - (delete-file filename))))))) - filename)) - -;;;###autoload -(defun turn-on-diff-hl-mode () - "Turn on `diff-hl-mode' or `diff-hl-dir-mode' in a buffer if appropriate." - (cond - (buffer-file-name - (unless (and diff-hl-disable-on-remote - (file-remote-p buffer-file-name)) - (diff-hl-mode 1))) - ((eq major-mode 'vc-dir-mode) - (diff-hl-dir-mode 1)))) - -;;;###autoload -(defun diff-hl--global-turn-on () - "Call `turn-on-diff-hl-mode' if the current major mode is applicable." - (when (cond ((eq diff-hl-global-modes t) - t) - ((eq (car-safe diff-hl-global-modes) 'not) - (not (memq major-mode (cdr diff-hl-global-modes)))) - (t (memq major-mode diff-hl-global-modes))) - (turn-on-diff-hl-mode))) - -(declare-function vc-annotate-extract-revision-at-line "vc-annotate") -(declare-function diff-hl-amend-mode "diff-hl-amend") - -;;;###autoload -(defun diff-hl-set-reference-rev (rev) - "Set the reference revision globally to REV. -When called interactively, REV read with completion. - -The default value chosen using one of methods below: - -- In a log view buffer, it uses the revision of current entry. -Call `vc-print-log' or `vc-print-root-log' first to open a log -view buffer. -- In a VC annotate buffer, it uses the revision of current line. -- In other situations, it uses the symbol at point. - -Notice that this sets the reference revision globally, so in -files from other repositories, `diff-hl-mode' will not highlight -changes correctly, until you run `diff-hl-reset-reference-rev'. - -Also notice that this will disable `diff-hl-amend-mode' in -buffers that enables it, since `diff-hl-amend-mode' overrides its -effect." - (interactive - (let* ((def (or (and (equal major-mode 'vc-annotate-mode) - (car (vc-annotate-extract-revision-at-line))) - (log-view-current-tag) - (thing-at-point 'symbol t))) - (prompt (if def - (format "Reference revision (default %s): " def) - "Reference revision: "))) - (list (vc-read-revision prompt nil nil def)))) - (if rev - (message "Set reference revision to %s" rev) - (user-error "No reference revision specified")) - (setq diff-hl-reference-revision rev) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when diff-hl-mode - (when (bound-and-true-p diff-hl-amend-mode) - (diff-hl-amend-mode -1)) - (diff-hl-update))))) - -;;;###autoload -(defun diff-hl-reset-reference-rev () - "Reset the reference revision globally to the most recent one." - (interactive) - (setq diff-hl-reference-revision nil) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when diff-hl-mode - (diff-hl-update))))) - -;;;###autoload -(define-globalized-minor-mode global-diff-hl-mode diff-hl-mode - diff-hl--global-turn-on :after-hook (diff-hl-global-mode-change)) - -(defun diff-hl-global-mode-change () - (unless global-diff-hl-mode - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when diff-hl-dir-mode - (diff-hl-dir-mode -1)))))) - -(provide 'diff-hl) - -;;; diff-hl.el ends here blob - f14e4d71f7af16c237f31115afdc76d6bf848a9d (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/test/diff-hl-test.el +++ /dev/null @@ -1,173 +0,0 @@ -;;; diff-hl-test.el --- tests for diff-hl -*- lexical-binding: t -*- - -;; Copyright (C) 2020, 2021 Free Software Foundation, Inc. - -;; Author: Nathan Moreau - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;;; Code: - -(require 'diff-hl) -(require 'subr-x) ;; string-trim -(require 'ert) -(require 'vc-git) - -(defvar diff-hl-test-source-file - (expand-file-name (concat (file-name-directory (locate-library "diff-hl")) - "test/empty"))) - -(defvar diff-hl-test-initial-content nil) - -(defmacro diff-hl-test-in-source (&rest body) - `(save-window-excursion - (find-file diff-hl-test-source-file) - ,@body)) -(put 'diff-hl-test-in-source 'lisp-indent-function 0) - -(defun diff-hl-test-init () - (diff-hl-test-in-source - (setq diff-hl-test-initial-content (buffer-string))) - t) - -(defun diff-hl-test-teardown () - (diff-hl-test-in-source - (erase-buffer) - (insert diff-hl-test-initial-content) - (save-buffer) - (pcase (vc-backend buffer-file-name) - (`Git - (vc-git-command nil 0 buffer-file-name "reset")) - (`Hg - (vc-hg-command nil 0 buffer-file-name "revert"))))) - -(defun diff-hl-test-compute-diff-lines () - (diff-hl-test-in-source - (save-buffer) - (let ((vc-diff-switches "-w")) - (diff-hl-diff-goto-hunk)) - (switch-to-buffer "*vc-diff*") - (let ((lines nil) - (previous-line (point-min))) - (goto-char (point-min)) - (while (< (point) (point-max)) - (forward-line 1) - (push (string-trim (buffer-substring-no-properties previous-line (point))) lines) - (setq previous-line (point))) - (delq nil (nreverse lines))))) - -(defmacro diff-hl-deftest (name &rest body) - `(ert-deftest ,name () - (diff-hl-test-init) - (unwind-protect - (progn ,@body) - (diff-hl-test-teardown)))) -(put 'diff-hl-deftest 'lisp-indent-function 'defun) - -(diff-hl-deftest diff-hl-insert () - (diff-hl-test-in-source - (goto-char (point-max)) - (insert "added\n") - (should (equal "+added" - (car (last (diff-hl-test-compute-diff-lines))))))) - -(diff-hl-deftest diff-hl-remove () - (diff-hl-test-in-source - (delete-region (point-min) (point-max)) - (should (equal "-last line" - (car (last (diff-hl-test-compute-diff-lines))))))) - -(diff-hl-deftest diff-hl-indirect-buffer-insert () - (diff-hl-test-in-source - (narrow-to-region (point-min) (point-max)) - (goto-char (point-max)) - (insert "added\n") - (should (equal "+added" - (car (last (diff-hl-test-compute-diff-lines))))))) - -(diff-hl-deftest diff-hl-indirect-buffer-remove () - (diff-hl-test-in-source - (narrow-to-region (point-min) (point-max)) - (goto-char (point-min)) - (delete-region (point) (point-max)) - (should (equal "-last line" - (car (last (diff-hl-test-compute-diff-lines))))))) - -(diff-hl-deftest diff-hl-indirect-buffer-move () - (diff-hl-test-in-source - (narrow-to-region (point-min) (point-max)) - (goto-char (point-min)) - (kill-whole-line 3) - (goto-char (point-max)) - (insert "added\n") - (save-buffer) - (diff-hl-mode 1) - (diff-hl-previous-hunk) - (should (looking-at "added")) - (diff-hl-previous-hunk) - (should (looking-at "function2")) - (should-error (diff-hl-previous-hunk) :type 'user-error) - (diff-hl-next-hunk) - (should (looking-at "added")) - (should-error (diff-hl-next-hunk) :type 'user-error))) - -(diff-hl-deftest diff-hl-can-ignore-staged-changes () - (diff-hl-test-in-source - (goto-char (point-min)) - (insert "new line 1\n") - (save-buffer) - (vc-git-command nil 0 buffer-file-name "add") - (goto-char (point-max)) - (insert "new line 2\n") - (save-buffer) - (let ((diff-hl-show-staged-changes t)) - (should - (equal (diff-hl-changes) - '((1 1 insert) - (12 1 insert))))) - (let ((diff-hl-show-staged-changes nil)) - (should - (equal (diff-hl-changes) - '((12 1 insert))))))) - -(diff-hl-deftest diff-hl-flydiff-can-ignore-staged-changes () - (diff-hl-test-in-source - (goto-char (point-min)) - (insert "new line 1\n") - (save-buffer) - (vc-git-command nil 0 buffer-file-name "add") - (goto-char (point-max)) - (insert "new line 2\n") - (let ((diff-hl-show-staged-changes t)) - (should - (equal (diff-hl-changes-from-buffer - (diff-hl-diff-buffer-with-reference buffer-file-name)) - '((1 1 insert) - (12 1 insert))))) - (let ((diff-hl-show-staged-changes nil)) - (should - (equal (diff-hl-changes-from-buffer - (diff-hl-diff-buffer-with-reference buffer-file-name)) - '((12 1 insert))))))) - -(defun diff-hl-run-tests () - (ert-run-tests-batch)) - -(provide 'diff-hl-test) - -;;; diff-hl-test.el ends here blob - 8fa2495840df05eb73531a91c38d8c3312296fc7 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2/test/empty +++ /dev/null @@ -1,10 +0,0 @@ -function1 () { -} - -function2 () { -} - -function3 () { -} - -last line blob - e0a645a551fd1f468389c0f8c3fc75c3046d5420 (mode 644) blob + /dev/null --- elpa/diff-hl-1.9.2.signed +++ /dev/null @@ -1,2 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2023-02-19T11:05:02+0100 using RSA -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2023-02-19T11:05:02+0100 using EDDSA \ No newline at end of file blob - 23d78ef84750589118afdd28176540be5542426a (mode 644) blob + /dev/null --- elpa/diminish-0.46/README.md +++ /dev/null @@ -1,50 +0,0 @@ -[![MELPA](https://melpa.org/packages/diminish-badge.svg)](https://melpa.org/#/diminish) -[![MELPA Stable](http://stable.melpa.org/packages/diminish-badge.svg)](http://stable.melpa.org/#/diminish) - -# diminish.el - -Introduction -============ - -> When we diminish a mode, we are saying we want it to continue doing its -> work for us, but we no longer want to be reminded of it. It becomes a -> night worker, like a janitor; it becomes an invisible man; it remains a -> component, perhaps an important one, sometimes an indispensable one, of -> the mechanism that maintains the day-people's world, but its place in -> their thoughts is diminished, usually to nothing. As we grow old we -> diminish more and more such thoughts, such people, usually to nothing. -> -- Will Mengarini - -This package implements hiding or abbreviation of the mode line displays -(lighters) of minor-modes. - -Quick start -=========== - -```emacs-lisp -(require 'diminish) - -(diminish 'rainbow-mode) ; Hide lighter from mode-line -(diminish 'rainbow-mode " Rbow") ; Replace rainbow-mode lighter with " Rbow" -(diminish 'rainbow-mode 'rainbow-mode-lighter) ; Use raingow-mode-lighter variable value -(diminish 'rainbow-mode '(" " "R-" "bow")) ; Replace rainbow-mode lighter with " R-bow" -(diminish 'rainbow-mode '((" " "R") "/" "bow")) ; Replace rainbow-mode lighter with " R/bow" -(diminish 'rainbow-mode '(:eval (format " Rbow/%s" (+ 2 3)))) ; Replace rainbow-mode lighter with " Rbow/5" -(diminish 'rainbow-mode ; Replace rainbow-mode lighter with greened " Rbow" - '(:propertize " Rbow" face '(:foreground "green"))) -(diminish 'rainbow-mode ; If rainbow-mode-mode-linep is non-nil " Rbow/t" - '(rainbow-mode-mode-linep " Rbow/t" " Rbow/nil")) -(diminish 'rainbow-mode '(3 " Rbow" "/" "s")) ; Replace rainbow-mode lighter with " Rb" -``` - -Ref: [Emacs manual - The Data Structure of the Mode Line](https://www.gnu.org/software/emacs/manual/html_node/elisp/Mode-Line-Data.html). - -John Wiegley's -[use-package](https://github.com/jwiegley/use-package#diminishing-and-delighting-minor-modes) -macro also has support for diminish.el. - -Acknowledgments -=============== - -diminish.el was created by Will Mengarini on 19th of February 1998 and is now -maintained by [Martin Yrjölä](https://github.com/myrjola). blob - 651ba0027de2098af1aa28ba1474408974840ba3 (mode 644) blob + /dev/null --- elpa/diminish-0.46/diminish-autoloads.el +++ /dev/null @@ -1,63 +0,0 @@ -;;; diminish-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from diminish.el - -(autoload 'diminish "diminish" "\ -Diminish mode-line display of minor mode MODE to TO-WHAT (default \"\"). - -Interactively, enter (with completion) the name of any minor mode, followed -on the next line by what you want it diminished to (default empty string). -The response to neither prompt should be quoted. However, in Lisp code, -both args must be quoted, the first as a symbol, the second as a string, -as in (diminish 'jiggle-mode \" Jgl\"). - -The mode-line displays of minor modes usually begin with a space, so -the modes' names appear as separate words on the mode line. However, if -you're having problems with a cramped mode line, you may choose to use single -letters for some modes, without leading spaces. Capitalizing them works -best; if you then diminish some mode to \"X\" but have abbrev-mode enabled as -well, you'll get a display like \"AbbrevX\". This function prepends a space -to TO-WHAT if it's > 1 char long & doesn't already begin with a space. - -(fn MODE &optional TO-WHAT)" t) -(autoload 'diminish-undo "diminish" "\ -Restore mode-line display of diminished mode MODE to its minor-mode value. -Do nothing if the arg is a minor mode that hasn't been diminished. - -Interactively, enter (with completion) the name of any diminished mode (a -mode that was formerly a minor mode on which you invoked \\[diminish]). -To restore all diminished modes to minor status, answer `diminished-modes'. -The response to the prompt shouldn't be quoted. However, in Lisp code, -the arg must be quoted as a symbol, as in (diminish-undo 'diminished-modes). - -(fn MODE)" t) -(autoload 'diminished-modes "diminish" "\ -Echo all active diminished or minor modes as if they were minor. -The display goes in the echo area; if it's too long even for that, -you can see the whole thing in the *Messages* buffer. -This doesn't change the status of any modes; it just lets you see -what diminished modes would be on the mode-line if they were still minor." t) -(register-definition-prefixes "diminish" '("diminish")) - -;;; End of scraped data - -(provide 'diminish-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; diminish-autoloads.el ends here blob - 632cad4c1da532e108d8dcbbe22af56ef6cf5e08 (mode 644) blob + /dev/null --- elpa/diminish-0.46/diminish-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from diminish.el -*- no-byte-compile: t -*- -(define-package "diminish" "0.46" "Diminished modes are minor modes with no modeline display" '((emacs "24.3")) :commit "66b3902401059d161424b1b8d0abc3cb0a7d6df0" :authors '(("Will Mengarini" . "seldon@eskimo.com")) :maintainer '("Martin Yrjölä" . "martin.yrjola@gmail.com") :keywords '("extensions" "diminish" "minor" "codeprose") :url "https://github.com/myrjola/diminish.el") blob - 5da0d27e80d08528df1a53897f241aa933d179be (mode 644) blob + /dev/null --- elpa/diminish-0.46/diminish.el +++ /dev/null @@ -1,294 +0,0 @@ -;;; diminish.el --- Diminished modes are minor modes with no modeline display - -;; Copyright (C) 1998 Free Software Foundation, Inc. - -;; Author: Will Mengarini -;; Maintainer: Martin Yrjölä -;; URL: https://github.com/myrjola/diminish.el -;; Created: Th 19 Feb 98 -;; Version: 0.46 -;; Package-Requires: ((emacs "24.3")) -;; Keywords: extensions, diminish, minor, codeprose - -;; This program 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 2, or (at your option) -;; any later version. - -;; This program 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 -;; this program; see the file LICENSE. If not, write to the write to the Free -;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -;; 02110-1301, USA. - -;;; Commentary: - -;; Minor modes each put a word on the mode line to signify that they're -;; active. This can cause other displays, such as % of file that point is -;; at, to run off the right side of the screen. For some minor modes, such -;; as mouse-avoidance-mode, the display is a waste of space, since users -;; typically set the mode in their .emacs & never change it. For other -;; modes, such as my jiggle-mode, it's a waste because there's already a -;; visual indication of whether the mode is in effect. - -;; A diminished mode is a minor mode that has had its mode line -;; display diminished, usually to nothing, although diminishing to a -;; shorter word or a single letter is also supported. This package -;; implements diminished modes. - -;; You can use this package either interactively or from your .emacs file. -;; In either case, first you'll need to copy this file to a directory that -;; appears in your load-path. `load-path' is the name of a variable that -;; contains a list of directories Emacs searches for files to load. -;; To prepend another directory to load-path, put a line like -;; (add-to-list 'load-path "c:/My_Directory") in your .emacs file. - -;; To create diminished modes interactively, type -;; M-x load-library -;; to get a prompt like -;; Load library: -;; and respond `diminish' (unquoted). Then type -;; M-x diminish -;; to get a prompt like -;; Diminish what minor mode: -;; and respond with the name of some minor mode, like mouse-avoidance-mode. -;; You'll then get this prompt: -;; To what mode-line display: -;; Respond by just hitting if you want the name of the mode -;; completely removed from the mode line. If you prefer, you can abbreviate -;; the name. If your abbreviation is 2 characters or more, such as "Av", -;; it'll be displayed as a separate word on the mode line, just like minor -;; modes' names. If it's a single character, such as "V", it'll be scrunched -;; up against the previous word, so for example if the undiminished mode line -;; display had been "Abbrev Fill Avoid", it would become "Abbrev FillV". -;; Multiple single-letter diminished modes will all be scrunched together. -;; The display of undiminished modes will not be affected. - -;; To find out what the mode line would look like if all diminished modes -;; were still minor, type M-x diminished-modes. This displays in the echo -;; area the complete list of minor or diminished modes now active, but -;; displays them all as minor. They remain diminished on the mode line. - -;; To convert a diminished mode back to a minor mode, type M-x diminish-undo -;; to get a prompt like -;; Restore what diminished mode: -;; Respond with the name of some diminished mode. To convert all -;; diminished modes back to minor modes, respond to that prompt -;; with `diminished-modes' (unquoted, & note the hyphen). - -;; When you're responding to the prompts for mode names, you can use -;; completion to avoid extra typing; for example, m o u SPC SPC SPC -;; is usually enough to specify mouse-avoidance-mode. Mode names -;; typically end in "-mode", but for historical reasons -;; auto-fill-mode is named by "auto-fill-function". - -;; To create diminished modes noninteractively in your .emacs file, put -;; code like -;; (require 'diminish) -;; (diminish 'abbrev-mode "Abv") -;; (diminish 'jiggle-mode) -;; (diminish 'mouse-avoidance-mode "M") -;; near the end of your .emacs file. It should be near the end so that any -;; minor modes your .emacs loads will already have been loaded by the time -;; they're to be converted to diminished modes. - -;; To diminish a major mode, (setq mode-name "whatever") in the mode hook. - -;;; Epigraph: - -;; "The quality of our thoughts is bordered on all sides -;; by our facility with language." -;; --J. Michael Straczynski - -;;; Code: - -(eval-when-compile (require 'cl-lib)) - -(defvar diminish-must-not-copy-minor-mode-alist nil - "Non-nil means loading diminish.el won't (copy-alist minor-mode-alist). -Normally `minor-mode-alist' is setq to that copy on loading diminish because -at least one of its cons cells, that for abbrev-mode, is read-only (see -ELisp Info on \"pure storage\"). If you setq this variable to t & then -try to diminish abbrev-mode under GNU Emacs 19.34, you'll get the error -message \"Attempt to modify read-only object\".") - -(or diminish-must-not-copy-minor-mode-alist - (cl-callf copy-alist minor-mode-alist)) - -(defvar diminished-mode-alist nil - "The original `minor-mode-alist' value of all (diminish)ed modes.") - -(defvar diminish-history-symbols nil - "Command history for symbols of diminished modes.") - -(defvar diminish-history-names nil - "Command history for names of diminished modes.") - -;; When we diminish a mode, we are saying we want it to continue doing its -;; work for us, but we no longer want to be reminded of it. It becomes a -;; night worker, like a janitor; it becomes an invisible man; it remains a -;; component, perhaps an important one, sometimes an indispensable one, of -;; the mechanism that maintains the day-people's world, but its place in -;; their thoughts is diminished, usually to nothing. As we grow old we -;; diminish more and more such thoughts, such people, usually to nothing. - -;; "The wise man knows that to keep under is to endure." The diminished -;; often come to value their invisibility. We speak--speak--of "the strong -;; silent type", but only as a superficiality; a stereotype in a movie, -;; perhaps, but even if an acquaintance, necessarily, by hypothesis, a -;; distant one. The strong silent type is actually a process. It begins -;; with introspection, continues with judgment, and is shaped by the -;; discovery that these judgments are impractical to share; there is no -;; appetite for the wisdom of the self-critical among the creatures of -;; material appetite who dominate our world. Their dominance's Darwinian -;; implications reinforce the self-doubt that is the germ of higher wisdom. -;; The thoughtful contemplate the evolutionary triumph of the predator. -;; Gnostics deny the cosmos could be so evil; this must all be a prank; the -;; thoughtful remain silent, invisible, self-diminished, and discover, -;; perhaps at first in surprise, the freedom they thus gain, and grow strong. - -;;;###autoload -(defun diminish (mode &optional to-what) - "Diminish mode-line display of minor mode MODE to TO-WHAT (default \"\"). - -Interactively, enter (with completion) the name of any minor mode, followed -on the next line by what you want it diminished to (default empty string). -The response to neither prompt should be quoted. However, in Lisp code, -both args must be quoted, the first as a symbol, the second as a string, -as in (diminish 'jiggle-mode \" Jgl\"). - -The mode-line displays of minor modes usually begin with a space, so -the modes' names appear as separate words on the mode line. However, if -you're having problems with a cramped mode line, you may choose to use single -letters for some modes, without leading spaces. Capitalizing them works -best; if you then diminish some mode to \"X\" but have abbrev-mode enabled as -well, you'll get a display like \"AbbrevX\". This function prepends a space -to TO-WHAT if it's > 1 char long & doesn't already begin with a space." - (interactive (list (read (completing-read - "Diminish what minor mode: " - (mapcar (lambda (x) (list (symbol-name (car x)))) - minor-mode-alist) - nil t nil 'diminish-history-symbols)) - (read-from-minibuffer - "To what mode-line display: " - nil nil nil 'diminish-history-names))) - (let ((minor (assq mode minor-mode-alist))) - (when minor - (progn (cl-callf or to-what "") - (when (and (stringp to-what) - (> (length to-what) 1)) - (or (= (string-to-char to-what) ?\ ) - (cl-callf2 concat " " to-what))) - (or (assq mode diminished-mode-alist) - (push (copy-sequence minor) diminished-mode-alist)) - (setcdr minor (list to-what)))))) - -;; But an image comes to me, vivid in its unreality, of a loon alone on his -;; forest lake, shrieking his soul out into a canopy of stars. Alone this -;; afternoon in my warm city apartment, I can feel the bite of his night air, -;; and smell his conifers. In him there is no acceptance of diminishment. - -;; "I have a benevolent habit of pouring out myself to everybody, -;; and would even pay for a listener, and I am afraid -;; that the Athenians may think me too talkative." -;; --Socrates, in the /Euthyphro/ - -;; I remember a news story about a retired plumber who had somehow managed to -;; steal a military tank. He rode it down city streets, rode over a parked -;; car--no one was hurt--rode onto a freeway, that concrete symbol of the -;; American spirit, or so we fancy it, shouting "Plumber Bob! Plumber Bob!". -;; He was shot dead by police. - -;;;###autoload -(defun diminish-undo (mode) - "Restore mode-line display of diminished mode MODE to its minor-mode value. -Do nothing if the arg is a minor mode that hasn't been diminished. - -Interactively, enter (with completion) the name of any diminished mode (a -mode that was formerly a minor mode on which you invoked \\[diminish]). -To restore all diminished modes to minor status, answer `diminished-modes'. -The response to the prompt shouldn't be quoted. However, in Lisp code, -the arg must be quoted as a symbol, as in (diminish-undo 'diminished-modes)." - (interactive - (list (read (completing-read - "Restore what diminished mode: " - (cons (list "diminished-modes") - (mapcar (lambda (x) (list (symbol-name (car x)))) - diminished-mode-alist)) - nil t nil 'diminish-history-symbols)))) - (if (eq mode 'diminished-modes) - (let ((diminished-modes diminished-mode-alist)) - (while diminished-modes - (diminish-undo (caar diminished-modes)) - (cl-callf cdr diminished-modes))) - (let ((minor (assq mode minor-mode-alist)) - (diminished (assq mode diminished-mode-alist))) - (or minor - (error "%S is not currently registered as a minor mode" mode)) - (when diminished - (setcdr minor (cdr diminished)))))) - -;; Plumber Bob was not from Seattle, my grey city, for rainy Seattle is a -;; city of interiors, a city of the self-diminished. When I moved here one -;; sunny June I was delighted to find that ducks and geese were common in -;; the streets. But I hoped to find a loon or two, and all I found were -;; ducks and geese. I wondered about this; I wondered why there were no -;; loons in Seattle; but my confusion resulted from my ignorance of the -;; psychology of rain, which is to say my ignorance of diminished modes. -;; What I needed, and lacked, was a way to discover they were there. - -;;;###autoload -(defun diminished-modes () - "Echo all active diminished or minor modes as if they were minor. -The display goes in the echo area; if it's too long even for that, -you can see the whole thing in the *Messages* buffer. -This doesn't change the status of any modes; it just lets you see -what diminished modes would be on the mode-line if they were still minor." - (interactive) - (let ((minor-modes minor-mode-alist) - message) - (while minor-modes - (when (symbol-value (caar minor-modes)) - ;; This minor mode is active in this buffer - (let* ((mode-pair (car minor-modes)) - (mode (car mode-pair)) - (minor-pair (or (assq mode diminished-mode-alist) mode-pair)) - (minor-name (cadr minor-pair))) - (when (symbolp minor-name) - ;; This minor mode uses symbol indirection in the cdr - (let ((symbols-seen (list minor-name))) - (while (and (symbolp (cl-callf symbol-value minor-name)) - (not (memq minor-name symbols-seen))) - (push minor-name symbols-seen)))) - (push minor-name message))) - (cl-callf cdr minor-modes)) - (setq message (mapconcat 'identity (nreverse message) "")) - (when (= (string-to-char message) ?\ ) - (cl-callf substring message 1)) - (message "%s" message))) - -;; A human mind is a Black Forest of diminished modes. Some are dangerous; -;; most of the mind of an intimate is a secret stranger, and these diminished -;; modes are rendered more unpredictable by their long isolation from the -;; corrective influence of interaction with reality. The student of history -;; learns that this description applies to whole societies as well. In some -;; ways the self-diminished are better able to discern the night worker. -;; They are rendered safer by their heightened awareness of others' -;; diminished modes, and more congenial by the spare blandness of their own -;; mode lines. To some people rain is truly depressing, but others it just -;; makes pensive, and, forcing them indoors where they may not have the -;; luxury of solitude, teaches them to self-diminish. That was what I had -;; not understood when I was searching for loons among the ducks and geese. -;; Loons come to Seattle all the time, but the ones that like it learn to be -;; silent, learn to self-diminish, and take on the colors of ducks and geese. -;; Now, here a dozen years, I can recognize them everywhere, standing quietly -;; in line with the ducks and geese at the espresso counter, gazing placidly -;; out on the world through loon-red eyes, thinking secret thoughts. - -(provide 'diminish) - -;;; diminish.el ends here blob - e3f526fbb03087c5abccf0c328b37c4b769beefd (mode 644) blob + /dev/null --- elpa/diminish-0.46.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2022-01-28T11:05:11+0100 using RSA \ No newline at end of file blob - ec5097cf687443f2c5f19c3cd9069996692f0464 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/.editorconfig +++ /dev/null @@ -1,29 +0,0 @@ -root = true - -[*] -end_of_line = lf -trim_trailing_whitespace = true -insert_final_newline = true - - -[*.el] -indent_style = space -max_line_length = 80 - - -[Makefile] -indent_style = tab -tab_width = 4 - - -[.gitmodules] -indent_style = tab -tab_width = 4 - - -[*.yml] -indent_style = space -indent_size = 2 - -[ert-tests/**/*.ini] -file_type_ext = editorconfig blob - b21179911cc6ec39f5247fdf19290451945da4a6 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/.github/workflows/build.yaml +++ /dev/null @@ -1,71 +0,0 @@ -name: build - -on: - push: - branches: - - master - pull_request: - types: [opened, synchronize] - branches: - - 'master' - release: - types: [published] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - emacs_version: - - "26.3" - - "27.2" - - "28.2" - - "29.3" - experimental: [false] - include: - - os: ubuntu-latest - emacs_version: snapshot - experimental: true - - os: macos-latest - emacs_version: snapshot - experimental: true - # 2023/8/2 Recently this test always fails - # so remove this until it works - # - os: windows-latest - # emacs_version: snapshot - # experimental: true - exclude: - - os: macos-latest - emacs_version: "26.3" - - os: macos-latest - emacs_version: "27.2" - continue-on-error: ${{ matrix.experimental }} - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Setup Emacs - uses: jcs090218/setup-emacs@master - with: - version: ${{ matrix.emacs_version }} - - - uses: emacs-eask/setup-eask@master - with: - version: 'snapshot' - - - name: Run tests (Unix) - if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' - run: make check-unix - - - name: Run tests (Windows) - if: matrix.os == 'windows-latest' - run: make check-dos blob - 08cd608de82ba5cf12f8cb05e25b5538bd1b9372 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/CHANGELOG.md +++ /dev/null @@ -1,416 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - - -## [Unreleased] - -### Added - -### Changed - -### Deprecated - -### Removed - -### Fixed - -### Security - - -## [0.11.0] - 2024-05-11 - -### Added - -- Support new major-modes - - kotlin-ts-mode ([#310]) - - hcl-mode ([#312]) - - protobuf-mode ([#316]) - - lua-ts-mode ([#317]) - - php-ts-mode ([#318]) - - elixir-ts-mode ([#319]) - - jsonian-mode ([#320]) - - swift-mode ([#321]) - - graphql-mode ([#325]) - - zig-mode ([#326]) - -### Changed - -- Remove editorconfig-mode legacy version ([#304]) - - Remove flag `editorconfig--legacy-version`, which was defined in [#263] -- Separate some utility commands to new file ([#330]) - - Following commands are now defined in `editoroconfig-tools.el`, not `editorconfig.el` - - editorconfig-apply - - editorconfig-mode-apply - - editorconfig-find-current-editorconfig - - editorconfig-display-current-properties (and its alias describe-editorconfig-properties) - - editorconfig-format-buffer - - These commands are configured to be autoloaded functions, except for `editorconfig-mode-apply` - - -## [0.10.1] - 2023-05-19 - -### Fixed - -- Fix when-let (again) ([#305]) -- Fix compile warning of python-mode offset ([#306]) - - -## [0.10.0] - 2023-05-07 - -### Added - -- Enable indentation for tree-sitter based typescript mode ([#282]) -- Add support for json-ts-mode ([#283]) -- Add support for some treesit modes ([#287]) -- Add indent variable associations for numerous tree-sitter modes ([#290]) -- Add js-ts-mode' spec to editorconfig-indentation-alist' ([#293]) -- Add bash-ts-mode to editorconfig-indentation-alist ([#296]) -- Add support for gdscript-mode ([#300]) - -### Changed - -- Drop Emacs 24.x and 25.x ([#286]) - -### Fixed - -- Fix write-file-functions default value ([#295]) -- Check mode-class property for special modes ([#301]) -- Load subr-x when compiling ([#302]) - - -## [0.9.1] - 2022-11-07 - -### Fixed - -- Check filename rather than buffer-file-name for consistency ([#280]) - - -## [0.9.0] - 2022-10-23 - -### Changed - -- Use new implementation by default ([#263]) - - Set `(setq editorconfig--legacy-version t)` to use previous one - - -## [0.8.2] - 2021-08-13 - -### Added - -- Add rustic-mode to editorconfig-indentation-alist ([#208]) -- Add conf-mode abbrev-table definitions ([#220]) -- Add meson-mode indentation rule ([#253]) -- Add support for rjsx-mode ([#254]) -- Update README for NonGNU ELPA repository ([#259]) -- Add new implementation of editorconfig-mode ([#248], [#250], [#251], [#255], [#258], [#260]) - - By default this is disabled: set `(setq editorconfig--enable-20210221-testing t)` to use this - -### Fixed - -- Fix so that "?" does not match "/" ([#211]) -- Fix document typo ([#213]) -- Don't make unchanged vars buffer-local ([#222]) -- Silence byte-compiler warnings ([#235]) -- Use revert-buffer-with-coding-system to set coding system ([#236]) -- Do not run editorconfig-apply on recentf-save-file ([#241]) -- Skip special-mode buffers when applying ([#247]) -- Stop excluding remote files by default ([#234], [#245]) -- Fix editorconfig execution for remote hosts via tramp ([#249]) -- Add minor fixes to tests ([#252]) -- Fix excluding the recentf-save-file when in a symlinked directory ([#256]) - -### Changed - -- Define -mode-apply as an interactive command ([#216]) -- Use elisp core by default ([#209]) -- User functions in the hooks `editorconfig-hack-properties-functions` and - `editorconfig-after-apply-functions` can no longer distinguish explicitly - unset properties from ones that were never set in the first place. ([#222]) - - -## [0.8.1] - 2019-10-10 - -### Added - -- Add indentation support - - [#196] - - enh-ruby-mode - - haxor-mode - - mips-mode - - nasm-mode - - terra-mode - - kotlin-mode - - bpftrace-mode ([#199]) - - f90-mode ([#200]) -- Add explicit support for rpm-spec-mode ([efc1ff4], see [#197] ) -- Add whitelist for file_type_emacs value ([#204]) - - -## [0.8.0] - 2019-03-26 - -### Fixed - -- Allow library forget properties order ([#187]) -- Use API to get version info ([#193]) - - `editorconfig-version()` was added and `editorconfig-core-version` removed -- Update docs and metadata to follow MELPA guidelines ([#189]) -- Refactor ([#188], [#191]) - - -## [0.7.14] - 2018-12-25 - -### Added - -- Add feature to decide major-mode from file_type_ext [EXPERIMENTAL] [#175] ([#178]) ([#179]) ([#180]) -- Add feature to hack properties before applying [#182] -- Add variable editorconfig-trim-whitespaces-mode [#183] - - Useful when you want to use non-default mode like `ws-butler` to trim spaces - -### Fixed - -- Make conf-mode used when a file has .editorconfig extension [01a0640] -- Fix tests -- Fix docs - -### Changed - -- Change hook name -custom-hooks -> -after-apply-functions [bb4bc44] - - -## [0.7.13] - 2018-08-23 - -### Fixed - -- Check editorconfig configs when read only state changes ([#168]) -- use CURDIR instead of PWD in Makefile ([#170]) -- Refactor fnmatch-p ([#171]) -- Update tests - - -## [0.7.12] - 2018-06-20 - -### Added - -- Add /Fix major-mode support - - pug-mode [#149] - - csharp-mode [#154] -- Add variable to disable lisp-indent-offset sometimes [#155] -- Add texinfo doc [#159] - -### Fixed - -- Avoid passing a non-absolute file path to editorconfig(1) [#151] -- Use "-with-signature" coding systems for all UTF-16 charsets [#158] -- Allow normal whitespace when reading EditorConfig settings file [#162] -- Add some fixes to tests - - -## [0.7.11] - 2017-11-07 - -### Added - -- Add /Fix major-mode support - - apache-mode - - groovy-mode - - web-mode -- Add support for a custom lighter -- Add editorconfig-format-buffer function -- Add experimental file_type_emacs support - -### Changed - -- Change hook editorconfig is applied on - - -## [0.7.10] - 2017-06-07 - -*Undocumented* - -## [0.7.9] - 2017-02-22 - -*Undocumented* - -## [0.7.8] - 2016-08-09 - -*Undocumented* - -## [0.7.7] - 2016-07-19 - -*Undocumented* - -## [0.7.6] - 2016-05-05 - -*Undocumented* - -## [0.7.5] - 2016-04-22 - -*Undocumented* - -## [0.7.4] - 2016-03-31 - -*Undocumented* - -## [0.7.3] - 2016-02-12 - -*Undocumented* - -## [0.7.2] - 2016-01-27 - -*Undocumented* - -## [0.7.1] - 2016-01-24 - -*Undocumented* - -## [0.7.0] - 2016-01-17 - -*Undocumented* - -## [0.6.2] - 2016-01-15 - -*Undocumented* - -## [0.6.1] - 2015-12-09 - -*Undocumented* - -## [0.6] - 2015-12-04 - -*Undocumented* - -## [0.5] - 2015-11-04 - -*Undocumented* - -## [0.4] - 2014-12-20 - -*Undocumented* - -## [0.3] - 2014-05-07 - -*Undocumented* - -## [0.2] - 2013-06-06 - -*Undocumented* - -## [0.1] - 2012-02-07 - -*Undocumented* - - -[Unreleased]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.11.0...HEAD -[0.11.0]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.10.1...v0.11.0 -[0.10.1]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.10.0...v0.10.1 -[0.10.0]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.9.1...v0.10.0 -[0.9.1]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.9.0...v0.9.1 -[0.9.0]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.8.2...v0.9.0 -[0.8.2]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.8.1...v0.8.2 -[0.8.1]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.8.0...v0.8.1 -[0.8.0]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.14...v0.8.0 -[0.7.14]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.13...v0.7.14 -[0.7.13]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.12...v0.7.13 -[0.7.12]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.11...v0.7.12 -[0.7.11]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.10...v0.7.11 -[0.7.10]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.9...v0.7.10 -[0.7.9]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.8...v0.7.9 -[0.7.8]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.7...v0.7.8 -[0.7.7]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.6...v0.7.7 -[0.7.6]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.5...v0.7.6 -[0.7.5]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.4...v0.7.5 -[0.7.4]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.3...v0.7.4 -[0.7.3]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.2...v0.7.3 -[0.7.2]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.1...v0.7.2 -[0.7.1]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.7.0...v0.7.1 -[0.7.0]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.6.2...v0.7.0 -[0.6.2]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.6.1...v0.6.2 -[0.6.1]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.6...v0.6.1 -[0.6]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.5...v0.6 -[0.5]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.4...v0.5 -[0.4]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.3...v0.4 -[0.3]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.2...v0.3 -[0.2]: https://github.com/editorconfig/editorconfig-emacs/compare/v0.1...v0.2 -[0.1]: https://github.com/editorconfig/editorconfig-emacs/releases/tag/v0.1 -[#330]: https://github.com/editorconfig/editorconfig-emacs/issues/330 -[#326]: https://github.com/editorconfig/editorconfig-emacs/issues/326 -[#325]: https://github.com/editorconfig/editorconfig-emacs/issues/325 -[#321]: https://github.com/editorconfig/editorconfig-emacs/issues/321 -[#320]: https://github.com/editorconfig/editorconfig-emacs/issues/320 -[#319]: https://github.com/editorconfig/editorconfig-emacs/issues/319 -[#318]: https://github.com/editorconfig/editorconfig-emacs/issues/318 -[#317]: https://github.com/editorconfig/editorconfig-emacs/issues/317 -[#316]: https://github.com/editorconfig/editorconfig-emacs/issues/316 -[#312]: https://github.com/editorconfig/editorconfig-emacs/issues/312 -[#310]: https://github.com/editorconfig/editorconfig-emacs/issues/310 -[#304]: https://github.com/editorconfig/editorconfig-emacs/issues/304 -[#306]: https://github.com/editorconfig/editorconfig-emacs/issues/306 -[#305]: https://github.com/editorconfig/editorconfig-emacs/issues/305 -[#302]: https://github.com/editorconfig/editorconfig-emacs/issues/302 -[#301]: https://github.com/editorconfig/editorconfig-emacs/issues/301 -[#300]: https://github.com/editorconfig/editorconfig-emacs/issues/300 -[#296]: https://github.com/editorconfig/editorconfig-emacs/issues/296 -[#295]: https://github.com/editorconfig/editorconfig-emacs/issues/295 -[#293]: https://github.com/editorconfig/editorconfig-emacs/issues/293 -[#290]: https://github.com/editorconfig/editorconfig-emacs/issues/290 -[#287]: https://github.com/editorconfig/editorconfig-emacs/issues/287 -[#286]: https://github.com/editorconfig/editorconfig-emacs/issues/286 -[#283]: https://github.com/editorconfig/editorconfig-emacs/issues/283 -[#282]: https://github.com/editorconfig/editorconfig-emacs/issues/282 -[#280]: https://github.com/editorconfig/editorconfig-emacs/issues/280 -[#263]: https://github.com/editorconfig/editorconfig-emacs/issues/263 -[#260]: https://github.com/editorconfig/editorconfig-emacs/issues/260 -[#258]: https://github.com/editorconfig/editorconfig-emacs/issues/258 -[#255]: https://github.com/editorconfig/editorconfig-emacs/issues/255 -[#251]: https://github.com/editorconfig/editorconfig-emacs/issues/251 -[#250]: https://github.com/editorconfig/editorconfig-emacs/issues/250 -[#248]: https://github.com/editorconfig/editorconfig-emacs/issues/248 -[#259]: https://github.com/editorconfig/editorconfig-emacs/issues/259 -[#256]: https://github.com/editorconfig/editorconfig-emacs/issues/256 -[#252]: https://github.com/editorconfig/editorconfig-emacs/issues/252 -[#249]: https://github.com/editorconfig/editorconfig-emacs/issues/249 -[#245]: https://github.com/editorconfig/editorconfig-emacs/issues/245 -[#234]: https://github.com/editorconfig/editorconfig-emacs/issues/234 -[#241]: https://github.com/editorconfig/editorconfig-emacs/issues/241 -[#236]: https://github.com/editorconfig/editorconfig-emacs/issues/236 -[#235]: https://github.com/editorconfig/editorconfig-emacs/issues/235 -[#222]: https://github.com/editorconfig/editorconfig-emacs/issues/222 -[#222]: https://github.com/editorconfig/editorconfig-emacs/issues/222 -[#220]: https://github.com/editorconfig/editorconfig-emacs/issues/220 -[#216]: https://github.com/editorconfig/editorconfig-emacs/issues/216 -[#213]: https://github.com/editorconfig/editorconfig-emacs/issues/213 -[#211]: https://github.com/editorconfig/editorconfig-emacs/issues/211 -[#209]: https://github.com/editorconfig/editorconfig-emacs/issues/209 -[#208]: https://github.com/editorconfig/editorconfig-emacs/issues/208 -[#204]: https://github.com/editorconfig/editorconfig-emacs/issues/204 -[#200]: https://github.com/editorconfig/editorconfig-emacs/issues/200 -[#199]: https://github.com/editorconfig/editorconfig-emacs/issues/199 -[#197]: https://github.com/editorconfig/editorconfig-emacs/issues/197 -[#196]: https://github.com/editorconfig/editorconfig-emacs/issues/196 -[#193]: https://github.com/editorconfig/editorconfig-emacs/issues/193 -[#191]: https://github.com/editorconfig/editorconfig-emacs/issues/191 -[#189]: https://github.com/editorconfig/editorconfig-emacs/issues/189 -[#188]: https://github.com/editorconfig/editorconfig-emacs/issues/188 -[#187]: https://github.com/editorconfig/editorconfig-emacs/issues/187 -[#183]: https://github.com/editorconfig/editorconfig-emacs/issues/183 -[#182]: https://github.com/editorconfig/editorconfig-emacs/issues/182 -[#180]: https://github.com/editorconfig/editorconfig-emacs/issues/180 -[#179]: https://github.com/editorconfig/editorconfig-emacs/issues/179 -[#178]: https://github.com/editorconfig/editorconfig-emacs/issues/178 -[#175]: https://github.com/editorconfig/editorconfig-emacs/issues/175 -[#171]: https://github.com/editorconfig/editorconfig-emacs/issues/171 -[#170]: https://github.com/editorconfig/editorconfig-emacs/issues/170 -[#168]: https://github.com/editorconfig/editorconfig-emacs/issues/168 -[#162]: https://github.com/editorconfig/editorconfig-emacs/issues/162 -[#159]: https://github.com/editorconfig/editorconfig-emacs/issues/159 -[#158]: https://github.com/editorconfig/editorconfig-emacs/issues/158 -[#155]: https://github.com/editorconfig/editorconfig-emacs/issues/155 -[#154]: https://github.com/editorconfig/editorconfig-emacs/issues/154 -[#151]: https://github.com/editorconfig/editorconfig-emacs/issues/151 -[#149]: https://github.com/editorconfig/editorconfig-emacs/issues/149 -[01a0640]: https://github.com/editorconfig/editorconfig-emacs/commit/01a064015ed8d00f2853f966f07d2be5b97bfe5e -[efc1ff4]: https://github.com/editorconfig/editorconfig-emacs/commit/efc1ff4b1c3422d6e231b1c01138becab4b9eded -[bb4bc44]: https://github.com/editorconfig/editorconfig-emacs/commit/bb4bc4497783e6607480cd0b761f974136784fdd blob - 492a943f829f92a53ea9b6c3404ca14b5cbadbab (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/CONTRIBUTORS +++ /dev/null @@ -1,11 +0,0 @@ -Contributors to EditorConfig Emacs plugin (chronological order): - -Trey Hunner -Jonas Bernoulli -Johan Sundström -Desmond O. Chang -Steve Jordan -Hong Xu -10sr -Usami Kenta -Izaak "Zaak" Beekman blob - 3ac3e5f297c66b9b52bff3b934dbad55ef636080 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/Eask +++ /dev/null @@ -1,20 +0,0 @@ -(package "editorconfig" - "0.11.0" - "EditorConfig Emacs Plugin") - -(website-url "https://github.com/editorconfig/editorconfig-emacs#readme") -(keywords "convenience" "editorconfig") - -(package-file "editorconfig.el") - -(files "editorconfig-*.el") - -(script "test" "echo \"Error: no test specified\" && exit 1") - -(source "gnu") -(source "melpa") - -(depends-on "emacs" "26.1") -(depends-on "nadvice") - -(setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 blob - 963f6de2ede09db7ebfd92aa4d443960ee878692 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/Makefile +++ /dev/null @@ -1,81 +0,0 @@ -# -*- Makefile -*- - -TEXI_CHAPTER := EditorConfig Emacs Plugin - -EMACS = emacs -EASK = eask -PANDOC = pandoc -AWK = awk - -PROJECT_ROOT_DIR = $(CURDIR) -ERT_TESTS = $(wildcard $(PROJECT_ROOT_DIR)/ert-tests/*.el) - -# Compile with noninteractive and relatively clean environment. -BATCHFLAGS = -batch -q --no-site-file -L $(PROJECT_ROOT_DIR) - -MAIN_SRC = editorconfig.el -SRCS = $(wildcard $(PROJECT_ROOT_DIR)/*.el) -OBJS = $(SRCS:.el=.elc) - -.PHONY: check-unix check-dos \ - compile clean \ - test test-ert test-core \ - sandbox doc - -# CI entry -check-unix: package install compile test -check-dos: package install compile test-ert - -package: - $(EASK) package - -install: - $(EASK) install - -compile: - $(EASK) compile - -clean: - $(EASK) clean elc - - -doc: doc/editorconfig.texi - -doc/editorconfig.texi: README.md doc/header.txt - mkdir -p doc - tail -n +4 $< | $(PANDOC) -s -f markdown -t texinfo -o $@.body - $(AWK) 'f{print} /^@chapter $(TEXI_CHAPTER)/{f=1;print}' $@.body >$@.body2 - cat doc/header.txt $@.body2 >$@ - rm -f $@.body $@.body2 - -test: test-ert test-core - $(EMACS) $(BATCHFLAGS) -l editorconfig.el - - -# ert test -test-ert: $(ERT_TESTS) $(OBJS) - $(EMACS) $(BATCHFLAGS) \ - --eval "(setq debug-on-error t)" \ - --eval "(require 'ert)" \ - --eval "(setq metadata-el-files '($(MAIN_SRC:%=\"%\")))" \ - $(ERT_TESTS:%=-l "%") \ - -f ert-run-tests-batch-and-exit - - - -# Core test -core-test/CMakeLists.txt: - git submodule init - -test-core: core-test/CMakeLists.txt $(OBJS) - cd $(PROJECT_ROOT_DIR)/core-test && \ - cmake -DEDITORCONFIG_CMD="$(PROJECT_ROOT_DIR)/bin/editorconfig-el" . - cd $(PROJECT_ROOT_DIR)/core-test && \ - EMACS_BIN=$(EMACS) EDITORCONFIG_CORE_LIBRARY_PATH="$(PROJECT_ROOT_DIR)" \ - ctest --output-on-failure . - - -# Start Emacs that loads *.el in current directory and does not load the user -# init file -sandbox: - $(EMACS) -q -L $(PROJECT_ROOT_DIR) $(MAIN_SRC:%=-l "%") blob - 6ace73f6fe81a0db5d05dd1179d95cd69bdb6617 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/README.md +++ /dev/null @@ -1,224 +0,0 @@ -![build](https://github.com/editorconfig/editorconfig-emacs/workflows/build/badge.svg) -[![MELPA](https://melpa.org/packages/editorconfig-badge.svg)](http://melpa.org/#/editorconfig) -[![MELPA Stable](https://stable.melpa.org/packages/editorconfig-badge.svg)](https://stable.melpa.org/#/editorconfig) -[![NonGNU ELPA](http://elpa.nongnu.org/nongnu/editorconfig.svg)](http://elpa.nongnu.org/nongnu/editorconfig.html) - -# EditorConfig Emacs Plugin - -This is an [EditorConfig][] plugin for [Emacs][]. - -## Getting Started - -### package.el - -This package is available from [MELPA][], [MELPA Stable][] and [NonGNU ELPA][]. -Install from these repositories and enable global minor-mode `editorconfig-mode`: - -```emacs-lisp -(editorconfig-mode 1) -``` - -Normally, enabling `editorconfig-mode` should be enough for this plugin to work: -all other configurations are optional. -This mode sets up hooks so that EditorConfig properties will be -loaded and applied to the new buffers automatically when visiting files. - -### use-package - -If you use [**use-package**][use-package], add the following to your -`init.el` file: - -```emacs-lisp -(use-package editorconfig - :ensure t - :config - (editorconfig-mode 1)) -``` - -### Manual installation - -Copy all `.el` files in this repository to `~/.emacs.d/lisp` and add the -following: - -```emacs-lisp -(add-to-list 'load-path "~/.emacs.d/lisp") -(require 'editorconfig) -(editorconfig-mode 1) -``` - -## Supported properties - -Current Emacs plugin coverage for EditorConfig's [properties][]: - -* `indent_style` -* `indent_size` -* `tab_width` -* `end_of_line` -* `charset` -* `trim_trailing_whitespace` -* `insert_final_newline = true` is supported -* ~~`insert_final_newline = false`~~ is not enforced - (as in trailing newlines actually being removed automagically), - we just buffer-locally override any preferences that would auto-add them - to files `.editorconfig` marks as trailing-newline-free -* `max_line_length` -* ~~`file_type_ext` (Experimental)~~ (See below) -* ~~`file_type_emacs` (Experimental)~~ (See below) -* `root` (only used by EditorConfig core) - -Not yet covered properties marked with ~~over-strike~~ -– pull requests implementing missing features warmly welcomed! -Typically, you will want to tie these to native functionality, -or the configuration of existing packages handling the feature. - -As several packages have their own handling of, say, indentation, -we might not yet cover some mode you use, but we try to add the -ones that show up on our radar. - -### ~~File Type (file_type_ext, file_type_emacs)~~ - -File-type feature is currently disabled, because this package is now undergoing -big internal refactoring. -For those who want this functionality, -please consider using [editorconfig-custom-majormode](https://github.com/10sr/editorconfig-custom-majormode-el). - -## Customize - -`editorconfig-emacs` provides some customize variables. - -Here are some of these variables: for the full list of available variables, -type M-x customize-group [RET] editorconfig [RET]. - -### `editorconfig-trim-whitespaces-mode` - -Buffer local minor-mode to use to trim trailing whitespaces. - -If set, editorconfig will enable/disable this mode in accord with -`trim_trailing_whitespace` property in `.editorconfig`. -Otherwise, use Emacs built-in `delete-trailing-whitespace` function. - -One possible value is -[`ws-butler-mode`](https://github.com/lewang/ws-butler), with which -only lines touched get trimmed. To use it, add following to your -init.el: - -``` emacs-lisp -(setq editorconfig-trim-whitespaces-mode - 'ws-butler-mode) -``` - -### `editorconfig-after-apply-functions` - -(Formerly `editorconfig-custom-hooks`) - -A list of functions which will be called after loading common EditorConfig settings, -when you can set some custom variables. - -For example, `web-mode` has several variables for indentation offset size and -EditorConfig sets them at once by `indent_size`. You can stop indenting -only blocks of `web-mode` by adding following to your init.el: - -```emacs-lisp -(add-hook 'editorconfig-after-apply-functions - (lambda (props) (setq web-mode-block-padding 0))) -``` - -## Troubleshooting - -Enabling `editorconfig-mode` should be enough for normal cases. - -When EditorConfig properties are not effective for unknown reason, we recommend -first trying `M-x editorconfig-display-current-properties`. - -This command will open a new buffer and display the EditorConfig properties -loaded for current buffer. -You can check if EditorConfig properties were not read for buffers at all, -or they were loaded but did not take effect for some other reasons. - -### Indentation for new major-modes - -Because most Emacs major-modes have their own indentation settings, this plugin -requires explicit support for each major-mode for `indent_size` property. - -By default this plugin ships with settings for many major-modes, but, -sorry to say, it cannot be perfect. Especially it is difficult to support -brand-new major-modes. -Please feel free to submit issue or pull-request for such major-mode! - -Supported major-modes and their indentation configs are defined in the variable -`editorconfig-indentation-alist`. - -### Not work at all for FOO-mode! - -Most cases properties are loaded just after visiting files when -`editorconfig-mode` is enabled. -But it is known that there are major-modes that this mechanism does not work -for and require explicit call of `editorconfig-apply`. - -Typically it will occur when the major-mode is not defined using -`define-derived-mode` (`rpm-spec-mode` is an example for this). -Please feel free to submit issues if you find such modes! - -### `editorconfig-format-buffer` does not work well with lsp-mode - -By default, [lsp-mode][] configures indent-region-function so that Emacs uses -language servers' `textDocument/rangeFormatting` request to format text in -buffers. -So EditorConfig settings are ignored unless language servers -themselves support loading configs from `.editorconfig`. - -To avoid this behavior ad-hocly, set `lsp-enable-indentation` to nil. - -## Submitting Bugs and Feature Requests - -Bugs, feature requests, and other issues should be submitted to the issue -tracker: https://github.com/editorconfig/editorconfig-emacs/issues - -### Development - -To run the test locally, you will need the following tools: - -- Make -- [CMake][] -- [Eask][] - -If you are on `Linux` or `macOS`: - - $ make check-unix - -On `Windows`: - - $ make check-dos - -To start a new Emacs process with current `*.el` and without loading user init -file, run: - - $ make sandbox - -## License - -EditorConfig Emacs Plugin 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 of the -License, or (at your option) any later version. - -This program 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 this program. If not, see . - - -[Emacs]: https://www.gnu.org/software/emacs/ -[MELPA]: https://melpa.org/#/editorconfig -[MELPA Stable]: https://stable.melpa.org/#/editorconfig -[NonGNU ELPA]: http://elpa.nongnu.org/nongnu/editorconfig.html -[use-package]: https://www.emacswiki.org/emacs/UsePackage -[EditorConfig]: https://editorconfig.org -[EditorConfig C Core]: https://github.com/editorconfig/editorconfig-core-c -[properties]: https://editorconfig.org/#supported-properties -[CMake]: https://cmake.org -[Eask]: https://github.com/emacs-eask/cli -[lsp-mode]: https://github.com/emacs-lsp/lsp-mode blob - 37389027bca563d2da626c3fa5c64dcd70dfb2b8 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/dir +++ /dev/null @@ -1,18 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs -* EditorConfig: (editorconfig). EditorConfig Emacs Plugin. blob - 5e7d3b85552e2ae6f4f777efca1d94a8bedcc7cd (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/doc/editorconfig.texi +++ /dev/null @@ -1,297 +0,0 @@ -@dircategory Emacs -@direntry -* EditorConfig: (editorconfig). EditorConfig Emacs Plugin. -@end direntry - -@node Top -@chapter EditorConfig Emacs Plugin -@anchor{#editorconfig-emacs-plugin} -This is an @uref{https://editorconfig.org,EditorConfig} plugin for -@uref{https://www.gnu.org/software/emacs/,Emacs}. - -@menu -* Getting Started:: -* Supported properties:: -* Customize:: -* Troubleshooting:: -* Submitting Bugs and Feature Requests:: -* License:: -@end menu - -@node Getting Started -@section Getting Started -@anchor{#getting-started} - -@menu -* packageel:: -* use-package:: -* Manual installation:: -@end menu - -@node packageel -@subsection package.el -@anchor{#package.el} -This package is available from -@uref{https://melpa.org/#/editorconfig,MELPA}, -@uref{https://stable.melpa.org/#/editorconfig,MELPA Stable} and -@uref{http://elpa.nongnu.org/nongnu/editorconfig.html,NonGNU ELPA}. -Install from these repositories and enable global minor-mode -@code{editorconfig-mode}: - -@verbatim -(editorconfig-mode 1) -@end verbatim - -Normally, enabling @code{editorconfig-mode} should be enough for this -plugin to work: all other configurations are optional. This mode sets up -hooks so that EditorConfig properties will be loaded and applied to the -new buffers automatically when visiting files. - -@node use-package -@subsection use-package -@anchor{#use-package} -If you use -@uref{https://www.emacswiki.org/emacs/UsePackage,@strong{use-package}}, -add the following to your @code{init.el} file: - -@verbatim -(use-package editorconfig - :ensure t - :config - (editorconfig-mode 1)) -@end verbatim - -@node Manual installation -@subsection Manual installation -@anchor{#manual-installation} -Copy all @code{.el} files in this repository to @code{~/.emacs.d/lisp} -and add the following: - -@verbatim -(add-to-list 'load-path "~/.emacs.d/lisp") -(require 'editorconfig) -(editorconfig-mode 1) -@end verbatim - -@node Supported properties -@section Supported properties -@anchor{#supported-properties} -Current Emacs plugin coverage for EditorConfig's -@uref{https://editorconfig.org/#supported-properties,properties}: - -@itemize -@item -@code{indent_style} -@item -@code{indent_size} -@item -@code{tab_width} -@item -@code{end_of_line} -@item -@code{charset} -@item -@code{trim_trailing_whitespace} -@item -@code{insert_final_newline = true} is supported -@item -@code{insert_final_newline = false} -is not enforced (as in trailing newlines actually being removed -automagically), we just buffer-locally override any preferences that -would auto-add them to files @code{.editorconfig} marks as -trailing-newline-free -@item -@code{max_line_length} -@item -@code{file_type_ext} (Experimental) -(See below) -@item -@code{file_type_emacs} (Experimental) -(See below) -@item -@code{root} (only used by EditorConfig core) -@end itemize - -Not yet covered properties marked with over-strike -- pull requests -implementing missing features warmly welcomed! Typically, you will want -to tie these to native functionality, or the configuration of existing -packages handling the feature. - -As several packages have their own handling of, say, indentation, we -might not yet cover some mode you use, but we try to add the ones that -show up on our radar. - -@menu -* File Type file_type_ext file_type_emacs:: -@end menu - -@node File Type file_type_ext file_type_emacs -@subsection File Type (file_type_ext, file_type_emacs) -@anchor{#file-type-file_type_ext-file_type_emacs} -File-type feature is currently disabled, because this package is now -undergoing big internal refactoring. For those who want this -functionality, please consider using -@uref{https://github.com/10sr/editorconfig-custom-majormode-el,editorconfig-custom-majormode}. - -@node Customize -@section Customize -@anchor{#customize} -@code{editorconfig-emacs} provides some customize variables. - -Here are some of these variables: for the full list of available -variables, type M-x customize-group [RET] editorconfig [RET]. - -@menu -* editorconfig-trim-whitespaces-mode:: -* editorconfig-after-apply-functions:: -* editorconfig-hack-properties-functions:: -@end menu - -@node editorconfig-trim-whitespaces-mode -@subsection @code{editorconfig-trim-whitespaces-mode} -@anchor{#editorconfig-trim-whitespaces-mode} -Buffer local minor-mode to use to trim trailing whitespaces. - -If set, editorconfig will enable/disable this mode in accord with -@code{trim_trailing_whitespace} property in @code{.editorconfig}. -Otherwise, use Emacs built-in @code{delete-trailing-whitespace} -function. - -One possible value is -@uref{https://github.com/lewang/ws-butler,@code{ws-butler-mode}}, with -which only lines touched get trimmed. To use it, add following to your -init.el: - -@verbatim -(setq editorconfig-trim-whitespaces-mode - 'ws-butler-mode) -@end verbatim - -@node editorconfig-after-apply-functions -@subsection @code{editorconfig-after-apply-functions} -@anchor{#editorconfig-after-apply-functions} -(Formerly @code{editorconfig-custom-hooks}) - -A list of functions which will be called after loading common -EditorConfig settings, when you can set some custom variables. - -For example, @code{web-mode} has several variables for indentation -offset size and EditorConfig sets them at once by @code{indent_size}. -You can stop indenting only blocks of @code{web-mode} by adding -following to your init.el: - -@verbatim -(add-hook 'editorconfig-after-apply-functions - (lambda (props) (setq web-mode-block-padding 0))) -@end verbatim - -@node editorconfig-hack-properties-functions -@subsection @code{editorconfig-hack-properties-functions} -@anchor{#editorconfig-hack-properties-functions} -A list of functions to alter property values before applying them. - -These functions will be run after loading ".editorconfig" files and -before applying them to current buffer, so that you can alter some -properties from ".editorconfig" before they take effect. - -For example, Makefile files always use tab characters for indentation: -you can overwrite "indent_style" property when current @code{major-mode} -is @code{makefile-mode}: - -@verbatim -(add-hook 'editorconfig-hack-properties-functions - '(lambda (props) - (when (derived-mode-p 'makefile-mode) - (puthash 'indent_style "tab" props)))) -@end verbatim - -@node Troubleshooting -@section Troubleshooting -@anchor{#troubleshooting} -Enabling @code{editorconfig-mode} should be enough for normal cases. - -When EditorConfig properties are not effective for unknown reason, we -recommend first trying -@code{M-x editorconfig-display-current-properties}. - -This command will open a new buffer and display the EditorConfig -properties loaded for current buffer. You can check if EditorConfig -properties were not read for buffers at all, or they were loaded but did -not take effect for some other reasons. - -@menu -* Indentation for new major-modes:: -* Not work at all for FOO-mode!:: -@end menu - -@node Indentation for new major-modes -@subsection Indentation for new major-modes -@anchor{#indentation-for-new-major-modes} -Because most Emacs major-modes have their own indentation settings, this -plugin requires explicit support for each major-mode for -@code{indent_size} property. - -By default this plugin ships with settings for many major-modes, but, -sorry to say, it cannot be perfect. Especially it is difficult to -support brand-new major-modes. Please feel free to submit issue or -pull-request for such major-mode! - -Supported major-modes and their indentation configs are defined in the -variable @code{editorconfig-indentation-alist}. - -@node Not work at all for FOO-mode! -@subsection Not work at all for FOO-mode! -@anchor{#not-work-at-all-for-foo-mode} -Most cases properties are loaded just after visiting files when -@code{editorconfig-mode} is enabled. But it is known that there are -major-modes that this mechanism does not work for and require explicit -call of @code{editorconfig-apply}. - -Typically it will occur when the major-mode is not defined using -@code{define-derived-mode} (@code{rpm-spec-mode} is an example for -this). Please feel free to submit issues if you find such modes! - -@node Submitting Bugs and Feature Requests -@section Submitting Bugs and Feature Requests -@anchor{#submitting-bugs-and-feature-requests} -Bugs, feature requests, and other issues should be submitted to the -issue tracker: https://github.com/editorconfig/editorconfig-emacs/issues - -@menu -* Development:: -@end menu - -@node Development -@subsection Development -@anchor{#development} -Make and @uref{https://cmake.org,CMake} must be installed to run the -tests locally: - -@verbatim -$ make check -@end verbatim - -To start a new Emacs process with current @code{*.el} and without -loading user init file, run: - -@verbatim -$ make sandbox -@end verbatim - -@node License -@section License -@anchor{#license} -EditorConfig Emacs Plugin 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 of the -License, or (at your option) any later version. - -This program 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 this program. If not, see @url{https://www.gnu.org/licenses/}. - -@bye blob - 8aa7143c5e844330511335a5e58e4fb329ebf327 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/doc/header.txt +++ /dev/null @@ -1,6 +0,0 @@ -@dircategory Emacs -@direntry -* EditorConfig: (editorconfig). EditorConfig Emacs Plugin. -@end direntry - -@node Top blob - 0658318ed5306cfc1ce3c79400afb3a26abcb9b3 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/editorconfig-autoloads.el +++ /dev/null @@ -1,148 +0,0 @@ -;;; editorconfig-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from editorconfig.el - -(defvar editorconfig-mode nil "\ -Non-nil if Editorconfig mode is enabled. -See the `editorconfig-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `editorconfig-mode'.") -(custom-autoload 'editorconfig-mode "editorconfig" nil) -(autoload 'editorconfig-mode "editorconfig" "\ -Toggle EditorConfig feature. - -To disable EditorConfig in some buffers, modify -`editorconfig-exclude-modes' or `editorconfig-exclude-regexps'. - -This is a global minor mode. If called interactively, toggle the -`Editorconfig mode' mode. If the prefix argument is positive, -enable the mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='editorconfig-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(autoload 'editorconfig-version "editorconfig" "\ -Get EditorConfig version as string. - -If called interactively or if SHOW-VERSION is non-nil, show the -version in the echo area and the messages buffer. - -(fn &optional SHOW-VERSION)" t) -(register-definition-prefixes "editorconfig" '("editorconfig-")) - - -;;; Generated autoloads from editorconfig-conf-mode.el - -(autoload 'editorconfig-conf-mode "editorconfig-conf-mode" "\ -Major mode for editing .editorconfig files. - -(fn)" t) -(add-to-list 'auto-mode-alist '("\\.editorconfig\\'" . editorconfig-conf-mode)) -(register-definition-prefixes "editorconfig-conf-mode" '("editorconfig-conf-mode-")) - - -;;; Generated autoloads from editorconfig-core.el - -(autoload 'editorconfig-core-get-nearest-editorconfig "editorconfig-core" "\ -Return path to .editorconfig file that is closest to DIRECTORY. - -(fn DIRECTORY)") -(autoload 'editorconfig-core-get-properties "editorconfig-core" "\ -Get EditorConfig properties for FILE. -If FILE is not given, use currently visiting file. -Give CONFNAME for basename of config file other than .editorconfig. -If need to specify config format version, give CONFVERSION. - -This function returns an alist of properties. Each element will -look like (KEY . VALUE). - -(fn &optional FILE CONFNAME CONFVERSION)") -(autoload 'editorconfig-core-get-properties-hash "editorconfig-core" "\ -Get EditorConfig properties for FILE. -If FILE is not given, use currently visiting file. -Give CONFNAME for basename of config file other than .editorconfig. -If need to specify config format version, give CONFVERSION. - -This function is almost same as `editorconfig-core-get-properties', but returns -hash object instead. - -(fn &optional FILE CONFNAME CONFVERSION)") -(register-definition-prefixes "editorconfig-core" '("editorconfig-core--")) - - -;;; Generated autoloads from editorconfig-core-handle.el - -(register-definition-prefixes "editorconfig-core-handle" '("editorconfig-core-handle")) - - -;;; Generated autoloads from editorconfig-fnmatch.el - -(autoload 'editorconfig-fnmatch-p "editorconfig-fnmatch" "\ -Test whether STRING match PATTERN. - -Matching ignores case if `case-fold-search' is non-nil. - -PATTERN should be a shell glob pattern, and some zsh-like wildcard matchings can -be used: - -* Matches any string of characters, except path separators (/) -** Matches any string of characters -? Matches any single character -[name] Matches any single character in name -[^name] Matches any single character not in name -{s1,s2,s3} Matches any of the strings given (separated by commas) -{min..max} Matches any number between min and max - -(fn STRING PATTERN)") -(register-definition-prefixes "editorconfig-fnmatch" '("editorconfig-fnmatch-")) - - -;;; Generated autoloads from editorconfig-tools.el - -(autoload 'editorconfig-apply "editorconfig-tools" "\ -Get and apply EditorConfig properties to current buffer. - -This function does not respect the values of `editorconfig-exclude-modes' and -`editorconfig-exclude-regexps' and always applies available properties. -Use `editorconfig-mode-apply' instead to make use of these variables." t) -(autoload 'editorconfig-find-current-editorconfig "editorconfig-tools" "\ -Find the closest .editorconfig file for current file." t) -(autoload 'editorconfig-display-current-properties "editorconfig-tools" "\ -Display EditorConfig properties extracted for current buffer." t) -(defalias 'describe-editorconfig-properties 'editorconfig-display-current-properties) -(autoload 'editorconfig-format-buffer "editorconfig-tools" "\ -Format buffer according to .editorconfig indent_style and indent_width." t) -(register-definition-prefixes "editorconfig-tools" '("editorconfig-mode-apply")) - -;;; End of scraped data - -(provide 'editorconfig-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; editorconfig-autoloads.el ends here blob - 2b4ddd4410f6a21e8d274292b63963355e6a8807 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/editorconfig-conf-mode.el +++ /dev/null @@ -1,95 +0,0 @@ -;;; editorconfig-conf-mode.el --- Major mode for editing .editorconfig files -*- lexical-binding: t -*- - -;; Copyright (C) 2011-2024 EditorConfig Team - -;; Author: EditorConfig Team - -;; See -;; https://github.com/editorconfig/editorconfig-emacs/graphs/contributors -;; or the CONTRIBUTORS file for the list of contributors. - -;; This file is part of EditorConfig Emacs Plugin. - -;; EditorConfig Emacs Plugin 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 of the License, or (at your -;; option) any later version. - -;; EditorConfig Emacs Plugin 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 -;; EditorConfig Emacs Plugin. If not, see . - -;;; Commentary: - -;; Major mode for editing .editorconfig files. - -;;; Code: - -(require 'conf-mode) - -(defvar editorconfig-conf-mode-syntax-table - (let ((table (make-syntax-table conf-unix-mode-syntax-table))) - (modify-syntax-entry ?\; "<" table) - table) - "Syntax table in use in `editorconfig-conf-mode' buffers.") - -(defvar editorconfig-conf-mode-abbrev-table nil - "Abbrev table in use in `editorconfig-conf-mode' buffers.") -(define-abbrev-table 'editorconfig-conf-mode-abbrev-table ()) - -;;;###autoload -(define-derived-mode editorconfig-conf-mode conf-unix-mode "Conf[EditorConfig]" - "Major mode for editing .editorconfig files." - (set-variable 'indent-line-function 'indent-relative) - (let ((key-property-list - '("charset" - "end_of_line" - "file_type_emacs" - "file_type_ext" - "indent_size" - "indent_style" - "insert_final_newline" - "max_line_length" - "root" - "tab_width" - "trim_trailing_whitespace")) - (key-value-list - '("unset" - "true" - "false" - "lf" - "cr" - "crlf" - "space" - "tab" - "latin1" - "utf-8" - "utf-8-bom" - "utf-16be" - "utf-16le")) - (font-lock-value - '(("^[ \t]*\\[\\(.+?\\)\\]" 1 font-lock-type-face) - ("^[ \t]*\\(.+?\\)[ \t]*[=:]" 1 font-lock-variable-name-face)))) - - ;; Highlight all key values - (dolist (key-value key-value-list) - (push `(,(format "[=:][ \t]*\\(%s\\)\\([ \t]\\|$\\)" key-value) - 1 font-lock-constant-face) - font-lock-value)) - ;; Highlight all key properties - (dolist (key-property key-property-list) - (push `(,(format "^[ \t]*\\(%s\\)[ \t]*[=:]" key-property) - 1 font-lock-builtin-face) - font-lock-value)) - - (conf-mode-initialize "#" font-lock-value))) - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.editorconfig\\'" . editorconfig-conf-mode)) - -(provide 'editorconfig-conf-mode) -;;; editorconfig-conf-mode.el ends here blob - d225e1456ba325c14e0392659ccdc77b8351eb06 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/editorconfig-core-handle.el +++ /dev/null @@ -1,243 +0,0 @@ -;;; editorconfig-core-handle.el --- Handle Class for EditorConfig File -*- lexical-binding: t -*- - -;; Copyright (C) 2011-2024 EditorConfig Team - -;; Author: EditorConfig Team - -;; See -;; https://github.com/editorconfig/editorconfig-emacs/graphs/contributors -;; or the CONTRIBUTORS file for the list of contributors. - -;; This file is part of EditorConfig Emacs Plugin. - -;; EditorConfig Emacs Plugin 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 of the License, or (at your -;; option) any later version. - -;; EditorConfig Emacs Plugin 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 -;; EditorConfig Emacs Plugin. If not, see . - -;;; Commentary: - -;; Handle structures for EditorConfig config file. This library is used -;; internally from editorconfig-core.el . - -;;; Code: - -(require 'cl-lib) - -(require 'editorconfig-fnmatch) - -(defvar editorconfig-core-handle--cache-hash - (make-hash-table :test 'equal) - "Hash of EditorConfig filename and its `editorconfig-core-handle' instance.") - -(cl-defstruct editorconfig-core-handle-section - "Structure representing one section in a .editorconfig file. - -Slots: - -`name' - String of section name (glob string). - -`props' - Alist of properties: (KEY . VALUE)." - (name nil) - (props nil)) - -(defun editorconfig-core-handle-section-get-properties (section file dir) - "Return properties alist when SECTION name match FILE. - -DIR should be the directory where .editorconfig file which has SECTION lives. -IF not match, return nil." - (when (editorconfig-core-handle--fnmatch-p - file (editorconfig-core-handle-section-name section) dir) - (editorconfig-core-handle-section-props section))) - -(cl-defstruct editorconfig-core-handle - "Structure representing an .editorconfig file. - -Slots: -`top-props' - Alist of top properties like ((\"root\" . \"true\")) - -`sections' - List of `editorconfig-core-handle-section' structure objects. - -`mtime' - Last modified time of .editorconfig file. - -`path' - Absolute path to .editorconfig file.' -" - (top-props nil) - (sections nil) - (mtime nil) - (path nil)) - - -(defun editorconfig-core-handle (conf) - "Return EditorConfig handle for CONF, which should be a file path. - -If CONF does not exist return nil." - (when (file-readable-p conf) - (let ((cached (gethash conf editorconfig-core-handle--cache-hash)) - (mtime (nth 5 (file-attributes conf)))) - (if (and cached - (equal (editorconfig-core-handle-mtime cached) mtime)) - cached - (let ((parsed (editorconfig-core-handle--parse-file conf))) - (puthash conf - (make-editorconfig-core-handle :top-props (plist-get parsed :top-props) - :sections (plist-get parsed :sections) - :mtime mtime - :path conf) - editorconfig-core-handle--cache-hash)))))) - -(defun editorconfig-core-handle-root-p (handle) - "Return non-nil if HANDLE represent root EditorConfig file. - -If HANDLE is nil return nil." - (when handle - (string-equal "true" - (downcase (or (cdr (assoc "root" - (editorconfig-core-handle-top-props handle))) - ""))))) - -(defun editorconfig-core-handle-get-properties (handle file) - "Return list of alist of properties from HANDLE for FILE. -The list returned will be ordered by the lines they appear. - -If HANDLE is nil return nil." - (when handle - (let ((dir (file-name-directory (editorconfig-core-handle-path handle)))) - (cl-loop for section in (editorconfig-core-handle-sections handle) - for props = (editorconfig-core-handle-section-get-properties section - file - dir) - when props collect (copy-alist props))))) -(make-obsolete 'editorconfig-core-handle-get-properties - 'editorconfig-core-handle-get-properties-hash - "0.8.0") - - -(defun editorconfig-core-handle-get-properties-hash (handle file) - "Return hash of properties from HANDLE for FILE. - -If HANDLE is nil return nil." - (when handle - (let ((hash (make-hash-table)) - (dir (file-name-directory (editorconfig-core-handle-path - handle)))) - (dolist (section (editorconfig-core-handle-sections handle)) - (cl-loop for (key . value) in (editorconfig-core-handle-section-get-properties section file dir) - do (puthash (intern key) value hash))) - hash))) - -(defun editorconfig-core-handle--fnmatch-p (name pattern dir) - "Return non-nil if NAME match PATTERN. -If pattern has slash, pattern should be relative to DIR. - -This function is a fnmatch with a few modification for EditorConfig usage." - (if (string-match-p "/" pattern) - (let ((pattern (replace-regexp-in-string "^/" "" pattern)) - (dir (file-name-as-directory dir))) - (editorconfig-fnmatch-p name (concat dir pattern))) - (editorconfig-fnmatch-p name (concat "**/" pattern)))) - -(defsubst editorconfig-core-handle--string-trim (str) - "Remove leading and trailing whitespaces from STR." - (replace-regexp-in-string "[[:space:]]+\\'" - "" - (replace-regexp-in-string "\\`[[:space:]]+" - "" - str))) - -(defun editorconfig-core-handle--parse-file (conf) - "Parse EditorConfig file CONF. - -This function returns cons of its top properties alist and -alist of patterns and its properties alist. -The list returned will be ordered by the lines they appear. - -If CONF is not found return nil." - (when (file-readable-p conf) - (with-temp-buffer - ;; NOTE: Use this instead of insert-file-contents-literally to enable - ;; code conversion - (insert-file-contents conf) - (goto-char (point-min)) - (let ((point-max (point-max)) - (sections ()) - (top-props nil) - - ;; String of current line - (line "") - ;; nil when pattern not appeared yet, "" when pattern is empty ("[]") - (pattern nil) - ;; Alist of properties for current PATTERN - (props ()) - - ;; Current line num - (current-line-number 1)) - (while (not (eq (point) point-max)) - (setq line - (buffer-substring-no-properties (line-beginning-position) - (line-end-position))) - (setq line - (replace-regexp-in-string "\\(^\\| \\)\\(#\\|;\\).*$" - "" - (editorconfig-core-handle--string-trim line))) - - (cond - ((string-equal "" line) - nil) - - ;; Start of section - ((string-match "^\\[\\(.*\\)\\]$" - line) - (when pattern - (setq sections - `(,@sections ,(make-editorconfig-core-handle-section - :name pattern - :props props))) - (setq pattern nil) - (setq props nil)) - (setq pattern (match-string 1 line))) - - (t - (let ((idx (string-match "=\\|:" line))) - (unless idx - (error "Error while reading config file: %s:%d:\n %s\n" - conf current-line-number line)) - (let ((key (downcase (editorconfig-core-handle--string-trim - (substring line 0 idx)))) - (value (editorconfig-core-handle--string-trim - (substring line (1+ idx))))) - (when (and (< (length key) 51) - (< (length value) 256)) - (if pattern - (when (< (length pattern) 4097) - (setq props - `(,@props (,key . ,value)))) - (setq top-props - `(,@top-props (,key . ,value))))))))) - (setq current-line-number (1+ current-line-number)) - (goto-char (point-min)) - (forward-line (1- current-line-number))) - (when pattern - (setq sections - `(,@sections ,(make-editorconfig-core-handle-section - :name pattern - :props props)))) - (list :top-props top-props - :sections sections))))) - -(provide 'editorconfig-core-handle) -;;; editorconfig-core-handle.el ends here blob - c7b52deaafd893efcd2ee6b6d40d942e22092fb7 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/editorconfig-core.el +++ /dev/null @@ -1,182 +0,0 @@ -;;; editorconfig-core.el --- EditorConfig Core library in Emacs Lisp -*- lexical-binding: t -*- - -;; Copyright (C) 2011-2024 EditorConfig Team - -;; Author: EditorConfig Team - -;; See -;; https://github.com/editorconfig/editorconfig-emacs/graphs/contributors -;; or the CONTRIBUTORS file for the list of contributors. - -;; This file is part of EditorConfig Emacs Plugin. - -;; EditorConfig Emacs Plugin 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 of the License, or (at your -;; option) any later version. - -;; EditorConfig Emacs Plugin 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 -;; EditorConfig Emacs Plugin. If not, see . - -;;; Commentary: - -;; This library is one implementation of EditorConfig Core, which parses -;; .editorconfig files and returns properties for given files. -;; This can be used in place of, for example, editorconfig-core-c. - - -;; Use from EditorConfig Emacs Plugin - -;; Emacs plugin (v0.5 or later) can utilize this implementation. -;; By default, the plugin first search for any EditorConfig executable, -;; and fallback to this library if not found. -;; If you always want to use this library, add following lines to your init.el: - -;; (setq editorconfig-get-properties-function -;; 'editorconfig-core-get-properties-hash) - - -;; Functions - -;; editorconfig-core-get-properties (&optional file confname confversion) - -;; Get EditorConfig properties for FILE. - -;; If FILE is not given, use currently visiting file. -;; Give CONFNAME for basename of config file other than .editorconfig. -;; If need to specify config format version, give CONFVERSION. - -;; This functions returns alist of properties. Each element will look like -;; (KEY . VALUE) . - - -;; editorconfig-core-get-properties-hash (&optional file confname confversion) - -;; Get EditorConfig properties for FILE. - -;; This function is almost same as `editorconfig-core-get-properties', but -;; returns hash object instead. - -;;; Code: - -(require 'cl-lib) - -(require 'editorconfig-core-handle) - -(eval-when-compile - (require 'subr-x)) - - -(defun editorconfig-core--get-handles (dir confname &optional result) - "Get list of EditorConfig handlers for DIR from CONFNAME. - -In the resulting list, the handle for root config file comes first, and the -nearest comes last. -The list may contains nil when no file was found for directories. -RESULT is used internally and normally should not be used." - (setq dir (expand-file-name dir)) - (let ((handle (editorconfig-core-handle (concat (file-name-as-directory dir) - confname))) - (parent (file-name-directory (directory-file-name dir)))) - (if (or (string= parent dir) - (and handle (editorconfig-core-handle-root-p handle))) - (cl-remove-if-not 'identity (cons handle result)) - (editorconfig-core--get-handles parent - confname - (cons handle result))))) - -;;;###autoload -(defun editorconfig-core-get-nearest-editorconfig (directory) - "Return path to .editorconfig file that is closest to DIRECTORY." - (when-let* ((handle (car (last - (editorconfig-core--get-handles directory - ".editorconfig"))))) - (editorconfig-core-handle-path handle))) - -;;;###autoload -(defun editorconfig-core-get-properties (&optional file confname confversion) - "Get EditorConfig properties for FILE. -If FILE is not given, use currently visiting file. -Give CONFNAME for basename of config file other than .editorconfig. -If need to specify config format version, give CONFVERSION. - -This function returns an alist of properties. Each element will -look like (KEY . VALUE)." - (let ((hash (editorconfig-core-get-properties-hash file confname confversion)) - (result nil)) - (maphash (lambda (key value) - (add-to-list 'result (cons (symbol-name key) value))) - hash) - result)) - -(defun editorconfig-core--hash-merge (into update) - "Merge two hashes INTO and UPDATE. - -This is a destructive function, hash INTO will be modified. -When the same key exists in both two hashes, values of UPDATE takes precedence." - (maphash (lambda (key value) (puthash key value into)) update) - into) - -;;;###autoload -(defun editorconfig-core-get-properties-hash (&optional file confname confversion) - "Get EditorConfig properties for FILE. -If FILE is not given, use currently visiting file. -Give CONFNAME for basename of config file other than .editorconfig. -If need to specify config format version, give CONFVERSION. - -This function is almost same as `editorconfig-core-get-properties', but returns -hash object instead." - (setq file - (expand-file-name (or file - buffer-file-name - (error "FILE is not given and `buffer-file-name' is nil")))) - (setq confname (or confname ".editorconfig")) - (setq confversion (or confversion "0.12.0")) - (let ((result (make-hash-table))) - (dolist (handle (editorconfig-core--get-handles (file-name-directory file) - confname)) - (editorconfig-core--hash-merge result - (editorconfig-core-handle-get-properties-hash handle - file))) - - ;; Downcase known boolean values - (dolist (key '( end_of_line indent_style indent_size insert_final_newline - trim_trailing_whitespace charset)) - (when-let* ((val (gethash key result))) - (puthash key (downcase val) result))) - - ;; Add indent_size property - (let ((v-indent-size (gethash 'indent_size result)) - (v-indent-style (gethash 'indent_style result))) - (when (and (not v-indent-size) - (string= v-indent-style "tab") - ;; If VERSION < 0.9.0, indent_size should have no default value - (version<= "0.9.0" - confversion)) - (puthash 'indent_size - "tab" - result))) - ;; Add tab_width property - (let ((v-indent-size (gethash 'indent_size result)) - (v-tab-width (gethash 'tab_width result))) - (when (and v-indent-size - (not v-tab-width) - (not (string= v-indent-size "tab"))) - (puthash 'tab_width v-indent-size result))) - ;; Update indent-size property - (let ((v-indent-size (gethash 'indent_size result)) - (v-tab-width (gethash 'tab_width result))) - (when (and v-indent-size - v-tab-width - (string= v-indent-size "tab")) - (puthash 'indent_size v-tab-width result))) - - result)) - -(provide 'editorconfig-core) -;;; editorconfig-core.el ends here blob - 25a344dc2ef9d65a9c4dc06bf44cfee6bb6dbcc0 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/editorconfig-fnmatch.el +++ /dev/null @@ -1,284 +0,0 @@ -;;; editorconfig-fnmatch.el --- Glob pattern matching in Emacs lisp -*- lexical-binding: t -*- - -;; Copyright (C) 2011-2024 EditorConfig Team - -;; Author: EditorConfig Team - -;; See -;; https://github.com/editorconfig/editorconfig-emacs/graphs/contributors -;; or the CONTRIBUTORS file for the list of contributors. - -;; This file is part of EditorConfig Emacs Plugin. - -;; EditorConfig Emacs Plugin 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 of the License, or (at your -;; option) any later version. - -;; EditorConfig Emacs Plugin 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 -;; EditorConfig Emacs Plugin. If not, see . - -;;; Commentary: - -;; editorconfig-fnmatch.el provides a fnmatch implementation with a few -;; extensions. -;; The main usage of this library is glob pattern matching for EditorConfig, but -;; it can also act solely. - -;; editorconfig-fnmatch-p (name pattern) - -;; Test whether NAME match PATTERN. - -;; PATTERN should be a shell glob pattern, and some zsh-like wildcard matchings -;; can be used: - -;; * Matches any string of characters, except path separators (/) -;; ** Matches any string of characters -;; ? Matches any single character -;; [name] Matches any single character in name -;; [^name] Matches any single character not in name -;; {s1,s2,s3} Matches any of the strings given (separated by commas) -;; {min..max} Matches any number between min and max - - -;; This library is a port from editorconfig-core-py library. -;; https://github.com/editorconfig/editorconfig-core-py/blob/master/editorconfig/fnmatch.py - -;;; Code: - -(require 'cl-lib) - -(defvar editorconfig-fnmatch--cache-hashtable - nil - "Cache of shell pattern and its translation.") -;; Clear cache on file reload -(setq editorconfig-fnmatch--cache-hashtable - (make-hash-table :test 'equal)) - - -(defconst editorconfig-fnmatch--left-brace-regexp - "\\(^\\|[^\\]\\){" - "Regular expression for left brace ({).") - -(defconst editorconfig-fnmatch--right-brace-regexp - "\\(^\\|[^\\]\\)}" - "Regular expression for right brace (}).") - - -(defconst editorconfig-fnmatch--numeric-range-regexp - "\\([+-]?[0-9]+\\)\\.\\.\\([+-]?[0-9]+\\)" - "Regular expression for numeric range (like {-3..+3}).") - -(defun editorconfig-fnmatch--match-num (regexp string) - "Return how many times REGEXP is found in STRING." - (let ((num 0)) - ;; START arg does not work as expected in this case - (while (string-match regexp string) - (setq num (1+ num) - string (substring string (match-end 0)))) - num)) - -;;;###autoload -(defun editorconfig-fnmatch-p (string pattern) - "Test whether STRING match PATTERN. - -Matching ignores case if `case-fold-search' is non-nil. - -PATTERN should be a shell glob pattern, and some zsh-like wildcard matchings can -be used: - -* Matches any string of characters, except path separators (/) -** Matches any string of characters -? Matches any single character -[name] Matches any single character in name -[^name] Matches any single character not in name -{s1,s2,s3} Matches any of the strings given (separated by commas) -{min..max} Matches any number between min and max" - (string-match (editorconfig-fnmatch-translate pattern) - string)) - -;;(editorconfig-fnmatch-translate "{a,{-3..3}}.js") -;;(editorconfig-fnmatch-p "1.js" "{a,{-3..3}}.js") - -(defun editorconfig-fnmatch-translate (pattern) - "Translate a shell PATTERN to a regular expression. - -Translation result will be cached, so same translation will not be done twice." - (let ((cached (gethash pattern - editorconfig-fnmatch--cache-hashtable))) - (or cached - (puthash pattern - (editorconfig-fnmatch--do-translate pattern) - editorconfig-fnmatch--cache-hashtable)))) - - -(defun editorconfig-fnmatch--do-translate (pattern &optional nested) - "Translate a shell PATTERN to a regular expression. - -Set NESTED to t when this function is called from itself. - -This function is called from `editorconfig-fnmatch-translate', when no cached -translation is found for PATTERN." - (let ((index 0) - (length (length pattern)) - (brace-level 0) - (in-brackets nil) - ;; List of strings of resulting regexp - (result ()) - (is-escaped nil) - (matching-braces (= (editorconfig-fnmatch--match-num - editorconfig-fnmatch--left-brace-regexp - pattern) - (editorconfig-fnmatch--match-num - editorconfig-fnmatch--right-brace-regexp - pattern))) - - current-char - pos - has-slash - has-comma - num-range) - - (while (< index length) - (if (and (not is-escaped) - (string-match "[^]\\*?[{},/\\-]+" - ;;(string-match "[^]\\*?[{},/\\-]+" "?.a") - pattern - index) - (eq index (match-beginning 0))) - (setq result `(,@result ,(regexp-quote (match-string 0 pattern))) - index (match-end 0) - is-escaped nil) - - (setq current-char (aref pattern index) - index (1+ index)) - - (cl-case current-char - (?* - (setq pos index) - (if (and (< pos length) - (= (aref pattern pos) ?*)) - (setq result `(,@result ".*")) - (setq result `(,@result "[^/]*")))) - - (?? - (setq result `(,@result "[^/]"))) - - (?\[ - (if in-brackets - (setq result `(,@result "\\[")) - (if (= (aref pattern index) ?/) - ;; Slash after an half-open bracket - (setq result `(,@result "\\[/") - index (+ index 1)) - (setq pos index - has-slash nil) - (while (and (< pos length) - (not (= (aref pattern pos) ?\])) - (not has-slash)) - (if (and (= (aref pattern pos) ?/) - (not (= (aref pattern (- pos 1)) ?\\))) - (setq has-slash t) - (setq pos (1+ pos)))) - (if has-slash - (setq result `(,@result ,(concat "\\[" - (substring pattern - index - (1+ pos)) - "\\]")) - index (+ pos 2)) - (if (and (< index length) - (memq (aref pattern index) - '(?! ?^))) - (setq index (1+ index) - result `(,@result "[^")) - (setq result `(,@result "["))) - (setq in-brackets t))))) - - (?- - (if in-brackets - (setq result `(,@result "-")) - (setq result `(,@result "\\-")))) - - (?\] - (setq result `(,@result "]") - in-brackets nil)) - - (?{ - (setq pos index - has-comma nil) - (while (and (or (and (< pos length) - (not (= (aref pattern pos) ?}))) - is-escaped) - (not has-comma)) - (if (and (eq (aref pattern pos) ?,) - (not is-escaped)) - (setq has-comma t) - (setq is-escaped (and (eq (aref pattern pos) - ?\\) - (not is-escaped)) - pos (1+ pos)))) - (if (and (not has-comma) - (< pos length)) - (let ((pattern-sub (substring pattern index pos))) - (setq num-range (string-match editorconfig-fnmatch--numeric-range-regexp - pattern-sub)) - (if num-range - (let ((number-start (string-to-number (match-string 1 - pattern-sub))) - (number-end (string-to-number (match-string 2 - pattern-sub)))) - (setq result `(,@result ,(concat "\\(?:" - (mapconcat 'number-to-string - (cl-loop for i from number-start to number-end - collect i) - "\\|") - "\\)")))) - (let ((inner (editorconfig-fnmatch--do-translate pattern-sub t))) - (setq result `(,@result ,(format "{%s}" inner))))) - (setq index (1+ pos))) - (if matching-braces - (setq result `(,@result "\\(?:") - brace-level (1+ brace-level)) - (setq result `(,@result "{"))))) - - (?, - (if (and (> brace-level 0) - (not is-escaped)) - (setq result `(,@result "\\|")) - (setq result `(,@result "\\,")))) - - (?} - (if (and (> brace-level 0) - (not is-escaped)) - (setq result `(,@result "\\)") - brace-level (- brace-level 1)) - (setq result `(,@result "}")))) - - (?/ - (if (and (<= (+ index 3) (length pattern)) - (string= (substring pattern index (+ index 3)) "**/")) - (setq result `(,@result "\\(?:/\\|/.*/\\)") - index (+ index 3)) - (setq result `(,@result "/")))) - - (t - (unless (= current-char ?\\) - (setq result `(,@result ,(regexp-quote (char-to-string current-char))))))) - - (if (= current-char ?\\) - (progn (when is-escaped - (setq result `(,@result "\\\\"))) - (setq is-escaped (not is-escaped))) - (setq is-escaped nil)))) - (unless nested - (setq result `("^" ,@result "\\'"))) - (apply #'concat result))) - -(provide 'editorconfig-fnmatch) -;;; editorconfig-fnmatch.el ends here blob - 5388bb2d1f8a48e9477f9bb5d269f3b51e5fef8d (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/editorconfig-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from editorconfig.el -*- no-byte-compile: t -*- -(define-package "editorconfig" "0.11.0" "EditorConfig Emacs Plugin" '((emacs "26.1") (nadvice "0.3")) :commit "f1531bab5b57e40759167b7e5db49acbbc09972f" :authors '(("EditorConfig Team" . "editorconfig@googlegroups.com")) :maintainer '("EditorConfig Team" . "editorconfig@googlegroups.com") :keywords '("convenience" "editorconfig") :url "https://github.com/editorconfig/editorconfig-emacs#readme") blob - 12c40577143b91b42639905224106ebbdbc9af67 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/editorconfig-tools.el +++ /dev/null @@ -1,132 +0,0 @@ -;;; editorconfig-tools.el --- Editorconfig tools -*- lexical-binding: t -*- - -;; Copyright (C) 2011-2024 EditorConfig Team - -;; Author: EditorConfig Team - -;; See -;; https://github.com/editorconfig/editorconfig-emacs/graphs/contributors -;; or the CONTRIBUTORS file for the list of contributors. - -;; This file is part of EditorConfig Emacs Plugin. - -;; EditorConfig Emacs Plugin 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 of the License, or (at your -;; option) any later version. - -;; EditorConfig Emacs Plugin 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 -;; EditorConfig Emacs Plugin. If not, see . - -;;; Commentary: - -;; Some utility commands for users, not used from editorconfig-mode. - -;;; Code: - -(require 'cl-lib) - -(eval-when-compile - (require 'subr-x)) - - -(require 'editorconfig) - -;;;###autoload -(defun editorconfig-apply () - "Get and apply EditorConfig properties to current buffer. - -This function does not respect the values of `editorconfig-exclude-modes' and -`editorconfig-exclude-regexps' and always applies available properties. -Use `editorconfig-mode-apply' instead to make use of these variables." - (interactive) - (when buffer-file-name - (condition-case err - (progn - (let ((props (editorconfig-call-get-properties-function buffer-file-name))) - (condition-case err - (run-hook-with-args 'editorconfig-hack-properties-functions props) - (error - (display-warning '(editorconfig editorconfig-hack-properties-functions) - (format "Error while running editorconfig-hack-properties-functions, abort running hook: %S" - err) - :warning))) - (setq editorconfig-properties-hash props) - (editorconfig-set-local-variables props) - (editorconfig-set-coding-system-revert - (gethash 'end_of_line props) - (gethash 'charset props)) - (condition-case err - (run-hook-with-args 'editorconfig-after-apply-functions props) - (error - (display-warning '(editorconfig editorconfig-after-apply-functions) - (format "Error while running editorconfig-after-apply-functions, abort running hook: %S" - err) - :warning))))) - (error - (display-warning '(editorconfig editorconfig-apply) - (format "Error in editorconfig-apply, styles will not be applied: %S" err) - :error))))) - -(defun editorconfig-mode-apply () - "Get and apply EditorConfig properties to current buffer. - -This function does nothing when the major mode is listed in -`editorconfig-exclude-modes', or variable `buffer-file-name' matches -any of regexps in `editorconfig-exclude-regexps'." - (interactive) - (when (and major-mode - (not (editorconfig--disabled-for-majormode major-mode)) - buffer-file-name - (not (editorconfig--disabled-for-filename buffer-file-name))) - (editorconfig-apply))) - - -;;;###autoload -(defun editorconfig-find-current-editorconfig () - "Find the closest .editorconfig file for current file." - (interactive) - (eval-and-compile (require 'editorconfig-core)) - (when-let* ((file (editorconfig-core-get-nearest-editorconfig - default-directory))) - (find-file file))) - -;;;###autoload -(defun editorconfig-display-current-properties () - "Display EditorConfig properties extracted for current buffer." - (interactive) - (if editorconfig-properties-hash - (let ((buf (get-buffer-create "*EditorConfig Properties*")) - (file buffer-file-name) - (props editorconfig-properties-hash)) - (with-current-buffer buf - (erase-buffer) - (insert (format "# EditorConfig for %s\n" file)) - (maphash (lambda (k v) - (insert (format "%S = %s\n" k v))) - props)) - (display-buffer buf)) - (message "Properties are not applied to current buffer yet.") - nil)) -;;;###autoload -(defalias 'describe-editorconfig-properties - 'editorconfig-display-current-properties) - -;;;###autoload -(defun editorconfig-format-buffer() - "Format buffer according to .editorconfig indent_style and indent_width." - (interactive) - (when (string= (gethash 'indent_style editorconfig-properties-hash) "tab") - (tabify (point-min) (point-max))) - (when (string= (gethash 'indent_style editorconfig-properties-hash) "space") - (untabify (point-min) (point-max))) - (indent-region (point-min) (point-max))) - - -(provide 'editorconfig-tools) -;;; editorconfig-tools.el ends here blob - 3a12d6ff5157f7ff245dc85e0ed970f9bbbbd9cc (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/editorconfig.el +++ /dev/null @@ -1,887 +0,0 @@ -;;; editorconfig.el --- EditorConfig Emacs Plugin -*- lexical-binding: t -*- - -;; Copyright (C) 2011-2024 EditorConfig Team - -;; Author: EditorConfig Team -;; Version: 0.11.0 -;; URL: https://github.com/editorconfig/editorconfig-emacs#readme -;; Package-Requires: ((emacs "26.1") (nadvice "0.3")) -;; Keywords: convenience editorconfig - -;; See -;; https://github.com/editorconfig/editorconfig-emacs/graphs/contributors -;; or the CONTRIBUTORS file for the list of contributors. - -;; This file is part of EditorConfig Emacs Plugin. - -;; EditorConfig Emacs Plugin 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 of the License, or (at your -;; option) any later version. - -;; EditorConfig Emacs Plugin 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 -;; EditorConfig Emacs Plugin. If not, see . - -;;; Commentary: - -;; EditorConfig helps developers define and maintain consistent -;; coding styles between different editors and IDEs. - -;; The EditorConfig project consists of a file format for defining -;; coding styles and a collection of text editor plugins that enable -;; editors to read the file format and adhere to defined styles. -;; EditorConfig files are easily readable and they work nicely with -;; version control systems. - -;;; Code: - -(require 'cl-lib) -(require 'pcase) - -(require 'nadvice) - -(eval-when-compile - (require 'rx) - (require 'subr-x) - (defvar tex-indent-basic) - (defvar tex-indent-item) - (defvar tex-indent-arg) - (defvar evil-shift-width)) - -(require 'editorconfig-core) - -(defgroup editorconfig nil - "EditorConfig Emacs Plugin. - -EditorConfig helps developers define and maintain consistent -coding styles between different editors and IDEs." - :tag "EditorConfig" - :prefix "editorconfig-" - :group 'tools) - -(define-obsolete-variable-alias - 'edconf-exec-path - 'editorconfig-exec-path - "0.5") -(defcustom editorconfig-exec-path - "editorconfig" - "Path to EditorConfig executable. - -Used by `editorconfig--execute-editorconfig-exec'." - :type 'string - :group 'editorconfig) - -(define-obsolete-variable-alias - 'edconf-get-properties-function - 'editorconfig-get-properties-function - "0.5") -(defcustom editorconfig-get-properties-function - 'editorconfig-core-get-properties-hash - "A function which gets EditorConfig properties for specified file. - -This function will be called with one argument, full path of the target file, -and should return a hash object containing properties, or nil if any core -program is not available. Keys of this hash should be symbols of properties, -and values should be strings of their values. - - -For example, if you always want to use built-in core library instead -of any EditorConfig executable to get properties, add following to -your init.el: - - (set-variable \\='editorconfig-get-properties-function - #\\='editorconfig-core-get-properties-hash) - -Possible known values are: - -* `editorconfig-core-get-properties-hash' (default) - * Always use built-in Emacs-Lisp implementation to get properties -* `editorconfig-get-properties' - * Use `editorconfig-get-properties-from-exec' when - `editorconfig-exec-path' executable is found, otherwise - use `editorconfig-core-get-properties-hash' -* `editorconfig-get-properties-from-exec' - * Get properties by executing EditorConfig executable" - :type 'function - :group 'editorconfig) - -(defcustom editorconfig-mode-lighter " EditorConfig" - "Command `editorconfig-mode' lighter string." - :type 'string - :group 'editorconfig) - -(define-obsolete-variable-alias - 'edconf-custom-hooks - 'editorconfig-after-apply-functions - "0.5") -(define-obsolete-variable-alias - 'editorconfig-custom-hooks - 'editorconfig-after-apply-functions - "0.7.14") -(defcustom editorconfig-after-apply-functions () - "A list of functions after loading common EditorConfig settings. - -Each element in this list is a hook function. This hook function -takes one parameter, which is a property hash table. The value -of properties can be obtained through gethash function. - -The hook does not have to be coding style related; you can add -whatever functionality you want. For example, the following is -an example to add a new property emacs_linum to decide whether to -show line numbers on the left: - - (add-hook \\='editorconfig-after-apply-functions - \\='(lambda (props) - (let ((show-line-num (gethash \\='emacs_linum props))) - (cond ((equal show-line-num \"true\") (linum-mode 1)) - ((equal show-line-num \"false\") (linum-mode 0)))))) - -This hook will be run even when there are no matching sections in -\".editorconfig\", or no \".editorconfig\" file was found at all." - :type 'hook - :group 'editorconfig) - -(defcustom editorconfig-hack-properties-functions () - "A list of function to alter property values before applying them. - -These functions will be run after loading \".editorconfig\" files and before -applying them to current buffer, so that you can alter some properties from -\".editorconfig\" before they take effect. -\(Since 2021/08/30 (v0.9.0): Buffer coding-systems are set before running -this functions, so this variable cannot be used to change coding-systems.) - -For example, Makefiles always use tab characters for indentation: you can -overwrite \"indent_style\" property when current `major-mode' is a -`makefile-mode' with following code: - - (add-hook \\='editorconfig-hack-properties-functions - \\='(lambda (props) - (when (derived-mode-p \\='makefile-mode) - (puthash \\='indent_style \"tab\" props)))) - -This hook will be run even when there are no matching sections in -\".editorconfig\", or no \".editorconfig\" file was found at all." - :type 'hook - :group 'editorconfig) -(make-obsolete-variable 'editorconfig-hack-properties-functions - "Using `editorconfig-after-apply-functions' instead is recommended, - because since 2021/08/30 (v0.9.0) this variable cannot support all properties: - charset values will be referenced before running this hook." - "v0.9.0") - -(define-obsolete-variable-alias - 'edconf-indentation-alist - 'editorconfig-indentation-alist - "0.5") -(defcustom editorconfig-indentation-alist - ;; For contributors: Sort modes in alphabetical order - '((apache-mode apache-indent-level) - (awk-mode c-basic-offset) - (bpftrace-mode c-basic-offset) - (c++-mode c-basic-offset) - (c++-ts-mode c-basic-offset - c-ts-mode-indent-offset) - (c-mode c-basic-offset) - (c-ts-mode c-basic-offset - c-ts-mode-indent-offset) - (cmake-mode cmake-tab-width) - (cmake-ts-mode cmake-tab-width - cmake-ts-mode-indent-offset) - (coffee-mode coffee-tab-width) - (cperl-mode cperl-indent-level) - (crystal-mode crystal-indent-level) - (csharp-mode c-basic-offset) - (csharp-ts-mode c-basic-offset - csharp-ts-mode-indent-offset) - (css-mode css-indent-offset) - (css-ts-mode css-indent-offset) - (d-mode c-basic-offset) - (elixir-ts-mode elixir-ts-indent-offset) - (emacs-lisp-mode lisp-indent-offset) - (enh-ruby-mode enh-ruby-indent-level) - (erlang-mode erlang-indent-level) - (ess-mode ess-indent-offset) - (f90-mode f90-associate-indent - f90-continuation-indent - f90-critical-indent - f90-do-indent - f90-if-indent - f90-program-indent - f90-type-indent) - (feature-mode feature-indent-offset - feature-indent-level) - (fsharp-mode fsharp-continuation-offset - fsharp-indent-level - fsharp-indent-offset) - (gdscript-mode gdscript-indent-offset) - (graphql-mode graphql-indent-level) - (groovy-mode groovy-indent-offset) - (go-ts-mode go-ts-mode-indent-offset) - (haskell-mode haskell-indent-spaces - haskell-indent-offset - haskell-indentation-layout-offset - haskell-indentation-left-offset - haskell-indentation-starter-offset - haskell-indentation-where-post-offset - haskell-indentation-where-pre-offset - shm-indent-spaces) - (haxor-mode haxor-tab-width) - (hcl-mode hcl-indent-level) - (html-ts-mode html-ts-mode-indent-offset) - (idl-mode c-basic-offset) - (jade-mode jade-tab-width) - (java-mode c-basic-offset) - (java-ts-mode c-basic-offset - java-ts-mode-indent-offset) - (js-mode js-indent-level) - (js-ts-mode js-indent-level) - (js-jsx-mode js-indent-level sgml-basic-offset) - (js2-mode js2-basic-offset) - (js2-jsx-mode js2-basic-offset sgml-basic-offset) - (js3-mode js3-indent-level) - (json-mode js-indent-level) - (json-ts-mode json-ts-mode-indent-offset) - (jsonian-mode jsonian-default-indentation) - (julia-mode julia-indent-offset) - (kotlin-mode kotlin-tab-width) - (kotlin-ts-mode kotlin-ts-mode-indent-offset) - (latex-mode . editorconfig-set-indentation-latex-mode) - (lisp-mode lisp-indent-offset) - (livescript-mode livescript-tab-width) - (lua-mode lua-indent-level) - (lua-ts-mode lua-ts-indent-offset) - (matlab-mode matlab-indent-level) - (meson-mode meson-indent-basic) - (mips-mode mips-tab-width) - (mustache-mode mustache-basic-offset) - (nasm-mode nasm-basic-offset) - (nginx-mode nginx-indent-level) - (nxml-mode nxml-child-indent (nxml-attribute-indent . 2)) - (objc-mode c-basic-offset) - (octave-mode octave-block-offset) - (perl-mode perl-indent-level) - ;; No need to change `php-mode-coding-style' value for php-mode - ;; since we run editorconfig later than it resets `c-basic-offset'. - ;; See https://github.com/editorconfig/editorconfig-emacs/issues/116 - ;; for details. - (php-mode c-basic-offset) - (php-ts-mode php-ts-mode-indent-offset) - (pike-mode c-basic-offset) - (protobuf-mode c-basic-offset) - (ps-mode ps-mode-tab) - (pug-mode pug-tab-width) - (puppet-mode puppet-indent-level) - (python-mode . editorconfig-set-indentation-python-mode) - (python-ts-mode . editorconfig-set-indentation-python-mode) - (rjsx-mode js-indent-level sgml-basic-offset) - (ruby-mode ruby-indent-level) - (ruby-ts-mode ruby-indent-level) - (rust-mode rust-indent-offset) - (rust-ts-mode rust-indent-offset - rust-ts-mode-indent-offset) - (rustic-mode rustic-indent-offset) - (scala-mode scala-indent:step) - (scss-mode css-indent-offset) - (sgml-mode sgml-basic-offset) - (sh-mode sh-basic-offset sh-indentation) - (swift-mode swift-mode:basic-offset) - (bash-ts-mode sh-basic-offset sh-indentation) - (slim-mode slim-indent-offset) - (sml-mode sml-indent-level) - (tcl-mode tcl-indent-level - tcl-continued-indent-level) - (terra-mode terra-indent-level) - (toml-ts-mode toml-ts-mode-indent-offset) - (typescript-mode typescript-indent-level) - (typescript-ts-base-mode typescript-ts-mode-indent-offset) - (verilog-mode verilog-indent-level - verilog-indent-level-behavioral - verilog-indent-level-declaration - verilog-indent-level-module - verilog-cexp-indent - verilog-case-indent) - (web-mode (web-mode-indent-style . (lambda (size) 2)) - web-mode-attr-indent-offset - web-mode-attr-value-indent-offset - web-mode-code-indent-offset - web-mode-css-indent-offset - web-mode-markup-indent-offset - web-mode-sql-indent-offset - web-mode-block-padding - web-mode-script-padding - web-mode-style-padding) - (yaml-mode yaml-indent-offset) - (yaml-ts-mode yaml-indent-offset) - (zig-mode zig-indent-offset)) - "Alist of indentation setting methods by modes. - -Each element looks like (MODE . FUNCTION) or (MODE . INDENT-SPEC-LIST). - -If FUNCTION is provided, it will be called when setting the -indentation. The indent size will be passed. - -If INDENT-SPEC-LIST is provided, each element of it must have one of the -following forms: - - 1. VARIABLE - It means (VARIABLE . 1). - - 2. (VARIABLE . SPEC) - Setting VARIABLE according to the type of SPEC: - - - Integer - The value is (* SPEC INDENT-SIZE); - - - Function - The value is (funcall SPEC INDENT-SIZE); - - - Any other type. - The value is SPEC. - -NOTE: Only the **buffer local** value of VARIABLE will be set." - :type '(alist :key-type symbol :value-type sexp) - :risky t - :group 'editorconfig) - -(defcustom editorconfig-exclude-modes () - "Modes in which `editorconfig-mode-apply' will not run." - :type '(repeat (symbol :tag "Major Mode")) - :group 'editorconfig) - -(defcustom editorconfig-exclude-regexps () - "List of regexp for buffer filenames `editorconfig-mode-apply' will not run. - -When variable `buffer-file-name' matches any of the regexps, then -`editorconfig-mode-apply' will not do its work." - :type '(repeat string) - :group 'editorconfig) -(with-eval-after-load 'recentf - (add-to-list 'editorconfig-exclude-regexps - (rx-to-string '(seq string-start - (eval (file-truename (expand-file-name recentf-save-file)))) - t))) - -(defcustom editorconfig-trim-whitespaces-mode nil - "Buffer local minor-mode to use to trim trailing whitespaces. - -If set, enable that mode when `trim_trailing_whitespace` is set to true. -Otherwise, use `delete-trailing-whitespace'." - :type 'symbol - :group 'editorconfig) - -(defvar editorconfig-properties-hash nil - "Hash object of EditorConfig properties that was enabled for current buffer. -Set by `editorconfig-apply' and nil if that is not invoked in -current buffer yet.") -(make-variable-buffer-local 'editorconfig-properties-hash) -(put 'editorconfig-properties-hash - 'permanent-local - t) - -(defvar editorconfig-lisp-use-default-indent nil - "Selectively ignore the value of indent_size for Lisp files. -Prevents `lisp-indent-offset' from being set selectively. - -nil - `lisp-indent-offset' is always set normally. -t - `lisp-indent-offset' is never set normally - (always use default indent for lisps). -number - `lisp-indent-offset' is not set only if indent_size is - equal to this number. For example, if this is set to 2, - `lisp-indent-offset' will not be set only if indent_size is 2.") - -(define-error 'editorconfig-error - "Error thrown from editorconfig lib") - -(defun editorconfig-error (&rest args) - "Signal an `editorconfig-error'. -Make a message by passing ARGS to `format-message'." - (signal 'editorconfig-error (list (apply #'format-message args)))) - -(defun editorconfig--disabled-for-filename (filename) - "Return non-nil when EditorConfig is disabled for FILENAME." - (cl-assert (stringp filename)) - (cl-loop for regexp in editorconfig-exclude-regexps - if (string-match regexp filename) return t - finally return nil)) - -(defun editorconfig--disabled-for-majormode (majormode) - "Return non-nil when Editorconfig is disabled for MAJORMODE." - (cl-assert majormode) - (or (provided-mode-derived-p majormode 'special-mode) - ;; Some special modes (like `archive-mode') are not derived from - ;; `special-mode' - (eq (get majormode 'mode-class) 'special) - (memq majormode - editorconfig-exclude-modes))) - -(defun editorconfig-string-integer-p (string) - "Return non-nil if STRING represents integer." - (and (stringp string) - (string-match-p "\\`[0-9]+\\'" string))) - -(defun editorconfig-set-indentation-python-mode (size) - "Set `python-mode' indent size to SIZE." - (when (boundp 'python-indent-offset) - (setq-local python-indent-offset size)) - ;; For https://gitlab.com/python-mode-devs/python-mode - (when (boundp 'py-indent-offset) - (setq-local py-indent-offset size))) - -(defun editorconfig-set-indentation-latex-mode (size) - "Set `latex-mode' indent size to SIZE." - (setq-local tex-indent-basic size) - (setq-local tex-indent-item size) - (setq-local tex-indent-arg (* 2 size)) - ;; For AUCTeX - (when (boundp 'TeX-brace-indent-level) - (setq-local TeX-brace-indent-level size)) - (when (boundp 'LaTeX-indent-level) - (setq-local LaTeX-indent-level size)) - (when (boundp 'LaTeX-item-indent) - (setq-local LaTeX-item-indent (- size)))) - -(defun editorconfig--should-set (size symbol) - "Determines if editorconfig should set SYMBOL using SIZE." - (if (eq symbol 'lisp-indent-offset) - (cond - ((null editorconfig-lisp-use-default-indent) t) - ((eql t editorconfig-lisp-use-default-indent) nil) - ((numberp editorconfig-lisp-use-default-indent) - (not (eql size editorconfig-lisp-use-default-indent))) - (t t)) - t)) - -(defun editorconfig-set-indentation (style &optional size tab_width) - "Set indentation type from STYLE, SIZE and TAB_WIDTH." - (if (editorconfig-string-integer-p size) - (setq size (string-to-number size)) - (unless (equal size "tab") (setq size nil))) - (cond (tab_width - (setq tab-width (string-to-number tab_width))) - ((numberp size) - (setq tab-width size))) - (when (equal size "tab") - (setq size tab-width)) - (cond ((equal style "space") - (setq indent-tabs-mode nil)) - ((equal style "tab") - (setq indent-tabs-mode t))) - (when size - (when (featurep 'evil) - (setq-local evil-shift-width size)) - (let ((parent major-mode) - entry) - ;; Find the closet parent mode of `major-mode' in - ;; `editorconfig-indentation-alist'. - (while (and (not (setq entry (assoc parent editorconfig-indentation-alist))) - (setq parent (get parent 'derived-mode-parent)))) - (when entry - (let ((fn-or-list (cdr entry))) - (cond ((functionp fn-or-list) (funcall fn-or-list size)) - ((listp fn-or-list) - (dolist (elem fn-or-list) - (cond ((and (symbolp elem) - (editorconfig--should-set size elem)) - (set (make-local-variable elem) size)) - ((and (consp elem) - (editorconfig--should-set size (car elem))) - (let ((spec (cdr elem))) - (set (make-local-variable (car elem)) - (cond ((functionp spec) (funcall spec size)) - ((integerp spec) (* spec size)) - (t spec)))))))))))))) - -(defvar-local editorconfig--apply-coding-system-currently nil - "Used internally.") -(put 'editorconfig--apply-coding-system-currently - 'permanent-local - t) - -(defun editorconfig-merge-coding-systems (end-of-line charset) - "Return merged coding system symbol of END-OF-LINE and CHARSET." - (let ((eol (cond - ((equal end-of-line "lf") 'undecided-unix) - ((equal end-of-line "cr") 'undecided-mac) - ((equal end-of-line "crlf") 'undecided-dos) - (t 'undecided))) - (cs (cond - ((equal charset "latin1") 'iso-latin-1) - ((equal charset "utf-8") 'utf-8) - ((equal charset "utf-8-bom") 'utf-8-with-signature) - ((equal charset "utf-16be") 'utf-16be-with-signature) - ((equal charset "utf-16le") 'utf-16le-with-signature) - (t 'undecided)))) - (merge-coding-systems cs eol))) - -(cl-defun editorconfig-set-coding-system-revert (end-of-line charset) - "Set buffer coding system by END-OF-LINE and CHARSET. - -This function will revert buffer when the coding-system has been changed." - ;; `editorconfig--advice-find-file-noselect' does not use this function - (let ((coding-system (editorconfig-merge-coding-systems end-of-line - charset))) - (display-warning '(editorconfig editorconfig-set-coding-system-revert) - (format "editorconfig-set-coding-system-revert: buffer-file-name: %S | buffer-file-coding-system: %S | coding-system: %S | apply-currently: %S" - buffer-file-name - buffer-file-coding-system - coding-system - editorconfig--apply-coding-system-currently) - :debug) - (when (eq coding-system 'undecided) - (cl-return-from editorconfig-set-coding-system-revert)) - (when (and buffer-file-coding-system - (memq buffer-file-coding-system - (coding-system-aliases (merge-coding-systems coding-system - buffer-file-coding-system)))) - (cl-return-from editorconfig-set-coding-system-revert)) - (unless (file-readable-p buffer-file-name) - (set-buffer-file-coding-system coding-system) - (cl-return-from editorconfig-set-coding-system-revert)) - (unless (memq coding-system - (coding-system-aliases editorconfig--apply-coding-system-currently)) - ;; Revert functions might call editorconfig-apply again - (unwind-protect - (progn - (setq editorconfig--apply-coding-system-currently coding-system) - ;; Revert without query if buffer is not modified - (let ((revert-without-query '("."))) - (revert-buffer-with-coding-system coding-system))) - (setq editorconfig--apply-coding-system-currently nil))))) - -(defun editorconfig-set-trailing-nl (final-newline) - "Set up requiring final newline by FINAL-NEWLINE. - -This function will set `require-final-newline' and `mode-require-final-newline' -to non-nil when FINAL-NEWLINE is true." - (pcase final-newline - ("true" - ;; keep prefs around how/when the nl is added, if set - otherwise add on save - (setq-local require-final-newline (or require-final-newline t)) - (setq-local mode-require-final-newline (or mode-require-final-newline t))) - ("false" - ;; FIXME: Add functionality for actually REMOVING any trailing newlines here! - ;; (rather than just making sure we don't automagically ADD a new one) - (setq-local require-final-newline nil) - (setq-local mode-require-final-newline nil)))) - -(defun editorconfig-set-trailing-ws (trim-trailing-ws) - "Set up trimming of trailing whitespace at end of lines by TRIM-TRAILING-WS." - (make-local-variable 'write-file-functions) ;; just current buffer - (when (and (equal trim-trailing-ws "true") - (not buffer-read-only)) - ;; when true we push delete-trailing-whitespace (emacs > 21) - ;; to write-file-functions - (if editorconfig-trim-whitespaces-mode - (funcall editorconfig-trim-whitespaces-mode 1) - (add-to-list 'write-file-functions 'delete-trailing-whitespace))) - (when (or (equal trim-trailing-ws "false") - buffer-read-only) - ;; when false we remove every delete-trailing-whitespace - ;; from write-file-functions - (when editorconfig-trim-whitespaces-mode - (funcall editorconfig-trim-whitespaces-mode 0)) - (setq write-file-functions - (remove 'delete-trailing-whitespace write-file-functions)))) - -(defun editorconfig-set-line-length (length) - "Set the max line length (`fill-column') to LENGTH." - (when (and (editorconfig-string-integer-p length) - (> (string-to-number length) 0)) - (setq fill-column (string-to-number length)))) - - -(defun editorconfig--execute-editorconfig-exec (filename) - "Execute EditorConfig core with FILENAME and return output." - (if filename - (with-temp-buffer - (let ((remote (file-remote-p filename)) - (remote-localname (file-remote-p filename - 'localname))) - (display-warning '(editorconfig editorconfig--execute-editorconfig-exec) - (format "editorconfig--execute-editorconfig-exec: filename: %S | remote: %S | remote-localname: %S" - filename - remote - remote-localname) - :debug) - (if remote - (progn - (cd (concat remote "/")) - (setq filename remote-localname)) - (cd "/"))) - (display-warning '(editorconfig editorconfig--execute-editorconfig-exec) - (format "editorconfig--execute-editorconfig-exec: default-directory: %S | filename: %S" - default-directory - filename - ) - :debug) - (if (eq 0 - (process-file editorconfig-exec-path nil t nil filename)) - (buffer-string) - (editorconfig-error (buffer-string)))) - "")) - -(defun editorconfig--parse-properties (props-string) - "Create properties hash table from PROPS-STRING." - (let ((props-list (split-string props-string "\n")) - (properties (make-hash-table))) - (dolist (prop props-list properties) - (let ((key-val (split-string prop " *= *"))) - (when (> (length key-val) 1) - (let ((key (intern (car key-val))) - (val (mapconcat 'identity (cdr key-val) ""))) - (puthash key val properties))))))) - -(defun editorconfig-get-properties-from-exec (filename) - "Get EditorConfig properties of file FILENAME. - -This function uses value of `editorconfig-exec-path' to get properties." - (if (executable-find editorconfig-exec-path) - (editorconfig--parse-properties (editorconfig--execute-editorconfig-exec filename)) - (editorconfig-error "Unable to find editorconfig executable"))) - -(defun editorconfig-get-properties (filename) - "Get EditorConfig properties for file FILENAME. - -It calls `editorconfig-get-properties-from-exec' if -`editorconfig-exec-path' is found, otherwise -`editorconfig-core-get-properties-hash'." - (if (and (executable-find editorconfig-exec-path) - (not (file-remote-p filename))) - (editorconfig-get-properties-from-exec filename) - (require 'editorconfig-core) - (editorconfig-core-get-properties-hash filename))) - -(defun editorconfig-call-get-properties-function (filename) - "Call `editorconfig-get-properties-function' with FILENAME and return result. - -This function also removes `unset' properties and calls -`editorconfig-hack-properties-functions'." - (unless (functionp editorconfig-get-properties-function) - (editorconfig-error "Invalid editorconfig-get-properties-function value")) - (if (stringp filename) - (setq filename (expand-file-name filename)) - (editorconfig-error "Invalid argument: %S" filename)) - (let ((props nil)) - (condition-case err - (setq props (funcall editorconfig-get-properties-function - filename)) - (error - (editorconfig-error "Error from editorconfig-get-properties-function: %S" - err))) - (cl-loop for k being the hash-keys of props using (hash-values v) - when (equal v "unset") do (remhash k props)) - props)) - -(defun editorconfig-set-local-variables (props) - "Set buffer variables according to EditorConfig PROPS." - (editorconfig-set-indentation (gethash 'indent_style props) - (gethash 'indent_size props) - (gethash 'tab_width props)) - (editorconfig-set-trailing-nl (gethash 'insert_final_newline props)) - (editorconfig-set-trailing-ws (gethash 'trim_trailing_whitespace props)) - (editorconfig-set-line-length (gethash 'max_line_length props))) - - -(defun editorconfig-major-mode-hook () - "Function to run when `major-mode' has been changed. - -This functions does not reload .editorconfig file, just sets local variables -again. Changing major mode can reset these variables. - -This function also executes `editorconfig-after-apply-functions' functions." - (display-warning '(editorconfig editorconfig-major-mode-hook) - (format "editorconfig-major-mode-hook: editorconfig-mode: %S, major-mode: %S, -properties-hash: %S" - (and (boundp 'editorconfig-mode) - editorconfig-mode) - major-mode - editorconfig-properties-hash) - :debug) - (when (and (bound-and-true-p editorconfig-mode) - editorconfig-properties-hash) - (editorconfig-set-local-variables editorconfig-properties-hash) - (condition-case err - (run-hook-with-args 'editorconfig-after-apply-functions editorconfig-properties-hash) - (error - (display-warning '(editorconfig editorconfig-major-mode-hook) - (format "Error while running `editorconfig-after-apply-functions': %S" - err)))))) - -(defvar editorconfig--cons-filename-codingsystem nil - "Used interally. - -`editorconfig--advice-find-file-noselect' will set this variable, and -`editorconfig--advice-insert-file-contents' will use this variable to set -`coding-system-for-read' value.") - -(defun editorconfig--advice-insert-file-contents (f filename &rest args) - "Set `coding-system-for-read'. - -This function should be added as an advice function to `insert-file-contents'. -F is that function, and FILENAME and ARGS are arguments passed to F." - ;; This function uses `editorconfig--cons-filename-codingsystem' to decide what coding-system - ;; should be used, which will be set by `editorconfig--advice-find-file-noselect'. - (display-warning '(editorconfig editorconfig--advice-insert-file-contents) - (format "editorconfig--advice-insert-file-contents: filename: %S args: %S codingsystem: %S bufferfilename: %S" - filename args - editorconfig--cons-filename-codingsystem - buffer-file-name) - :debug) - (if (and (stringp filename) - (stringp (car editorconfig--cons-filename-codingsystem)) - (string= (expand-file-name filename) - (car editorconfig--cons-filename-codingsystem)) - (cdr editorconfig--cons-filename-codingsystem) - (not (eq (cdr editorconfig--cons-filename-codingsystem) - 'undecided))) - (let ((coding-system-for-read (cdr editorconfig--cons-filename-codingsystem)) - ;; (coding-system-for-read 'undecided) - ) - (apply f filename args)) - (apply f filename args))) - -(defun editorconfig--advice-find-file-noselect (f filename &rest args) - "Get EditorConfig properties and apply them to buffer to be visited. - -This function should be added as an advice function to `find-file-noselect'. -F is that function, and FILENAME and ARGS are arguments passed to F." - (let ((props nil) - (coding-system nil) - (ret nil)) - (condition-case err - (when (and (stringp filename) - (not (editorconfig--disabled-for-filename filename))) - (setq props (editorconfig-call-get-properties-function filename)) - (setq coding-system - (editorconfig-merge-coding-systems (gethash 'end_of_line props) - (gethash 'charset props)))) - (error - (display-warning '(editorconfig editorconfig--advice-find-file-noselect) - (format "Failed to get properties, styles will not be applied: %S" - err) - :warning))) - - (let ((editorconfig--cons-filename-codingsystem (cons (expand-file-name filename) - coding-system))) - (setq ret (apply f filename args))) - - (condition-case err - (with-current-buffer ret - (when (and props - ;; filename has already been checked - (not (editorconfig--disabled-for-majormode major-mode))) - (when (and (file-remote-p filename) - (not (local-variable-p 'buffer-file-coding-system)) - (not (file-exists-p filename)) - coding-system - (not (eq coding-system - 'undecided))) - ;; When file path indicates it is a remote file and it actually - ;; does not exists, `buffer-file-coding-system' will not be set. - ;; (Seems `insert-file-contents' will not be called) - ;; For that case, explicitly set this value so that saving will be done - ;; with expected coding system. - (set-buffer-file-coding-system coding-system)) - - ;; NOTE: When using editorconfig-2-mode, hack-properties-functions cannot affect coding-system value, - ;; because it has to be set before initializing buffers. - (condition-case err - (run-hook-with-args 'editorconfig-hack-properties-functions props) - (error - (display-warning '(editorconfig editorconfig-hack-properties-functions) - (format "Error while running editorconfig-hack-properties-functions, abort running hook: %S" - err) - :warning))) - (setq editorconfig-properties-hash props) - ;; When initializing buffer, `editorconfig-major-mode-hook' - ;; will be called before setting `editorconfig-properties-hash', so - ;; execute this explicitly here. - (editorconfig-set-local-variables props) - - (condition-case err - (run-hook-with-args 'editorconfig-after-apply-functions props) - (error - (display-warning '(editorconfig editorconfig--advice-find-file-noselect) - (format "Error while running `editorconfig-after-apply-functions': %S" - err)))))) - (error - (display-warning '(editorconfig editorconfig--advice-find-file-noselect) - (format "Error while setting variables from EditorConfig: %S" err)))) - ret)) - - -;;;###autoload -(define-minor-mode editorconfig-mode - "Toggle EditorConfig feature. - -To disable EditorConfig in some buffers, modify -`editorconfig-exclude-modes' or `editorconfig-exclude-regexps'." - :global t - :lighter editorconfig-mode-lighter - (let ((modehooks '(prog-mode-hook - text-mode-hook - read-only-mode-hook - ;; Some modes call `kill-all-local-variables' in their init - ;; code, which clears some values set by editorconfig. - ;; For those modes, editorconfig-apply need to be called - ;; explicitly through their hooks. - rpm-spec-mode-hook))) - (if editorconfig-mode - (progn - (advice-add 'find-file-noselect :around 'editorconfig--advice-find-file-noselect) - (advice-add 'insert-file-contents :around 'editorconfig--advice-insert-file-contents) - (dolist (hook modehooks) - (add-hook hook - 'editorconfig-major-mode-hook - t))) - (advice-remove 'find-file-noselect 'editorconfig--advice-find-file-noselect) - (advice-remove 'insert-file-contents 'editorconfig--advice-insert-file-contents) - (dolist (hook modehooks) - (remove-hook hook 'editorconfig-major-mode-hook))))) - - -;; (defconst editorconfig--version -;; (eval-when-compile -;; (require 'lisp-mnt) -;; (declare-function lm-version "lisp-mnt" nil) -;; (lm-version)) -;; "EditorConfig version.") - -(declare-function find-library-name "find-func" (library)) -(declare-function lm-version "lisp-mnt" nil) - -;;;###autoload -(defun editorconfig-version (&optional show-version) - "Get EditorConfig version as string. - -If called interactively or if SHOW-VERSION is non-nil, show the -version in the echo area and the messages buffer." - (interactive (list t)) - (let* ((version (with-temp-buffer - (require 'find-func) - (insert-file-contents (find-library-name "editorconfig")) - (require 'lisp-mnt) - (lm-version))) - (pkg (and (eval-and-compile (require 'package nil t)) - (cadr (assq 'editorconfig - package-alist)))) - (pkg-version (and pkg - (package-version-join (package-desc-version pkg)))) - (version-full (if (and pkg-version - (not (string= version pkg-version))) - (concat version "-" pkg-version) - version))) - (when show-version - (message "EditorConfig Emacs v%s" version-full)) - version-full)) - -(provide 'editorconfig) -;;; editorconfig.el ends here - -;; Local Variables: -;; sentence-end-double-space: t -;; End: blob - 1b4024afc832eecc4997745254bd44559e22ffb2 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0/editorconfig.info +++ /dev/null @@ -1,353 +0,0 @@ -This is doc369236.info, produced by makeinfo version 6.8 from -editorconfig.texi. - -INFO-DIR-SECTION Emacs -START-INFO-DIR-ENTRY -* EditorConfig: (editorconfig). EditorConfig Emacs Plugin. -END-INFO-DIR-ENTRY - - -File: doc369236.info, Node: Top, Next: Getting Started, Up: (dir) - -1 EditorConfig Emacs Plugin -*************************** - -This is an EditorConfig (https://editorconfig.org) plugin for Emacs -(https://www.gnu.org/software/emacs/). - -* Menu: - -* Getting Started:: -* Supported properties:: -* Customize:: -* Troubleshooting:: -* Submitting Bugs and Feature Requests:: -* License:: - - -File: doc369236.info, Node: Getting Started, Next: Supported properties, Prev: Top, Up: Top - -1.1 Getting Started -=================== - -* Menu: - -* packageel:: -* use-package:: -* Manual installation:: - - -File: doc369236.info, Node: packageel, Next: use-package, Up: Getting Started - -1.1.1 package.el ----------------- - -This package is available from MELPA (https://melpa.org/#/editorconfig), -MELPA Stable (https://stable.melpa.org/#/editorconfig) and NonGNU ELPA -(http://elpa.nongnu.org/nongnu/editorconfig.html). Install from these -repositories and enable global minor-mode 'editorconfig-mode': - -(editorconfig-mode 1) - - Normally, enabling 'editorconfig-mode' should be enough for this -plugin to work: all other configurations are optional. This mode sets -up hooks so that EditorConfig properties will be loaded and applied to -the new buffers automatically when visiting files. - - -File: doc369236.info, Node: use-package, Next: Manual installation, Prev: packageel, Up: Getting Started - -1.1.2 use-package ------------------ - -If you use *use-package* (https://www.emacswiki.org/emacs/UsePackage), -add the following to your 'init.el' file: - -(use-package editorconfig - :ensure t - :config - (editorconfig-mode 1)) - - -File: doc369236.info, Node: Manual installation, Prev: use-package, Up: Getting Started - -1.1.3 Manual installation -------------------------- - -Copy all '.el' files in this repository to '~/.emacs.d/lisp' and add the -following: - -(add-to-list 'load-path "~/.emacs.d/lisp") -(require 'editorconfig) -(editorconfig-mode 1) - - -File: doc369236.info, Node: Supported properties, Next: Customize, Prev: Getting Started, Up: Top - -1.2 Supported properties -======================== - -Current Emacs plugin coverage for EditorConfig's properties -(https://editorconfig.org/#supported-properties): - - * 'indent_style' - * 'indent_size' - * 'tab_width' - * 'end_of_line' - * 'charset' - * 'trim_trailing_whitespace' - * 'insert_final_newline = true' is supported - * 'insert_final_newline = false' is not enforced (as in trailing - newlines actually being removed automagically), we just - buffer-locally override any preferences that would auto-add them to - files '.editorconfig' marks as trailing-newline-free - * 'max_line_length' - * 'file_type_ext' (Experimental) (See below) - * 'file_type_emacs' (Experimental) (See below) - * 'root' (only used by EditorConfig core) - - Not yet covered properties marked with over-strike - pull requests -implementing missing features warmly welcomed! Typically, you will want -to tie these to native functionality, or the configuration of existing -packages handling the feature. - - As several packages have their own handling of, say, indentation, we -might not yet cover some mode you use, but we try to add the ones that -show up on our radar. - -* Menu: - -* File Type file_type_ext file_type_emacs:: - - -File: doc369236.info, Node: File Type file_type_ext file_type_emacs, Up: Supported properties - -1.2.1 File Type (file_type_ext, file_type_emacs) ------------------------------------------------- - -File-type feature is currently disabled, because this package is now -undergoing big internal refactoring. For those who want this -functionality, please consider using editorconfig-custom-majormode -(https://github.com/10sr/editorconfig-custom-majormode-el). - - -File: doc369236.info, Node: Customize, Next: Troubleshooting, Prev: Supported properties, Up: Top - -1.3 Customize -============= - -'editorconfig-emacs' provides some customize variables. - - Here are some of these variables: for the full list of available -variables, type M-x customize-group [RET] editorconfig [RET]. - -* Menu: - -* editorconfig-trim-whitespaces-mode:: -* editorconfig-after-apply-functions:: -* editorconfig-hack-properties-functions:: - - -File: doc369236.info, Node: editorconfig-trim-whitespaces-mode, Next: editorconfig-after-apply-functions, Up: Customize - -1.3.1 'editorconfig-trim-whitespaces-mode' ------------------------------------------- - -Buffer local minor-mode to use to trim trailing whitespaces. - - If set, editorconfig will enable/disable this mode in accord with -'trim_trailing_whitespace' property in '.editorconfig'. Otherwise, use -Emacs built-in 'delete-trailing-whitespace' function. - - One possible value is 'ws-butler-mode' -(https://github.com/lewang/ws-butler), with which only lines touched get -trimmed. To use it, add following to your init.el: - -(setq editorconfig-trim-whitespaces-mode - 'ws-butler-mode) - - -File: doc369236.info, Node: editorconfig-after-apply-functions, Next: editorconfig-hack-properties-functions, Prev: editorconfig-trim-whitespaces-mode, Up: Customize - -1.3.2 'editorconfig-after-apply-functions' ------------------------------------------- - -(Formerly 'editorconfig-custom-hooks') - - A list of functions which will be called after loading common -EditorConfig settings, when you can set some custom variables. - - For example, 'web-mode' has several variables for indentation offset -size and EditorConfig sets them at once by 'indent_size'. You can stop -indenting only blocks of 'web-mode' by adding following to your init.el: - -(add-hook 'editorconfig-after-apply-functions - (lambda (props) (setq web-mode-block-padding 0))) - - -File: doc369236.info, Node: editorconfig-hack-properties-functions, Prev: editorconfig-after-apply-functions, Up: Customize - -1.3.3 'editorconfig-hack-properties-functions' ----------------------------------------------- - -A list of functions to alter property values before applying them. - - These functions will be run after loading ".editorconfig" files and -before applying them to current buffer, so that you can alter some -properties from ".editorconfig" before they take effect. - - For example, Makefile files always use tab characters for -indentation: you can overwrite "indent_style" property when current -'major-mode' is 'makefile-mode': - -(add-hook 'editorconfig-hack-properties-functions - '(lambda (props) - (when (derived-mode-p 'makefile-mode) - (puthash 'indent_style "tab" props)))) - - -File: doc369236.info, Node: Troubleshooting, Next: Submitting Bugs and Feature Requests, Prev: Customize, Up: Top - -1.4 Troubleshooting -=================== - -Enabling 'editorconfig-mode' should be enough for normal cases. - - When EditorConfig properties are not effective for unknown reason, we -recommend first trying 'M-x editorconfig-display-current-properties'. - - This command will open a new buffer and display the EditorConfig -properties loaded for current buffer. You can check if EditorConfig -properties were not read for buffers at all, or they were loaded but did -not take effect for some other reasons. - -* Menu: - -* Indentation for new major-modes:: -* Not work at all for FOO-mode!:: - - -File: doc369236.info, Node: Indentation for new major-modes, Next: Not work at all for FOO-mode!, Up: Troubleshooting - -1.4.1 Indentation for new major-modes -------------------------------------- - -Because most Emacs major-modes have their own indentation settings, this -plugin requires explicit support for each major-mode for 'indent_size' -property. - - By default this plugin ships with settings for many major-modes, but, -sorry to say, it cannot be perfect. Especially it is difficult to -support brand-new major-modes. Please feel free to submit issue or -pull-request for such major-mode! - - Supported major-modes and their indentation configs are defined in -the variable 'editorconfig-indentation-alist'. - - -File: doc369236.info, Node: Not work at all for FOO-mode!, Prev: Indentation for new major-modes, Up: Troubleshooting - -1.4.2 Not work at all for FOO-mode! ------------------------------------ - -Most cases properties are loaded just after visiting files when -'editorconfig-mode' is enabled. But it is known that there are -major-modes that this mechanism does not work for and require explicit -call of 'editorconfig-apply'. - - Typically it will occur when the major-mode is not defined using -'define-derived-mode' ('rpm-spec-mode' is an example for this). Please -feel free to submit issues if you find such modes! - - -File: doc369236.info, Node: Submitting Bugs and Feature Requests, Next: License, Prev: Troubleshooting, Up: Top - -1.5 Submitting Bugs and Feature Requests -======================================== - -Bugs, feature requests, and other issues should be submitted to the -issue tracker: https://github.com/editorconfig/editorconfig-emacs/issues - -* Menu: - -* Development:: - - -File: doc369236.info, Node: Development, Up: Submitting Bugs and Feature Requests - -1.5.1 Development ------------------ - -Make and CMake (https://cmake.org) must be installed to run the tests -locally: - -$ make check - - To start a new Emacs process with current '*.el' and without loading -user init file, run: - -$ make sandbox - - -File: doc369236.info, Node: License, Prev: Submitting Bugs and Feature Requests, Up: Top - -1.6 License -=========== - -EditorConfig Emacs Plugin 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 of the -License, or (at your option) any later version. - - This program 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 this program. If not, see . - - - -Tag Table: -Node: Top205 -Ref: #editorconfig-emacs-plugin334 -Node: Getting Started584 -Ref: #getting-started724 -Node: packageel788 -Ref: #package.el907 -Node: use-package1471 -Ref: #use-package1620 -Node: Manual installation1808 -Ref: #manual-installation1955 -Node: Supported properties2130 -Ref: #supported-properties2286 -Node: File Type file_type_ext file_type_emacs3460 -Ref: #file-type-file_type_ext-file_type_emacs3658 -Node: Customize3917 -Ref: #customize4051 -Node: editorconfig-trim-whitespaces-mode4370 -Ref: #editorconfig-trim-whitespaces-mode4583 -Node: editorconfig-after-apply-functions5074 -Ref: #editorconfig-after-apply-functions5334 -Node: editorconfig-hack-properties-functions5820 -Ref: #editorconfig-hack-properties-functions6045 -Node: Troubleshooting6655 -Ref: #troubleshooting6817 -Node: Indentation for new major-modes7357 -Ref: #indentation-for-new-major-modes7558 -Node: Not work at all for FOO-mode!8074 -Ref: #not-work-at-all-for-foo-mode8271 -Node: Submitting Bugs and Feature Requests8693 -Ref: #submitting-bugs-and-feature-requests8895 -Node: Development9063 -Ref: #development9187 -Node: License9391 -Ref: #license9511 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: blob - f72a058811fabe0b74bdd821148a10327528e812 (mode 644) blob + /dev/null --- elpa/editorconfig-0.11.0.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2024-05-11T11:10:04+0200 using EDDSA \ No newline at end of file blob - 12e7d3f6b9b4c981456c0a60920673978313214e (mode 644) blob + /dev/null --- elpa/eglot-1.17/EGLOT-NEWS +++ /dev/null @@ -1,688 +0,0 @@ -Eglot NEWS -*- outline -*- - -Copyright (C) 2018-2024 Free Software Foundation, Inc. -See the end of the file for license conditions. - -Please send Eglot bug reports to 'bug-gnu-emacs@gnu.org', and Cc (or -X-Debbugs-CC) the maintainer 'joaotavora@gmail.com' as well. Please -read the chapter titled "Troubleshooting" in the Eglot manual, -available https://joaotavora.github.io/eglot/#Troubleshooting-Eglot - -This file is about changes in Eglot, the Emacs client for LSP -(Language Server Protocol) distributed with GNU Emacs since Emacs -version 29.1 and with GNU ELPA since 2018. - -Note: references to some Eglot issues are presented as "github#nnnn". -This refers to https://github.com/joaotavora/eglot/issues/. That is, -to look up issue github#1234, go to -https://github.com/joaotavora/eglot/issues/1234. - - -* Changes in upcoming Eglot - - -* Changes in Eglot 1.17 (25/1/2024) - -** Fixes to completion (github#847, github#1349) - -** Fix code-action gathering for some servers (github#847) - -** Experimental support for Eglot-only subprojects - -Until project.el's support for subprojects improves, github#1337 -describes a reasonably sane way to configure nested sub-projects -within a larger one just for Eglot purposes. - - -* Changes in Eglot 1.16 (27/12/2023) - -** Code actions can be previewed in diff format - -The variable 'eglot-confirm-server-edits' replaces the obsolete -'eglot-confirm-server-initiated-edits' and brings about a new -confirmation model for code actions, making it possible to have only -certain commands require user confirmation. It allows a temporary -'diff-mode' buffer to display the proposed changes, so the user can -apply them one by one. See bug#60338. - -** Completion sorting has been fixed - -In some situations, Eglot was not respecting the completion sort order -decided by the language server, falling back on the sort order -determined by the 'flex' completion style instead. See github#1306. - -** Improve mouse invocation of code actions - -When invoking code actions by middle clicking with the mouse on -Flymake diagnostics, it was often the case that Eglot didn't request -code actions correctly and thus no actions were offered to the user. -This has been fixed. See github#1295. - -** Optimized file-watching capability - -Some servers, like the Pyright language server, issue too many file -watching requests. This change slightly reduces the number of file -watcher objects requested from the operating system, which can be a -problem, particularly on Mac OS. See github#1228 and github#1226. - -** Faster, more accurate, event logging - -The Eglot events buffer takes advantage of new functionality in -Jsonrpc 1.23. By default, Lisp-style printing of JSON-RPC message (a -common cause of performance degradation) is disabled. The full -original JSON message is presented instead. See new variable -'eglot-events-buffer-config', which replaces the obsolete -'eglot-events-buffer-size'. - -** 'textdocument/onTypeFormatting' feature has been fixed - -For 'newline' commands, Eglot sometimes sent the wrong character code -to the server. Also this feature is now less chatty in the mode-line -and messages buffer. - -** Partial fix C-M-i "middle-of-symbol" completions (github#1339) - -** Add "Extending Eglot" section to manual - -** Fixed Elisp interface 'eglot-lsp-context' (github#1336, github#1337) - -** Supports LSP's 'window/showRequest' (bug#62116) - -** The self-upgrade command is now called 'eglot-upgrade-eglot' - -** Newly added directories also watched (github#1228) - -** Send correct ':language-id' for JavaScript server (bug#67150) - -** New servers have been added to 'eglot-server-programs'. - -- nls (bug#63603) -- nixd (bug#64214) -- lexical (bug#65359) -- terraform-ls (bug#65671) -- ruff-lsp (bug#67441) -- uiua (bug#67850) - - -* Changes in Eglot 1.15 (29/4/2023) - -** Fix LSP "languageId" detection - -Many servers today support multiple languages, meaning they can handle -more than one file type in the same connection. This relies on the -client supplying a ':languageId' string. Previously, Eglot calculated -this string based on an imperfect heuristic and was often wrong. See -github#1206. - -** Fix problems with missing signature documentation (bug#62687) - -** Reworked 'eglot-imenu' - -Eglot's Imenu backend (used for M-x imenu among other extensions), has -been reworked. Most newer servers respond to -'textDocument/documentSymbol' with a vector of 'DocumentSymbol', not -'SymbolInformation'. It's not worth it trying to make the two formats -resemble each other. This also lays groundwork supporting a -forthcoming "breadcrumb" feature of bug#58431. - -** New command 'eglot-update' - -This allows users to easily update to the latest version of Eglot. - - -* Changes in Eglot 1.14 (3/4/2023) - -** Faster, more responsive completion - -Eglot takes advantage of LSP's "isIncomplete" flag in responses to -completion requests to drive new completion-caching mechanism for the -duration of each completion session. Once a full set of completions -is obtained for a given position, the server needn't be contacted in -many scenarios, resulting in significantly less communication -overhead. This works with the popular Company package and stock -completion-at-point interfaces. - -A variable 'eglot-cache-session-completions', t by default, controls -this. The mechanism was tested with ccls, jdtls, pylsp, golsp and -clangd. Notably, the C/C++ language server Clangd version 15 has a -bug in its "isIcomplete" flag (it is fixed in later versions). If you -run into problems, disable this mechanism like so: - -(add-hook 'c-common-mode-hook - (lambda () (setq-local eglot-cache-session-completions nil))) - -** At-point documentation less obtrusive in echo area - -Eglot takes advantage of new features of ElDoc to separate short -documentation strings from large ones, sending the former to be shown in -the ElDoc's echo area and the latter to be shown in other outlets, -such as the *eldoc* buffer obtainable with 'C-h .'. - -** New variable 'eglot-prefer-plaintext' - -Customize this to t to opt-in to docstrings in plain text instead of -Markdown. - -(bug#61373) - -** Progress indicators inhabit the mode-line by default - -To switch to the echo area, customize 'eglot-report-progress' to -'messages'. To switch off progress reporting completely, set to nil. - -** Snippet support is easier to enable - -The user needn't manually activate 'yas-minor-mode' or -'yas-global-mode'. If YASnippet is installed and the server supports -snippets, it is used automatically, unless the symbol 'yasnippet' has -been added to 'eglot-stay-out-of'. - - -* Changes in Eglot 1.13 (15/03/2023) - -** ELPA installations on Emacs 26.3 are supported again. - - -* Changes in Eglot 1.12.29 (Eglot bundled with Emacs 29.1) - -** Eglot has a new command to upgrade to the latest version. - -The new command 'eglot-upgrade-eglot' allows easily grabbing the -latest version of Eglot from ELPA. This might be more convenient than -using the more general command 'package-install', which by default -will not upgrade "built-in" packages, those that come with Emacs. - - -* Changes in Eglot 1.12 (13/03/2023) - -** LSP inlay hints are now supported. -Inlay hints are small text annotations not unlike diagnostics, but -designed to help readability instead of indicating problems. For -example, a C++ LSP server can serve hints about positional parameter -names in function calls and a variable's automatically deduced type. -Emacs can display these hints using overlays, helping the user -remember those types and parameter names. - -** Longstanding Tramp instability issues solved. -The solution involves a Tramp-specific workaround in Eglot. Future -Tramp versions will have this problem solved at the origin. The -workaround will then be removed. Emacs bug#61350 has more details. - -(github#859, github#1020, github#883) - -** LSP's 'positionEncoding' capability is now supported. -The position-encoding scheme (UTF-8, UTF-16 or UTF-32) can now -be negotiated with the server. - -** More of the user's Eldoc configuration is respected. -This change addresses the problems reported in many Eglot reports -dating back to early 2021 at least. - -(github#646, github#894, github#920, github#1031, github#1171). - -This is unfinished work, as 'eldoc-documentation-strategy' is still -set by Eglot during its tenure over a buffer. The default value for -it cannot work reasonably with Eglot's additions to -'eldoc-documentation-functions'. - -** Completion labels are correctly displayed in servers like clangd. - -(github#1141) - -** Assorted bugfixes. - -(bug#61312, bug#61478, bug#61532, bug#61726, bug#61866, bug#61748) - - -* Changes in Eglot 1.11 (27/1/2023) - -** New server vscode-json-languageserver added to 'eglot-server-programs'. - -(bug#60198) - -** Assorted bugfixes. - -(bug#60379, bug#60557, (bug#61048) - - -* Changes in Eglot 1.10 (16/12/2022) - -** Emacs progress reporters are used for LSP progress notifications. -(bug#59149) - -** LSP reported URIs other than file:// are passed on to Emacs. -This change allows other URI handlers, such as a 'jar:' handling -package, to cooperate with Eglot and find files inside compressed file -systems (bug#58790). - -** Eglot now shows in the menu bar. - -** Tree-sitter modes added to 'eglot-server-programs'. -These modes are usually handled by the same server that handles the -"classical mode". - -** New servers csharp-ls and texlab added to 'eglot-server-programs'. - -** Assorted bugfixes. -(bug#59824, bug#59338) - - -* Changes in Eglot 1.9 (8/10/2022) - -This is the last release before integration into Emacs's core. - -** New 'M-x eglot-list-connections' command. -Probably not very useful for now. More keybindings and clickable -shortcuts to connection-specific commands to be added later. - -** Manual temporarily lives in separate MANUAL.md file. -The manual has been rewritten mostly from scratch. It is structured -hierarchically and is more complete. After the merge into Emacs, the -Eglot Texinfo manual bundled with Emacs used this temporary manual as -a starting point. - -** Support for "single server, multiple modes". -Previously, if an entry such as '((c++-mode c-mode) . ("clangd)")' was -found in 'eglot-server-programs', it meant that '.cpp' files '.c' -files would lead to two 'clangd' instances for managing them, even if -these files were in the same project. Now only one is created. It is -assumed that most, if not all, servers configured in -'eglot-server-programs' handle this correctly. - -(github#681) - -** 'eglot-imenu' no longer uses problematic "special elements". -Though Eglot's 'eglot-imenu' returned a fully compliant 'imenu' -structure, that object was not understood by many other frontends -other than 'M-x imenu' itself. Since the special functionality it -enabled wasn't being used anyway, it was decided to remove it to fix -these longstanding problems. - -(github#758, github#536, github#535) - -** 'eglot-workspace-configuration' has been overhauled. -This variable and its uses are now more thoroughly documented. It can -be set to a function for dynamic calculation of the configuration. -The preferred format is a plist, though the earlier alist format is -still supported. - -(github#967, github#590, github#790) - -** 'C-u M-.' lists and completes arbitrary workspace symbols. -A very old request, now made possible by a relatively recent change to -the 'workspace/symbol' RPC method. - -(github#131) - -** Reworked mode-line menus. -New menus help discover Eglot's features and show which of them are -supported by the current server. Menus can be customized away via -'eglot-menu-string', making space in mode-line. - -(github#792) - -** Easier to use LSP 'initialize.initializationOptions'. -In 'eglot-server-programs' a plist may be appended to the usual list -of strings passed as command line arguments. The value of its -':initializationOptions' key constructs the corresponding LSP JSON -object. This may be easier than creating a 'defclass' for a specific -server and specializing 'eglot-initialization-options' to that class. - -(github#901, github#845, github#940) - -** LSP on-type formatting is now supported. -This is the 'documentOnTypeFormattingProvider' LSP capability, which -may be disabled via 'eglot-ignored-server-capabilities' - -(github#899) - -** Basic LSP "workspace folders" support added. -Eglot now advertises 'project-root' and 'project-external-roots' as -workspace-folders. (Configuring 'project-vc-external-roots-function' -via Elisp or 'tags-table-list' via Customize are two ways to set the -external roots of a simple git project.) - -(github#893) - -** Eglot can now show project wide diagnostics via Flymake. -Some LSP servers report diagnostics for all files in the current -workspace. Flymake has (as of version 1.2.1) the option to show -diagnostics from buffers other than the currently visited one. The -command 'M-x flymake-show-project-diagnostics' will now show all the -diagnostics relevant to a workspace. - -(github#810) - -** Support LSP completion tags. -An LSP completion tag can be used to tell the editor how to render a -completion. Presently, one kind of tag exists, denoting its -corresponding completion as obsolete. - -(github#797) - -** Support LSP optional diagnostic tags. -A diagnostic tag can indicate either "unused or unnecessary code" or -"deprecated or obsolete code". Following the rendering suggestions in -the protocol, we fade out unnecessary code and strike-through -deprecated code. - -(github#794) - -** The Rust language server is now rust-analyzer by default. -Eglot will now prefer starting "rust-analyzer" to "rls" when it is -available. The special support code for RLS has been removed. - -(github#803) - -** New servers have been added to 'eglot-server-programs'. -- clojure-lsp (github#813) -- racket-langserver (github#694) -- futhark lsp (github#922) -- purescript-language-server (github#905) -- Perl::LanguageServer (github#952) -- marksman (github#1013) -- jedi-language-server ([#994](github#994)) - - -* Changes in Eglot 1.8 (12/1/2022) - -** Alternate servers supported out-of-box for the same major mode. -In practice, this removes the need for Eglot to "officially" bless one -server over another. - -Do not confuse this feature with another missing feature which -consists of supporting multiple servers simultaneously managing a -major mode within a project. - -(github#688) - -** TRAMP support added. -There are no variables to customize: visit a remote file, ensure the -server also exists in the remote, and type "M-x eglot". - -(github#637, github#463, github#84) - -** 'eglot-ignored-server-capabilities' is now correctly spelled. -This user-visible variable used to be spelled -'eglot-ignored-server-capabilites', which is still a valid but -obsolete name. - -(github#724) - -** Eglot can manage cross-referenced files outside project. -This is activated by a new customization option -'eglot-extend-to-xref', which defaults to nil. - -(github#76, github#686, github#695) - -** Code action shortcuts can be added by the user. -'M-x eglot-code-actions' accepts an optional 'action-kind' argument, -specified interactively with 'C-u'. Other shortcuts call specific -actions directly ('eglot-code-action-inline', -'eglot-code-action-extract', 'eglot-code-action-rewrite', -'eglot-code-action-organize-imports' and -'eglot-code-action-quickfix'). One can create one's own shortcuts for -code actions with specific a kind by calling 'eglot-code-actions' from -Lisp. - -(github#411) - -** New command 'eglot-shutdown-all added. -This disconnects all the Eglot connections in the user's session. - -(github#643) - -** New variable 'eglot-withhold-process-id' added. -If non-nil, Eglot will not send the Emacs process ID to the language -server. This can be useful when using docker to run a language -server. - -(github#722) - -** Several new servers have been added to 'eglot-server-programs'. -- cmake-language-server (github#787) -- css-languageserver (github#204, github#769) -- fortls (github#603) -- html-languageserver (github#204, github#769) -- json-languageserver (github#204, github#769) -- lua-lsp (github#721) -- mint ls (github#750) -- pyright (github#742) -- vim-language-server (github#787) -- yaml-language-server (github#751) -- zls (github#646) - - -* Changes in Eglot 1.7 (16/12/2020) - -** Hierarchical symbols are supported in Imenu. -(github#303). - -** Multiple "documentation at point" sources are supported. -Such sources include as LSP's signature, hover and also the Flymake -diagnostic messages. They can all be presented in the echo area -(space permitting), or via 'C-h .'. For now, composition of different -sources can be customized using 'eldoc-documentation-strategy', -'eldoc-echo-area-use-multiline-p' and 'eldoc-prefer-doc-buffer'. - -The variables 'eglot-put-doc-in-help-buffer' and -'eglot-auto-display-help-buffer' have been removed. - -(github#439, github#494, github#481, github#454) - - -* Changes in Eglot 1.6 (16/04/2020) - -** Column offset calculation is now LSP-conformant. -It seems the majority of servers now comply with the language server -specification when it comes to handling non-ASCII texts. Therefore -the default values of 'eglot-move-to-column-function' and -'eglot-current-column-function' have been changed. Consult the -documentation of these variables for how to restore the old behavior. - -(github#361) - -** LSP workspace/configuration requests are supported. -Also a new section "Per-project server configuration" in the README.md -should answer some FAQ's in this regard. - -(github#326) - - -* Changes in Eglot 1.5 (20/10/2019) - -** Eglot takes over Company configuration. -Similar to what was already the case with Flymake, Eldoc and Xref, use -just the backend that can do something useful in Eglot, -'company-capf'. See 'eglot-stay-out-of' to opt out of this. - -(github#324) - -** New option 'eglot-autoshutdown' added. -This disconnects the server after last managed buffer is killed. - -(github#217, github#270) - -** Completion support has been fixed. -Among other things, consider LSP's "filterText" cookies, which enable -a kind of poor-man's flex-matching for some backends. - -(github#235, github#313, github#311, github#279) - -** Supports LSP's "goto declaration/implementation/typeDefinition". - -(github#302) - -** New option 'eglot-send-changes-idle-time' added. - -(github#258) - -** Eglot's Eldoc no longer flickers when moving around. - -(github#198) - -** Large docs shown in help buffer instead of echo area by default. -Also add two new customization variables -'eglot-put-doc-in-help-buffer' and 'eglot-auto-display-help-buffer'. - -(github#198) - -** Built-in support for Go, Elixir and Ada added. - -(github#304, github#264, github#316) - - -* Changes in Eglot 1.4 (5/1/2019) - -** Parameter highlighting in the first line of signature corrected. - -** Markdown documentation strings are rendered with faces. -Eglot uses 'gfm-view-mode' for this. - -** Hard dependencies on Flymake have been removed. -The user can turn Flymake off now in buffers managed by Eglot. - -** Connection hooks are run with proper directory local variables. -This fixes issues with suspiciously empty 'didChangeConfiguration' -messages that are supposed to communicate parameters from a -directory-set 'eglot-workspace-configuration'. - -(github#196) - -** Completion sorting has been fixed. -If the server returns completions in some sensible order, Eglot will -keep it. - -(github#190) - -** Flymake and Eldoc taken over completely while managing buffers. -No longer try to add Eglot's facilities to existing facilities in -these two domains. - - -* Changes in Eglot 1.3 (10/12/2018) - -** Provide strict checking of incoming LSP messages. - -(github#144, github#156) - -** Add brief context after 'xref-find-references' when available. - -(github#52) - -** Support 'completionContext' to help servers like 'ccls'. - -** Use Flymake from GNU ELPA. - -(github#178) - - -* Changes in Eglot 1.2 (23/11/2018) - -** Support snippet completions. -Eglot uses 'yasnippet.el' for this, if it is installed. - -(github#50) - -** 'workspace/didChangeConfiguration' implemented. - -(github#29) - -** Handle experimental/unknown server methods gracefully. - -(github#39) - -** Accept functions as entries in 'eglot-server-programs'. -'CONTACT' in the '(MAJOR-MODE . CONTACT)' association in -'eglot-server-programs' can now be a function of no arguments -producing any value previously valid for a contact. Functions can be -interactive or non-interactive. - -(github#63) - -** Improve completion to be snappier and don't hinder typing. - -(github#61) - -** Consider ':triggerCharacters' in company completion. - -(github#80) - -** Add support for LSP 'TextEdit' objects in completion. - -** Prefer ccls over cquery for C/C++ - -(github#94) - -** 'eglot-ignored-server-capabilites' is more user-friendly. - -(github#126) - -** Supports asynchronous connections. -If a connection to the server is taking too long, is will continue in -the background. A new defcustom 'eglot-sync-connect' controls this -feature. - -(github#68) - -** The 'eglot-shutdown' command prompts for the server to shutdown. - -(github#73) - -** Add support for the Eclipse JDT language server. - -(github#63) - -** Add out-of-the-box support for Haskell, Kotlin, Go, Ocaml, R. - -** Add the ability to move to LSP-precise columns. -Some servers like 'clangd' follow the UTF-16-based spec very closely -here. - -(github#124) - -** Fix a potential security issue fontifying LSP doc. - -(github#154) - -** Fix many, many bugs - -(github#44, github#48, github#54, github#58, github#64, github#74, - github#81, github#82, github#86, github#87, github#83, github#93, - github#100, github#115, github#120, github#121, github#126, - github#138, github#144, github#158, github#160, github#167) - - -* Changes in Eglot 1.1 (9/7/2018) - -** Implement TCP autostart/autoconnect (and support Ruby's Solargraph). -The ':autoport' symbol in the server invocation is replaced -dynamically by a local port believed to be vacant, so that the ensuing -TCP connection finds a listening server. - -** Eglot now depends on Emacs library 'jsonrpc.el'. - - ----------------------------------------------------------------------- -This file is part of GNU Emacs. - -GNU Emacs 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 of the License, or -(at your option) any later version. - -GNU Emacs 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. If not, see . - - -Local Variables: -bug-reference-bug-regexp: "\\(\\(github\\|bug\\)#\\([0-9]+\\)\\)" -bug-reference-url-format: eglot--debbugs-or-github-bug-uri -paragraph-separate: "[ ]" -End: - blob - eaf3da0e92dcb5f82d495be23dec933dfa86fb84 (mode 644) blob + /dev/null --- elpa/eglot-1.17/doclicense.texi +++ /dev/null @@ -1,505 +0,0 @@ -@c The GNU Free Documentation License. -@center Version 1.3, 3 November 2008 - -@c This file is intended to be included within another document, -@c hence no sectioning command or @node. - -@display -Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. -@uref{https://fsf.org/} - -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. -@end display - -@enumerate 0 -@item -PREAMBLE - -The purpose of this License is to make a manual, textbook, or other -functional and useful document @dfn{free} in the sense of freedom: to -assure everyone the effective freedom to copy and redistribute it, -with or without modifying it, either commercially or noncommercially. -Secondarily, this License preserves for the author and publisher a way -to get credit for their work, while not being considered responsible -for modifications made by others. - -This License is a kind of ``copyleft'', which means that derivative -works of the document must themselves be free in the same sense. It -complements the GNU General Public License, which is a copyleft -license designed for free software. - -We have designed this License in order to use it for manuals for free -software, because free software needs free documentation: a free -program should come with manuals providing the same freedoms that the -software does. But this License is not limited to software manuals; -it can be used for any textual work, regardless of subject matter or -whether it is published as a printed book. We recommend this License -principally for works whose purpose is instruction or reference. - -@item -APPLICABILITY AND DEFINITIONS - -This License applies to any manual or other work, in any medium, that -contains a notice placed by the copyright holder saying it can be -distributed under the terms of this License. Such a notice grants a -world-wide, royalty-free license, unlimited in duration, to use that -work under the conditions stated herein. The ``Document'', below, -refers to any such manual or work. Any member of the public is a -licensee, and is addressed as ``you''. You accept the license if you -copy, modify or distribute the work in a way requiring permission -under copyright law. - -A ``Modified Version'' of the Document means any work containing the -Document or a portion of it, either copied verbatim, or with -modifications and/or translated into another language. - -A ``Secondary Section'' is a named appendix or a front-matter section -of the Document that deals exclusively with the relationship of the -publishers or authors of the Document to the Document's overall -subject (or to related matters) and contains nothing that could fall -directly within that overall subject. (Thus, if the Document is in -part a textbook of mathematics, a Secondary Section may not explain -any mathematics.) The relationship could be a matter of historical -connection with the subject or with related matters, or of legal, -commercial, philosophical, ethical or political position regarding -them. - -The ``Invariant Sections'' are certain Secondary Sections whose titles -are designated, as being those of Invariant Sections, in the notice -that says that the Document is released under this License. If a -section does not fit the above definition of Secondary then it is not -allowed to be designated as Invariant. The Document may contain zero -Invariant Sections. If the Document does not identify any Invariant -Sections then there are none. - -The ``Cover Texts'' are certain short passages of text that are listed, -as Front-Cover Texts or Back-Cover Texts, in the notice that says that -the Document is released under this License. A Front-Cover Text may -be at most 5 words, and a Back-Cover Text may be at most 25 words. - -A ``Transparent'' copy of the Document means a machine-readable copy, -represented in a format whose specification is available to the -general public, that is suitable for revising the document -straightforwardly with generic text editors or (for images composed of -pixels) generic paint programs or (for drawings) some widely available -drawing editor, and that is suitable for input to text formatters or -for automatic translation to a variety of formats suitable for input -to text formatters. A copy made in an otherwise Transparent file -format whose markup, or absence of markup, has been arranged to thwart -or discourage subsequent modification by readers is not Transparent. -An image format is not Transparent if used for any substantial amount -of text. A copy that is not ``Transparent'' is called ``Opaque''. - -Examples of suitable formats for Transparent copies include plain -ASCII without markup, Texinfo input format, La@TeX{} input -format, SGML or XML using a publicly available -DTD, and standard-conforming simple HTML, -PostScript or PDF designed for human modification. Examples -of transparent image formats include PNG, XCF and -JPG@. Opaque formats include proprietary formats that can be -read and edited only by proprietary word processors, SGML or -XML for which the DTD and/or processing tools are -not generally available, and the machine-generated HTML, -PostScript or PDF produced by some word processors for -output purposes only. - -The ``Title Page'' means, for a printed book, the title page itself, -plus such following pages as are needed to hold, legibly, the material -this License requires to appear in the title page. For works in -formats which do not have any title page as such, ``Title Page'' means -the text near the most prominent appearance of the work's title, -preceding the beginning of the body of the text. - -The ``publisher'' means any person or entity that distributes copies -of the Document to the public. - -A section ``Entitled XYZ'' means a named subunit of the Document whose -title either is precisely XYZ or contains XYZ in parentheses following -text that translates XYZ in another language. (Here XYZ stands for a -specific section name mentioned below, such as ``Acknowledgements'', -``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' -of such a section when you modify the Document means that it remains a -section ``Entitled XYZ'' according to this definition. - -The Document may include Warranty Disclaimers next to the notice which -states that this License applies to the Document. These Warranty -Disclaimers are considered to be included by reference in this -License, but only as regards disclaiming warranties: any other -implication that these Warranty Disclaimers may have is void and has -no effect on the meaning of this License. - -@item -VERBATIM COPYING - -You may copy and distribute the Document in any medium, either -commercially or noncommercially, provided that this License, the -copyright notices, and the license notice saying this License applies -to the Document are reproduced in all copies, and that you add no other -conditions whatsoever to those of this License. You may not use -technical measures to obstruct or control the reading or further -copying of the copies you make or distribute. However, you may accept -compensation in exchange for copies. If you distribute a large enough -number of copies you must also follow the conditions in section 3. - -You may also lend copies, under the same conditions stated above, and -you may publicly display copies. - -@item -COPYING IN QUANTITY - -If you publish printed copies (or copies in media that commonly have -printed covers) of the Document, numbering more than 100, and the -Document's license notice requires Cover Texts, you must enclose the -copies in covers that carry, clearly and legibly, all these Cover -Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on -the back cover. Both covers must also clearly and legibly identify -you as the publisher of these copies. The front cover must present -the full title with all words of the title equally prominent and -visible. You may add other material on the covers in addition. -Copying with changes limited to the covers, as long as they preserve -the title of the Document and satisfy these conditions, can be treated -as verbatim copying in other respects. - -If the required texts for either cover are too voluminous to fit -legibly, you should put the first ones listed (as many as fit -reasonably) on the actual cover, and continue the rest onto adjacent -pages. - -If you publish or distribute Opaque copies of the Document numbering -more than 100, you must either include a machine-readable Transparent -copy along with each Opaque copy, or state in or with each Opaque copy -a computer-network location from which the general network-using -public has access to download using public-standard network protocols -a complete Transparent copy of the Document, free of added material. -If you use the latter option, you must take reasonably prudent steps, -when you begin distribution of Opaque copies in quantity, to ensure -that this Transparent copy will remain thus accessible at the stated -location until at least one year after the last time you distribute an -Opaque copy (directly or through your agents or retailers) of that -edition to the public. - -It is requested, but not required, that you contact the authors of the -Document well before redistributing any large number of copies, to give -them a chance to provide you with an updated version of the Document. - -@item -MODIFICATIONS - -You may copy and distribute a Modified Version of the Document under -the conditions of sections 2 and 3 above, provided that you release -the Modified Version under precisely this License, with the Modified -Version filling the role of the Document, thus licensing distribution -and modification of the Modified Version to whoever possesses a copy -of it. In addition, you must do these things in the Modified Version: - -@enumerate A -@item -Use in the Title Page (and on the covers, if any) a title distinct -from that of the Document, and from those of previous versions -(which should, if there were any, be listed in the History section -of the Document). You may use the same title as a previous version -if the original publisher of that version gives permission. - -@item -List on the Title Page, as authors, one or more persons or entities -responsible for authorship of the modifications in the Modified -Version, together with at least five of the principal authors of the -Document (all of its principal authors, if it has fewer than five), -unless they release you from this requirement. - -@item -State on the Title page the name of the publisher of the -Modified Version, as the publisher. - -@item -Preserve all the copyright notices of the Document. - -@item -Add an appropriate copyright notice for your modifications -adjacent to the other copyright notices. - -@item -Include, immediately after the copyright notices, a license notice -giving the public permission to use the Modified Version under the -terms of this License, in the form shown in the Addendum below. - -@item -Preserve in that license notice the full lists of Invariant Sections -and required Cover Texts given in the Document's license notice. - -@item -Include an unaltered copy of this License. - -@item -Preserve the section Entitled ``History'', Preserve its Title, and add -to it an item stating at least the title, year, new authors, and -publisher of the Modified Version as given on the Title Page. If -there is no section Entitled ``History'' in the Document, create one -stating the title, year, authors, and publisher of the Document as -given on its Title Page, then add an item describing the Modified -Version as stated in the previous sentence. - -@item -Preserve the network location, if any, given in the Document for -public access to a Transparent copy of the Document, and likewise -the network locations given in the Document for previous versions -it was based on. These may be placed in the ``History'' section. -You may omit a network location for a work that was published at -least four years before the Document itself, or if the original -publisher of the version it refers to gives permission. - -@item -For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve -the Title of the section, and preserve in the section all the -substance and tone of each of the contributor acknowledgements and/or -dedications given therein. - -@item -Preserve all the Invariant Sections of the Document, -unaltered in their text and in their titles. Section numbers -or the equivalent are not considered part of the section titles. - -@item -Delete any section Entitled ``Endorsements''. Such a section -may not be included in the Modified Version. - -@item -Do not retitle any existing section to be Entitled ``Endorsements'' or -to conflict in title with any Invariant Section. - -@item -Preserve any Warranty Disclaimers. -@end enumerate - -If the Modified Version includes new front-matter sections or -appendices that qualify as Secondary Sections and contain no material -copied from the Document, you may at your option designate some or all -of these sections as invariant. To do this, add their titles to the -list of Invariant Sections in the Modified Version's license notice. -These titles must be distinct from any other section titles. - -You may add a section Entitled ``Endorsements'', provided it contains -nothing but endorsements of your Modified Version by various -parties---for example, statements of peer review or that the text has -been approved by an organization as the authoritative definition of a -standard. - -You may add a passage of up to five words as a Front-Cover Text, and a -passage of up to 25 words as a Back-Cover Text, to the end of the list -of Cover Texts in the Modified Version. Only one passage of -Front-Cover Text and one of Back-Cover Text may be added by (or -through arrangements made by) any one entity. If the Document already -includes a cover text for the same cover, previously added by you or -by arrangement made by the same entity you are acting on behalf of, -you may not add another; but you may replace the old one, on explicit -permission from the previous publisher that added the old one. - -The author(s) and publisher(s) of the Document do not by this License -give permission to use their names for publicity for or to assert or -imply endorsement of any Modified Version. - -@item -COMBINING DOCUMENTS - -You may combine the Document with other documents released under this -License, under the terms defined in section 4 above for modified -versions, provided that you include in the combination all of the -Invariant Sections of all of the original documents, unmodified, and -list them all as Invariant Sections of your combined work in its -license notice, and that you preserve all their Warranty Disclaimers. - -The combined work need only contain one copy of this License, and -multiple identical Invariant Sections may be replaced with a single -copy. If there are multiple Invariant Sections with the same name but -different contents, make the title of each such section unique by -adding at the end of it, in parentheses, the name of the original -author or publisher of that section if known, or else a unique number. -Make the same adjustment to the section titles in the list of -Invariant Sections in the license notice of the combined work. - -In the combination, you must combine any sections Entitled ``History'' -in the various original documents, forming one section Entitled -``History''; likewise combine any sections Entitled ``Acknowledgements'', -and any sections Entitled ``Dedications''. You must delete all -sections Entitled ``Endorsements.'' - -@item -COLLECTIONS OF DOCUMENTS - -You may make a collection consisting of the Document and other documents -released under this License, and replace the individual copies of this -License in the various documents with a single copy that is included in -the collection, provided that you follow the rules of this License for -verbatim copying of each of the documents in all other respects. - -You may extract a single document from such a collection, and distribute -it individually under this License, provided you insert a copy of this -License into the extracted document, and follow this License in all -other respects regarding verbatim copying of that document. - -@item -AGGREGATION WITH INDEPENDENT WORKS - -A compilation of the Document or its derivatives with other separate -and independent documents or works, in or on a volume of a storage or -distribution medium, is called an ``aggregate'' if the copyright -resulting from the compilation is not used to limit the legal rights -of the compilation's users beyond what the individual works permit. -When the Document is included in an aggregate, this License does not -apply to the other works in the aggregate which are not themselves -derivative works of the Document. - -If the Cover Text requirement of section 3 is applicable to these -copies of the Document, then if the Document is less than one half of -the entire aggregate, the Document's Cover Texts may be placed on -covers that bracket the Document within the aggregate, or the -electronic equivalent of covers if the Document is in electronic form. -Otherwise they must appear on printed covers that bracket the whole -aggregate. - -@item -TRANSLATION - -Translation is considered a kind of modification, so you may -distribute translations of the Document under the terms of section 4. -Replacing Invariant Sections with translations requires special -permission from their copyright holders, but you may include -translations of some or all Invariant Sections in addition to the -original versions of these Invariant Sections. You may include a -translation of this License, and all the license notices in the -Document, and any Warranty Disclaimers, provided that you also include -the original English version of this License and the original versions -of those notices and disclaimers. In case of a disagreement between -the translation and the original version of this License or a notice -or disclaimer, the original version will prevail. - -If a section in the Document is Entitled ``Acknowledgements'', -``Dedications'', or ``History'', the requirement (section 4) to Preserve -its Title (section 1) will typically require changing the actual -title. - -@item -TERMINATION - -You may not copy, modify, sublicense, or distribute the Document -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense, or distribute it is void, and -will automatically terminate your rights under this License. - -However, if you cease all violation of this License, then your license -from a particular copyright holder is reinstated (a) provisionally, -unless and until the copyright holder explicitly and finally -terminates your license, and (b) permanently, if the copyright holder -fails to notify you of the violation by some reasonable means prior to -60 days after the cessation. - -Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, receipt of a copy of some or all of the same material does -not give you any rights to use it. - -@item -FUTURE REVISIONS OF THIS LICENSE - -The Free Software Foundation may publish new, revised versions -of the GNU Free Documentation License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. See -@uref{https://www.gnu.org/licenses/}. - -Each version of the License is given a distinguishing version number. -If the Document specifies that a particular numbered version of this -License ``or any later version'' applies to it, you have the option of -following the terms and conditions either of that specified version or -of any later version that has been published (not as a draft) by the -Free Software Foundation. If the Document does not specify a version -number of this License, you may choose any version ever published (not -as a draft) by the Free Software Foundation. If the Document -specifies that a proxy can decide which future versions of this -License can be used, that proxy's public statement of acceptance of a -version permanently authorizes you to choose that version for the -Document. - -@item -RELICENSING - -``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any -World Wide Web server that publishes copyrightable works and also -provides prominent facilities for anybody to edit those works. A -public wiki that anybody can edit is an example of such a server. A -``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the -site means any set of copyrightable works thus published on the MMC -site. - -``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 -license published by Creative Commons Corporation, a not-for-profit -corporation with a principal place of business in San Francisco, -California, as well as future copyleft versions of that license -published by that same organization. - -``Incorporate'' means to publish or republish a Document, in whole or -in part, as part of another Document. - -An MMC is ``eligible for relicensing'' if it is licensed under this -License, and if all works that were first published under this License -somewhere other than this MMC, and subsequently incorporated in whole -or in part into the MMC, (1) had no cover texts or invariant sections, -and (2) were thus incorporated prior to November 1, 2008. - -The operator of an MMC Site may republish an MMC contained in the site -under CC-BY-SA on the same site at any time before August 1, 2009, -provided the MMC is eligible for relicensing. - -@end enumerate - -@page -@heading ADDENDUM: How to use this License for your documents - -To use this License in a document you have written, include a copy of -the License in the document and put the following copyright and -license notices just after the title page: - -@smallexample -@group - Copyright (C) @var{year} @var{your name}. - Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.3 - or any later version published by the Free Software Foundation; - with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - Texts. A copy of the license is included in the section entitled ``GNU - Free Documentation License''. -@end group -@end smallexample - -If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, -replace the ``with@dots{}Texts.''@: line with this: - -@smallexample -@group - with the Invariant Sections being @var{list their titles}, with - the Front-Cover Texts being @var{list}, and with the Back-Cover Texts - being @var{list}. -@end group -@end smallexample - -If you have Invariant Sections without Cover Texts, or some other -combination of the three, merge those two alternatives to suit the -situation. - -If your document contains nontrivial examples of program code, we -recommend releasing these examples in parallel under your choice of -free software license, such as the GNU General Public License, -to permit their use in free software. - -@c Local Variables: -@c ispell-local-pdict: "ispell-dict" -@c End: blob - e7404398d2490c4107fd5efed128b4ad3bfbfe1f (mode 644) blob + /dev/null --- elpa/eglot-1.17/docstyle.texi +++ /dev/null @@ -1,19 +0,0 @@ -@c Emacs documentation style settings -@documentencoding UTF-8 -@c These two require Texinfo 5.0 or later, so we use the older -@c equivalent @set variables supported in 4.11 and hence -@ignore -@codequotebacktick on -@codequoteundirected on -@end ignore -@set txicodequoteundirected -@set txicodequotebacktick -@iftex -@c It turns out TeX sometimes fails to hyphenate, so we help it here -@hyphenation{au-to-mat-i-cal-ly} -@hyphenation{spec-i-fied} -@hyphenation{work-a-round} -@hyphenation{work-a-rounds} -@hyphenation{un-marked} -@hyphenation{dic-tion-ary} -@end iftex blob - 82a10315825ad182da45899fee2672f2a9c38642 (mode 644) blob + /dev/null --- elpa/eglot-1.17/eglot-autoloads.el +++ /dev/null @@ -1,90 +0,0 @@ -;;; eglot-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from eglot.el - -(define-obsolete-function-alias 'eglot-update #'eglot-upgrade-eglot "29.1") -(autoload 'eglot "eglot" "\ -Start LSP server for PROJECT's buffers under MANAGED-MAJOR-MODES. - -This starts a Language Server Protocol (LSP) server suitable for -the buffers of PROJECT whose `major-mode' is among -MANAGED-MAJOR-MODES. CLASS is the class of the LSP server to -start and CONTACT specifies how to connect to the server. - -Interactively, the command attempts to guess MANAGED-MAJOR-MODES, -CLASS, CONTACT, and LANGUAGE-IDS from `eglot-server-programs', -according to the current buffer's `major-mode'. PROJECT is -guessed from `project-find-functions'. The search for active -projects in this context binds `eglot-lsp-context' (which see). - -If it can't guess, it prompts the user for the mode and the -server. With a single \\[universal-argument] prefix arg, it -always prompts for COMMAND. With two \\[universal-argument], it -also always prompts for MANAGED-MAJOR-MODE. - -The LSP server of CLASS is started (or contacted) via CONTACT. -If this operation is successful, current *and future* file -buffers of MANAGED-MAJOR-MODE inside PROJECT become \"managed\" -by the LSP server, meaning the information about their contents is -exchanged periodically with the server to provide enhanced -code-analysis via `xref-find-definitions', `flymake-mode', -`eldoc-mode', and `completion-at-point', among others. - -PROJECT is a project object as returned by `project-current'. - -CLASS is a subclass of `eglot-lsp-server'. - -CONTACT specifies how to contact the server. It is a -keyword-value plist used to initialize CLASS or a plain list as -described in `eglot-server-programs', which see. - -LANGUAGE-IDS is a list of language ID string to send to the -server for each element in MANAGED-MAJOR-MODES. - -INTERACTIVE is ignored and provided for backward compatibility. - -(fn MANAGED-MAJOR-MODES PROJECT CLASS CONTACT LANGUAGE-IDS &optional INTERACTIVE)" t) -(autoload 'eglot-ensure "eglot" "\ -Start Eglot session for current buffer if there isn't one. - -Only use this function (in major mode hooks, etc) if you are -confident that Eglot can be started safely and efficiently for -*every* buffer visited where these hooks may execute. - -Since it is difficult to establish this confidence fully, it's -often wise to use the interactive command `eglot' instead. This -command only needs to be invoked once per project, as all other -files of a given major mode visited within the same project will -automatically become managed with no further user intervention -needed.") -(autoload 'eglot-upgrade-eglot "eglot" "\ -Update Eglot to latest version. - -(fn &rest _)" t) -(put 'eglot-workspace-configuration 'safe-local-variable #'listp) -(put 'eglot--debbugs-or-github-bug-uri 'bug-reference-url-format t) -(defun eglot--debbugs-or-github-bug-uri nil (format (if (string= (match-string 2) "github") "https://github.com/joaotavora/eglot/issues/%s" "https://debbugs.gnu.org/%s") (match-string 3))) -(register-definition-prefixes "eglot" '("eglot-")) - -;;; End of scraped data - -(provide 'eglot-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; eglot-autoloads.el ends here blob - 96f9c055bb8e6d930b99d126d2320d35dfac7770 (mode 644) blob + /dev/null --- elpa/eglot-1.17/eglot-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from eglot.el -*- no-byte-compile: t -*- -(define-package "eglot" "1.17" "The Emacs Client for LSP servers" '((emacs "26.3") (jsonrpc "1.0.24") (flymake "1.2.1") (project "0.9.8") (xref "1.6.2") (eldoc "1.14.0") (seq "2.23") (external-completion "0.1")) :commit "b014bca833a17f5b2258e88115f03cffa983d0bd" :authors '(("João Távora" . "joaotavora@gmail.com")) :maintainer '("João Távora" . "joaotavora@gmail.com") :keywords '("convenience" "languages") :url "https://github.com/joaotavora/eglot") blob - 408370745737e374bdb0b0724873665bda3236d6 (mode 644) blob + /dev/null --- elpa/eglot-1.17/eglot.el +++ /dev/null @@ -1,4051 +0,0 @@ -;;; eglot.el --- The Emacs Client for LSP servers -*- lexical-binding: t; -*- - -;; Copyright (C) 2018-2024 Free Software Foundation, Inc. - -;; Version: 1.17 -;; Author: João Távora -;; Maintainer: João Távora -;; URL: https://github.com/joaotavora/eglot -;; Keywords: convenience, languages -;; Package-Requires: ((emacs "26.3") (jsonrpc "1.0.24") (flymake "1.2.1") (project "0.9.8") (xref "1.6.2") (eldoc "1.14.0") (seq "2.23") (external-completion "0.1")) - -;; This is a GNU ELPA :core package. Avoid adding functionality -;; that is not available in the version of Emacs recorded above or any -;; of the package dependencies. - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;; Eglot ("Emacs Polyglot") is an Emacs LSP client that stays out of -;; your way. -;; -;; Typing M-x eglot in some source file is often enough to get you -;; started, if the language server you're looking to use is installed -;; in your system. Please refer to the manual, available from -;; https://joaotavora.github.io/eglot/ or from M-x info for more usage -;; instructions. -;; -;; If you wish to contribute changes to Eglot, please do read the user -;; manual first. Additionally, take the following in consideration: - -;; * Eglot's main job is to hook up the information that language -;; servers offer via LSP to Emacs's UI facilities: Xref for -;; definition-chasing, Flymake for diagnostics, Eldoc for at-point -;; documentation, etc. Eglot's job is generally *not* to provide -;; such a UI itself, though a small number of simple -;; counter-examples do exist, e.g. in the `eglot-rename' command or -;; the `eglot-inlay-hints-mode' minor mode. When a new UI is -;; evidently needed, consider adding a new package to Emacs, or -;; extending an existing one. -;; -;; * Eglot was designed to function with just the UI facilities found -;; in the latest Emacs core, as long as those facilities are also -;; available as GNU ELPA :core packages. Historically, a number of -;; :core packages were added or reworked in Emacs to make this -;; possible. This principle should be upheld when adding new LSP -;; features or tweaking existing ones. Design any new facilities in -;; a way that they could work in the absence of LSP or using some -;; different protocol, then make sure Eglot can link up LSP -;; information to it. - -;; * There are few Eglot configuration variables. This principle -;; should also be upheld. If Eglot had these variables, it could be -;; duplicating configuration found elsewhere, bloating itself up, -;; and making it generally hard to integrate with the ever growing -;; set of LSP features and Emacs packages. For instance, this is -;; why one finds a single variable -;; `eglot-ignored-server-capabilities' instead of a number of -;; capability-specific flags, or why customizing the display of -;; LSP-provided documentation is done via ElDoc's variables, not -;; Eglot's. -;; -;; * Linking up LSP information to other libraries is generally done -;; in the `eglot--managed-mode' minor mode function, by -;; buffer-locally setting the other library's variables to -;; Eglot-specific versions. When deciding what to set the variable -;; to, the general idea is to choose a good default for beginners -;; that doesn't clash with Emacs's defaults. The settings are only -;; in place during Eglot's LSP-enriched tenure over a project. Even -;; so, some of those decisions will invariably aggravate a minority -;; of Emacs power users, but these users can use `eglot-stay-out-of' -;; and `eglot-managed-mode-hook' to adjust things to their -;; preferences. -;; -;; * On occasion, to enable new features, Eglot can have soft -;; dependencies on popular libraries that are not in Emacs core. -;; "Soft" means that the dependency doesn't impair any other use of -;; Eglot beyond that feature. Such is the case of the snippet -;; functionality, via the Yasnippet package, Markdown formatting of -;; at-point documentation via the markdown-mode package, and nicer -;; looking completions when the Company package is used. - -;;; Code: - -(require 'imenu) -(require 'cl-lib) - -(require 'url-parse) -(require 'url-util) -(require 'pcase) -(require 'compile) ; for some faces -(require 'warnings) -(eval-when-compile - (require 'subr-x)) -(require 'filenotify) -(require 'ert) -(require 'text-property-search nil t) -(require 'diff-mode) -(require 'diff) - -;; These dependencies are also GNU ELPA core packages. Because of -;; bug#62576, since there is a risk that M-x package-install, despite -;; having installed them, didn't correctly re-load them over the -;; built-in versions. -(eval-and-compile - ;; For those packages that are preloaded, reload them if needed, - ;; since that's the best we can do anyway. - ;; FIXME: Maybe the ELPA packages for those preloaded packages should - ;; force-reload themselves eagerly when the package is activated! - (let ((reload (if (fboundp 'require-with-check) ;Emacs≥30 - #'require-with-check - (lambda (feature &rest _) - ;; Just blindly reload like we used to do before - ;; `require-with-check'. - (load (symbol-name feature) nil 'nomessage))))) - - (funcall reload 'eldoc nil 'reload) - (funcall reload 'seq nil 'reload) - ;; For those packages which are not preloaded OTOH, signal an error if - ;; the loaded file is not the one that should have been loaded. - (mapc reload '(project flymake xref jsonrpc external-completion)))) - -;; forward-declare, but don't require (Emacs 28 doesn't seem to care) -(defvar markdown-fontify-code-blocks-natively) -(defvar company-backends) -(defvar company-tooltip-align-annotations) -(defvar tramp-ssh-controlmaster-options) -(defvar tramp-use-ssh-controlmaster-options) - - -;;; Obsolete aliases -;;; -(make-obsolete-variable 'eglot--managed-mode-hook - 'eglot-managed-mode-hook "1.6") -(define-obsolete-variable-alias 'eglot-confirm-server-initiated-edits - 'eglot-confirm-server-edits "1.16") -(make-obsolete-variable 'eglot-events-buffer-size - 'eglot-events-buffer-config "1.16") -(define-obsolete-function-alias 'eglot--uri-to-path #'eglot-uri-to-path "1.16") -(define-obsolete-function-alias 'eglot--path-to-uri #'eglot-path-to-uri "1.16") -(define-obsolete-function-alias 'eglot--range-region #'eglot-range-region "1.16") -(define-obsolete-function-alias 'eglot--server-capable #'eglot-server-capable "1.16") -(define-obsolete-function-alias 'eglot--server-capable-or-lose #'eglot-server-capable-or-lose "1.16") -(define-obsolete-function-alias - 'eglot-lsp-abiding-column #'eglot-utf-16-linepos "1.12") -(define-obsolete-function-alias - 'eglot-current-column #'eglot-utf-32-linepos "1.12") -(define-obsolete-variable-alias - 'eglot-current-column-function 'eglot-current-linepos-function "1.12") -(define-obsolete-function-alias - 'eglot-move-to-current-column #'eglot-move-to-utf-32-linepos "1.12") -(define-obsolete-function-alias - 'eglot-move-to-lsp-abiding-column #'eglot-move-to-utf-16-linepos "1.12") -(define-obsolete-variable-alias - 'eglot-move-to-column-function 'eglot-move-to-linepos-function "1.12") -(define-obsolete-variable-alias 'eglot-ignored-server-capabilites - 'eglot-ignored-server-capabilities "1.8") -;;;###autoload -(define-obsolete-function-alias 'eglot-update #'eglot-upgrade-eglot "29.1") - - -;;; User tweakable stuff -(defgroup eglot nil - "Interaction with Language Server Protocol servers." - :prefix "eglot-" - :group 'applications) - -(defun eglot-alternatives (alternatives) - "Compute server-choosing function for `eglot-server-programs'. -Each element of ALTERNATIVES is a string PROGRAM or a list of -strings (PROGRAM ARGS...) where program names an LSP server -program to start with ARGS. Returns a function to be invoked -automatically by Eglot on startup. When invoked, that function -will return a list (ABSPATH ARGS), where ABSPATH is the absolute -path of the PROGRAM that was chosen (interactively or -automatically)." - (lambda (&optional interactive _project) - ;; JT@2021-06-13: This function is way more complicated than it - ;; could be because it accounts for the fact that - ;; `eglot--executable-find' may take much longer to execute on - ;; remote files. - (let* ((listified (cl-loop for a in alternatives - collect (if (listp a) a (list a)))) - (err (lambda () - (error "None of '%s' are valid executables" - (mapconcat #'car listified ", "))))) - (cond ((and interactive current-prefix-arg) - ;; A C-u always lets user input something manually, - nil) - (interactive - (let* ((augmented (mapcar (lambda (a) - (let ((found (eglot--executable-find - (car a) t))) - (and found - (cons (car a) (cons found (cdr a)))))) - listified)) - (available (remove nil augmented))) - (cond ((cdr available) - (cdr (assoc - (completing-read - "[eglot] More than one server executable available: " - (mapcar #'car available) - nil t nil nil (car (car available))) - available #'equal))) - ((cdr (car available))) - (t - ;; Don't error when used interactively, let the - ;; Eglot prompt the user for alternative (github#719) - nil)))) - (t - (cl-loop for (p . args) in listified - for probe = (eglot--executable-find p t) - when probe return (cons probe args) - finally (funcall err))))))) - -(defvar eglot-server-programs `(((rust-ts-mode rust-mode) . ("rust-analyzer")) - ((cmake-mode cmake-ts-mode) . ("cmake-language-server")) - (vimrc-mode . ("vim-language-server" "--stdio")) - ((python-mode python-ts-mode) - . ,(eglot-alternatives - '("pylsp" "pyls" ("pyright-langserver" "--stdio") "jedi-language-server" "ruff-lsp"))) - ((js-json-mode json-mode json-ts-mode) - . ,(eglot-alternatives '(("vscode-json-language-server" "--stdio") - ("vscode-json-languageserver" "--stdio") - ("json-languageserver" "--stdio")))) - (((js-mode :language-id "javascript") - (js-ts-mode :language-id "javascript") - (tsx-ts-mode :language-id "typescriptreact") - (typescript-ts-mode :language-id "typescript") - (typescript-mode :language-id "typescript")) - . ("typescript-language-server" "--stdio")) - ((bash-ts-mode sh-mode) . ("bash-language-server" "start")) - ((php-mode phps-mode) - . ,(eglot-alternatives - '(("phpactor" "language-server") - ("php" "vendor/felixfbecker/language-server/bin/php-language-server.php")))) - ((c-mode c-ts-mode c++-mode c++-ts-mode objc-mode) - . ,(eglot-alternatives - '("clangd" "ccls"))) - (((caml-mode :language-id "ocaml") - (tuareg-mode :language-id "ocaml") reason-mode) - . ("ocamllsp")) - ((ruby-mode ruby-ts-mode) - . ("solargraph" "socket" "--port" :autoport)) - (haskell-mode - . ("haskell-language-server-wrapper" "--lsp")) - (elm-mode . ("elm-language-server")) - (mint-mode . ("mint" "ls")) - (kotlin-mode . ("kotlin-language-server")) - ((go-mode go-dot-mod-mode go-dot-work-mode go-ts-mode go-mod-ts-mode) - . ("gopls")) - ((R-mode ess-r-mode) . ("R" "--slave" "-e" - "languageserver::run()")) - ((java-mode java-ts-mode) . ("jdtls")) - ((dart-mode dart-ts-mode) - . ("dart" "language-server" - "--client-id" "emacs.eglot-dart")) - ((elixir-mode elixir-ts-mode heex-ts-mode) - . ,(if (and (fboundp 'w32-shell-dos-semantics) - (w32-shell-dos-semantics)) - '("language_server.bat") - (eglot-alternatives - '("language_server.sh" "start_lexical.sh")))) - (ada-mode . ("ada_language_server")) - (scala-mode . ,(eglot-alternatives - '("metals" "metals-emacs"))) - (racket-mode . ("racket" "-l" "racket-langserver")) - ((tex-mode context-mode texinfo-mode bibtex-mode) - . ,(eglot-alternatives '("digestif" "texlab"))) - (erlang-mode . ("erlang_ls" "--transport" "stdio")) - ((yaml-ts-mode yaml-mode) . ("yaml-language-server" "--stdio")) - (nix-mode . ,(eglot-alternatives '("nil" "rnix-lsp" "nixd"))) - (nickel-mode . ("nls")) - (gdscript-mode . ("localhost" 6008)) - ((fortran-mode f90-mode) . ("fortls")) - (futhark-mode . ("futhark" "lsp")) - ((lua-mode lua-ts-mode) . ,(eglot-alternatives - '("lua-language-server" "lua-lsp"))) - (zig-mode . ("zls")) - ((css-mode css-ts-mode) - . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio") - ("css-languageserver" "--stdio")))) - (html-mode . ,(eglot-alternatives '(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio")))) - ((dockerfile-mode dockerfile-ts-mode) . ("docker-langserver" "--stdio")) - ((clojure-mode clojurescript-mode clojurec-mode clojure-ts-mode) - . ("clojure-lsp")) - ((csharp-mode csharp-ts-mode) - . ,(eglot-alternatives - '(("omnisharp" "-lsp") - ("csharp-ls")))) - (purescript-mode . ("purescript-language-server" "--stdio")) - ((perl-mode cperl-mode) . ("perl" "-MPerl::LanguageServer" "-e" "Perl::LanguageServer::run")) - (markdown-mode - . ,(eglot-alternatives - '(("marksman" "server") - ("vscode-markdown-language-server" "--stdio")))) - (graphviz-dot-mode . ("dot-language-server" "--stdio")) - (terraform-mode . ("terraform-ls" "serve")) - ((uiua-ts-mode uiua-mode) . ("uiua" "lsp"))) - "How the command `eglot' guesses the server to start. -An association list of (MAJOR-MODE . CONTACT) pairs. MAJOR-MODE -identifies the buffers that are to be managed by a specific -language server. The associated CONTACT specifies how to connect -to a server for those buffers. - -MAJOR-MODE can be: - -* In the most common case, a symbol such as `c-mode'; - -* A list (MAJOR-MODE-SYMBOL :LANGUAGE-ID ID) where - MAJOR-MODE-SYMBOL is the aforementioned symbol and ID is a - string identifying the language to the server; - -* A list combining the previous two alternatives, meaning - multiple major modes will be associated with a single server - program. This association is such that the same resulting - server process will manage buffers of different major modes. - -CONTACT can be: - -* In the most common case, a list of strings (PROGRAM [ARGS...]). - PROGRAM is called with ARGS and is expected to serve LSP requests - over the standard input/output channels. - -* A list (PROGRAM [ARGS...] :initializationOptions OPTIONS), - whereupon PROGRAM is called with ARGS as in the first option, - and the LSP \"initializationOptions\" JSON object is - constructed from OPTIONS. If OPTIONS is a unary function, it - is called with the server instance and should return a JSON - object. - -* A list (HOST PORT [TCP-ARGS...]) where HOST is a string and - PORT is a positive integer for connecting to a server via TCP. - Remaining ARGS are passed to `open-network-stream' for - upgrading the connection with encryption or other capabilities. - -* A list (PROGRAM [ARGS...] :autoport [MOREARGS...]), whereupon a - combination of previous options is used. First, an attempt is - made to find an available server port, then PROGRAM is launched - with ARGS; the `:autoport' keyword substituted for that number; - and MOREARGS. Eglot then attempts to establish a TCP - connection to that port number on the localhost. - -* A cons (CLASS-NAME . INITARGS) where CLASS-NAME is a symbol - designating a subclass of `eglot-lsp-server', for representing - experimental LSP servers. INITARGS is a keyword-value plist - used to initialize the object of CLASS-NAME, or a plain list - interpreted as the previous descriptions of CONTACT. In the - latter case that plain list is used to produce a plist with a - suitable :PROCESS initarg to CLASS-NAME. The class - `eglot-lsp-server' descends from `jsonrpc-process-connection', - which you should see for the semantics of the mandatory - :PROCESS argument. - -* A function of two arguments (INTERACTIVE PROJECT) producing any - of the above values for CONTACT. INTERACTIVE will be t if an - interactive `M-x eglot' was used, and nil otherwise (e.g. from - `eglot-ensure'). Interactive calls may ask the user for hints - on finding the required programs, etc. PROJECT is whatever - project Eglot discovered via `project-find-functions' (which - see). The function should return nil or signal an error if it - can't produce a valid CONTACT. The helper function - `eglot-alternatives' (which see) can be used to produce a - function that offers more than one server for a given - MAJOR-MODE.") - -(defface eglot-highlight-symbol-face - '((t (:inherit bold))) - "Face used to highlight the symbol at point.") - -(defface eglot-mode-line - '((t (:inherit font-lock-constant-face :weight bold))) - "Face for package-name in Eglot's mode line.") - -(defface eglot-diagnostic-tag-unnecessary-face - '((t (:inherit shadow))) - "Face used to render unused or unnecessary code.") - -(defface eglot-diagnostic-tag-deprecated-face - '((t . (:inherit shadow :strike-through t))) - "Face used to render deprecated or obsolete code.") - -(defcustom eglot-autoreconnect 3 - "Control ability to reconnect automatically to the LSP server. -If t, always reconnect automatically (not recommended). If nil, -never reconnect automatically after unexpected server shutdowns, -crashes or network failures. A positive integer number says to -only autoreconnect if the previous successful connection attempt -lasted more than that many seconds." - :type '(choice (const :tag "Reconnect automatically" t) - (const :tag "Never reconnect" nil) - (integer :tag "Number of seconds"))) - -(defcustom eglot-connect-timeout 30 - "Number of seconds before timing out LSP connection attempts. -If nil, never time out." - :type '(choice (number :tag "Number of seconds") - (const :tag "Never time out" nil))) - -(defcustom eglot-sync-connect 3 - "Control blocking of LSP connection attempts. -If t, block for `eglot-connect-timeout' seconds. A positive -integer number means block for that many seconds, and then wait -for the connection in the background. nil has the same meaning -as 0, i.e. don't block at all." - :type '(choice (const :tag "Block for `eglot-connect-timeout' seconds" t) - (const :tag "Never block" nil) - (integer :tag "Number of seconds to block"))) - -(defcustom eglot-autoshutdown nil - "If non-nil, shut down server after killing last managed buffer." - :type 'boolean) - -(defcustom eglot-send-changes-idle-time 0.5 - "Don't tell server of changes before Emacs's been idle for this many seconds." - :type 'number) - -(defcustom eglot-events-buffer-config - (list :size (or (bound-and-true-p eglot-events-buffer-size) 2000000) - :format 'full) - "Configure the Eglot events buffer. - -Value is a plist accepting the keys `:size', which controls the -size in characters of the buffer (0 disables, nil means -infinite), and `:format', which controls the shape of each log -entry (`full' includes the original JSON, `lisp' uses -pretty-printed Lisp). - -For changes on this variable to take effect, you need to restart -the LSP connection. That can be done by `eglot-reconnect'." - :type '(plist :key-type (symbol :tag "Keyword") - :options (((const :tag "Size" :size) - (choice - (const :tag "No limit" nil) - (integer :tag "Number of characters"))) - ((const :tag "Format" :format) - (choice - (const :tag "Full with original JSON" full) - (const :tag "Shortened" short) - (const :tag "Pretty-printed lisp" lisp)))))) - -(defcustom eglot-confirm-server-edits '((eglot-rename . nil) - (t . maybe-summary)) - "Control if changes proposed by LSP should be confirmed with user. - -If this variable's value is the symbol `diff', a diff buffer is -pops up, allowing the user to apply each change individually. If -the symbol `summary' or any other non-nil value, the user is -prompted in the minibuffer with aa short summary of changes. The -symbols `maybe-diff' and `maybe-summary' mean that the -confirmation is offered to the user only if the changes target -files visited in buffers. Finally, a nil value means all changes -are applied directly without any confirmation. - -If this variable's value can also be an alist ((COMMAND . ACTION) -...) where COMMAND is a symbol designating a command, such as -`eglot-rename', `eglot-code-actions', -`eglot-code-action-quickfix', etc. ACTION is one of the symbols -described above. The value `t' for COMMAND is accepted and its -ACTION is the default value for commands not in the alist." - :type (let ((basic-choices - '((const :tag "Use diff" diff) - (const :tag "Summarize and prompt" summary) - (const :tag "Maybe use diff" maybe-diff) - (const :tag "Maybe summarize and prompt" maybe-summary) - (const :tag "Don't confirm" nil)))) - `(choice ,@basic-choices - (alist :tag "Per-command alist" - :key-type (choice (function :tag "Command") - (const :tag "Default" t)) - :value-type (choice . ,basic-choices))))) - -(defcustom eglot-extend-to-xref nil - "If non-nil, activate Eglot in cross-referenced non-project files." - :type 'boolean) - -(defcustom eglot-prefer-plaintext nil - "If non-nil, always request plaintext responses to hover requests." - :type 'boolean) - -(defcustom eglot-menu-string "eglot" - "String displayed in mode line when Eglot is active." - :type 'string) - -(defcustom eglot-report-progress t - "If non-nil, show progress of long running LSP server work. -If set to `messages', use *Messages* buffer, else use Eglot's -mode line indicator." - :type '(choice (const :tag "Don't show progress" nil) - (const :tag "Show progress in *Messages*" messages) - (const :tag "Show progress in Eglot's mode line indicator" t)) - :version "1.10") - -(defcustom eglot-ignored-server-capabilities (list) - "LSP server capabilities that Eglot could use, but won't. -You could add, for instance, the symbol -`:documentHighlightProvider' to prevent automatic highlighting -under cursor." - :type '(set - :tag "Tick the ones you're not interested in" - (const :tag "Documentation on hover" :hoverProvider) - (const :tag "Code completion" :completionProvider) - (const :tag "Function signature help" :signatureHelpProvider) - (const :tag "Go to definition" :definitionProvider) - (const :tag "Go to type definition" :typeDefinitionProvider) - (const :tag "Go to implementation" :implementationProvider) - (const :tag "Go to declaration" :declarationProvider) - (const :tag "Find references" :referencesProvider) - (const :tag "Highlight symbols automatically" :documentHighlightProvider) - (const :tag "List symbols in buffer" :documentSymbolProvider) - (const :tag "List symbols in workspace" :workspaceSymbolProvider) - (const :tag "Execute code actions" :codeActionProvider) - (const :tag "Code lens" :codeLensProvider) - (const :tag "Format buffer" :documentFormattingProvider) - (const :tag "Format portion of buffer" :documentRangeFormattingProvider) - (const :tag "On-type formatting" :documentOnTypeFormattingProvider) - (const :tag "Rename symbol" :renameProvider) - (const :tag "Highlight links in document" :documentLinkProvider) - (const :tag "Decorate color references" :colorProvider) - (const :tag "Fold regions of buffer" :foldingRangeProvider) - (const :tag "Execute custom commands" :executeCommandProvider) - (const :tag "Inlay hints" :inlayHintProvider))) - -(defvar eglot-withhold-process-id nil - "If non-nil, Eglot will not send the Emacs process id to the language server. -This can be useful when using docker to run a language server.") - - -;;; Constants -;;; -(defconst eglot--version - (eval-when-compile - (when byte-compile-current-file - (require 'lisp-mnt) - (lm-version byte-compile-current-file))) - "The version as a string of this version of Eglot. -It is nil if Eglot is not byte-complied.") - -(defconst eglot--symbol-kind-names - `((1 . "File") (2 . "Module") - (3 . "Namespace") (4 . "Package") (5 . "Class") - (6 . "Method") (7 . "Property") (8 . "Field") - (9 . "Constructor") (10 . "Enum") (11 . "Interface") - (12 . "Function") (13 . "Variable") (14 . "Constant") - (15 . "String") (16 . "Number") (17 . "Boolean") - (18 . "Array") (19 . "Object") (20 . "Key") - (21 . "Null") (22 . "EnumMember") (23 . "Struct") - (24 . "Event") (25 . "Operator") (26 . "TypeParameter"))) - -(defconst eglot--kind-names - `((1 . "Text") (2 . "Method") (3 . "Function") (4 . "Constructor") - (5 . "Field") (6 . "Variable") (7 . "Class") (8 . "Interface") - (9 . "Module") (10 . "Property") (11 . "Unit") (12 . "Value") - (13 . "Enum") (14 . "Keyword") (15 . "Snippet") (16 . "Color") - (17 . "File") (18 . "Reference") (19 . "Folder") (20 . "EnumMember") - (21 . "Constant") (22 . "Struct") (23 . "Event") (24 . "Operator") - (25 . "TypeParameter"))) - -(defconst eglot--tag-faces - `((1 . eglot-diagnostic-tag-unnecessary-face) - (2 . eglot-diagnostic-tag-deprecated-face))) - -(defvaralias 'eglot-{} 'eglot--{}) - -(defconst eglot--{} (make-hash-table :size 0) "The empty JSON object.") - -(defun eglot--executable-find (command &optional remote) - "Like Emacs 27's `executable-find', ignore REMOTE on Emacs 26." - (if (>= emacs-major-version 27) (executable-find command remote) - (executable-find command))) - -(defun eglot--accepted-formats () - (if (and (not eglot-prefer-plaintext) (fboundp 'gfm-view-mode)) - ["markdown" "plaintext"] ["plaintext"])) - -(defconst eglot--uri-path-allowed-chars - (let ((vec (copy-sequence url-path-allowed-chars))) - (aset vec ?: nil) ;; see github#639 - vec) - "Like `url-path-allows-chars' but more restrictive.") - - -;;; Message verification helpers -;;; -(eval-and-compile - (defvar eglot--lsp-interface-alist - `( - (CodeAction (:title) (:kind :diagnostics :edit :command :isPreferred :data)) - (ConfigurationItem () (:scopeUri :section)) - (Command ((:title . string) (:command . string)) (:arguments)) - (CompletionItem (:label) - (:kind :detail :documentation :deprecated :preselect - :sortText :filterText :insertText :insertTextFormat - :textEdit :additionalTextEdits :commitCharacters - :command :data :tags)) - (Diagnostic (:range :message) (:severity :code :source :relatedInformation :codeDescription :tags)) - (DocumentHighlight (:range) (:kind)) - (FileSystemWatcher (:globPattern) (:kind)) - (Hover (:contents) (:range)) - (InitializeResult (:capabilities) (:serverInfo)) - (Location (:uri :range)) - (LocationLink (:targetUri :targetRange :targetSelectionRange) (:originSelectionRange)) - (LogMessageParams (:type :message)) - (MarkupContent (:kind :value)) - (ParameterInformation (:label) (:documentation)) - (Position (:line :character)) - (Range (:start :end)) - (Registration (:id :method) (:registerOptions)) - (ResponseError (:code :message) (:data)) - (ShowMessageParams (:type :message)) - (ShowMessageRequestParams (:type :message) (:actions)) - (SignatureHelp (:signatures) (:activeSignature :activeParameter)) - (SignatureInformation (:label) (:documentation :parameters :activeParameter)) - (SymbolInformation (:name :kind :location) - (:deprecated :containerName)) - (DocumentSymbol (:name :range :selectionRange :kind) - (:detail :deprecated :children)) - (TextDocumentEdit (:textDocument :edits) ()) - (TextEdit (:range :newText)) - (VersionedTextDocumentIdentifier (:uri :version) ()) - (WorkDoneProgress (:kind) (:title :message :percentage :cancellable)) - (WorkspaceEdit () (:changes :documentChanges)) - (WorkspaceSymbol (:name :kind) (:containerName :location :data)) - (InlayHint (:position :label) (:kind :textEdits :tooltip :paddingLeft - :paddingRight :data)) - (InlayHintLabelPart (:value) (:tooltip :location :command))) - "Alist (INTERFACE-NAME . INTERFACE) of known external LSP interfaces. - -INTERFACE-NAME is a symbol designated by the spec as -\"interface\". INTERFACE is a list (REQUIRED OPTIONAL) where -REQUIRED and OPTIONAL are lists of KEYWORD designating field -names that must be, or may be, respectively, present in a message -adhering to that interface. KEY can be a keyword or a cons (SYM -TYPE), where type is used by `cl-typep' to check types at -runtime. - -Here's what an element of this alist might look like: - - (Command ((:title . string) (:command . string)) (:arguments))")) - -(eval-and-compile - (defvar eglot-strict-mode - '(;; Uncomment next lines for fun and debugging - ;; disallow-non-standard-keys - ;; enforce-required-keys - ;; enforce-optional-keys - no-unknown-interfaces) - "How strictly to check LSP interfaces at compile- and run-time. - -Value is a list of symbols (if the list is empty, no checks are -performed). - -If the symbol `disallow-non-standard-keys' is present, an error -is raised if any extraneous fields are sent by the server. At -compile-time, a warning is raised if a destructuring spec -includes such a field. - -If the symbol `enforce-required-keys' is present, an error is -raised if any required fields are missing from the message sent -from the server. At compile-time, a warning is raised if a -destructuring spec doesn't use such a field. - -If the symbol `enforce-optional-keys' is present, nothing special -happens at run-time. At compile-time, a warning is raised if a -destructuring spec doesn't use all optional fields. - -If the symbol `disallow-unknown-methods' is present, Eglot warns -on unknown notifications and errors on unknown requests. - -If the symbol `no-unknown-interfaces' is present, Eglot warns at -compile time if an undeclared LSP interface is used.")) - -(cl-defun eglot--check-object (interface-name - object - &optional - (enforce-required t) - (disallow-non-standard t) - (check-types t)) - "Check that OBJECT conforms to INTERFACE. Error otherwise." - (cl-destructuring-bind - (&key types required-keys optional-keys &allow-other-keys) - (eglot--interface interface-name) - (when-let ((missing (and enforce-required - (cl-set-difference required-keys - (eglot--plist-keys object))))) - (eglot--error "A `%s' must have %s" interface-name missing)) - (when-let ((excess (and disallow-non-standard - (cl-set-difference - (eglot--plist-keys object) - (append required-keys optional-keys))))) - (eglot--error "A `%s' mustn't have %s" interface-name excess)) - (when check-types - (cl-loop - for (k v) on object by #'cddr - for type = (or (cdr (assoc k types)) t) ;; FIXME: enforce nil type? - unless (cl-typep v type) - do (eglot--error "A `%s' must have a %s as %s, but has %s" - interface-name))) - t)) - -(eval-and-compile - (defun eglot--keywordize-vars (vars) - (mapcar (lambda (var) (intern (format ":%s" var))) vars)) - - (defun eglot--ensure-type (k) (if (consp k) k (cons k t))) - - (defun eglot--interface (interface-name) - (let* ((interface (assoc interface-name eglot--lsp-interface-alist)) - (required (mapcar #'eglot--ensure-type (car (cdr interface)))) - (optional (mapcar #'eglot--ensure-type (cadr (cdr interface))))) - (list :types (append required optional) - :required-keys (mapcar #'car required) - :optional-keys (mapcar #'car optional)))) - - (defun eglot--check-dspec (interface-name dspec) - "Check destructuring spec DSPEC against INTERFACE-NAME." - (cl-destructuring-bind (&key required-keys optional-keys &allow-other-keys) - (eglot--interface interface-name) - (cond ((or required-keys optional-keys) - (let ((too-many - (and - (memq 'disallow-non-standard-keys eglot-strict-mode) - (cl-set-difference - (eglot--keywordize-vars dspec) - (append required-keys optional-keys)))) - (ignored-required - (and - (memq 'enforce-required-keys eglot-strict-mode) - (cl-set-difference - required-keys (eglot--keywordize-vars dspec)))) - (missing-out - (and - (memq 'enforce-optional-keys eglot-strict-mode) - (cl-set-difference - optional-keys (eglot--keywordize-vars dspec))))) - (when too-many (byte-compile-warn - "Destructuring for %s has extraneous %s" - interface-name too-many)) - (when ignored-required (byte-compile-warn - "Destructuring for %s ignores required %s" - interface-name ignored-required)) - (when missing-out (byte-compile-warn - "Destructuring for %s is missing out on %s" - interface-name missing-out)))) - ((memq 'no-unknown-interfaces eglot-strict-mode) - (byte-compile-warn "Unknown LSP interface %s" interface-name)))))) - -(cl-defmacro eglot--dbind (vars object &body body) - "Destructure OBJECT, binding VARS in BODY. -VARS is ([(INTERFACE)] SYMS...) -Honor `eglot-strict-mode'." - (declare (indent 2) (debug (sexp sexp &rest form))) - (let ((interface-name (if (consp (car vars)) - (car (pop vars)))) - (object-once (make-symbol "object-once")) - (fn-once (make-symbol "fn-once"))) - (cond (interface-name - (eglot--check-dspec interface-name vars) - `(let ((,object-once ,object)) - (cl-destructuring-bind (&key ,@vars &allow-other-keys) ,object-once - (eglot--check-object ',interface-name ,object-once - (memq 'enforce-required-keys eglot-strict-mode) - (memq 'disallow-non-standard-keys eglot-strict-mode) - (memq 'check-types eglot-strict-mode)) - ,@body))) - (t - `(let ((,object-once ,object) - (,fn-once (lambda (,@vars) ,@body))) - (if (memq 'disallow-non-standard-keys eglot-strict-mode) - (cl-destructuring-bind (&key ,@vars) ,object-once - (funcall ,fn-once ,@vars)) - (cl-destructuring-bind (&key ,@vars &allow-other-keys) ,object-once - (funcall ,fn-once ,@vars)))))))) - -(cl-defmacro eglot--lambda (cl-lambda-list &body body) - "Function of args CL-LAMBDA-LIST for processing INTERFACE objects. -Honor `eglot-strict-mode'." - (declare (indent 1) (debug (sexp &rest form))) - (let ((e (cl-gensym "jsonrpc-lambda-elem"))) - `(lambda (,e) (cl-block nil (eglot--dbind ,cl-lambda-list ,e ,@body))))) - -(cl-defmacro eglot--dcase (obj &rest clauses) - "Like `pcase', but for the LSP object OBJ. -CLAUSES is a list (DESTRUCTURE FORMS...) where DESTRUCTURE is -treated as in `eglot--dbind'." - (declare (indent 1) (debug (sexp &rest (sexp &rest form)))) - (let ((obj-once (make-symbol "obj-once"))) - `(let ((,obj-once ,obj)) - (cond - ,@(cl-loop - for (vars . body) in clauses - for vars-as-keywords = (eglot--keywordize-vars vars) - for interface-name = (if (consp (car vars)) - (car (pop vars))) - for condition = - (cond (interface-name - (eglot--check-dspec interface-name vars) - ;; In this mode, in runtime, we assume - ;; `eglot-strict-mode' is partially on, otherwise we - ;; can't disambiguate between certain types. - `(ignore-errors - (eglot--check-object - ',interface-name ,obj-once - t - (memq 'disallow-non-standard-keys eglot-strict-mode) - t))) - (t - ;; In this interface-less mode we don't check - ;; `eglot-strict-mode' at all: just check that the object - ;; has all the keys the user wants to destructure. - `(null (cl-set-difference - ',vars-as-keywords - (eglot--plist-keys ,obj-once))))) - collect `(,condition - (cl-destructuring-bind (&key ,@vars &allow-other-keys) - ,obj-once - ,@body))) - (t - (eglot--error "%S didn't match any of %S" - ,obj-once - ',(mapcar #'car clauses))))))) - -(cl-defmacro eglot--when-live-buffer (buf &rest body) - "Check BUF live, then do BODY in it." (declare (indent 1) (debug t)) - (let ((b (cl-gensym))) - `(let ((,b ,buf)) (if (buffer-live-p ,b) (with-current-buffer ,b ,@body))))) - -(cl-defmacro eglot--when-buffer-window (buf &body body) - "Check BUF showing somewhere, then do BODY in it." (declare (indent 1) (debug t)) - (let ((b (cl-gensym))) - `(let ((,b ,buf)) - ;;notice the exception when testing with `ert' - (when (or (get-buffer-window ,b) (ert-running-test)) - (with-current-buffer ,b ,@body))))) - -(cl-defmacro eglot--widening (&rest body) - "Save excursion and restriction. Widen. Then run BODY." (declare (debug t)) - `(save-excursion (save-restriction (widen) ,@body))) - - -;;; Public Elisp API -;;; -(cl-defgeneric eglot-handle-request (server method &rest params) - "Handle SERVER's METHOD request with PARAMS.") - -(cl-defgeneric eglot-handle-notification (server method &rest params) - "Handle SERVER's METHOD notification with PARAMS.") - -(cl-defgeneric eglot-execute-command (_ _ _) - (declare (obsolete eglot-execute "30.1")) - (:method - (server command arguments) - (eglot--request server :workspace/executeCommand - `(:command ,(format "%s" command) :arguments ,arguments)))) - -(cl-defgeneric eglot-execute (server action) - "Ask SERVER to execute ACTION. -ACTION is an LSP object of either `CodeAction' or `Command' type." - (:method - (server action) "Default implementation." - (eglot--dcase action - (((Command)) (eglot--request server :workspace/executeCommand action)) - (((CodeAction) edit command data) - (if (and (null edit) (null command) data - (eglot-server-capable :codeActionProvider :resolveProvider)) - (eglot-execute server (eglot--request server :codeAction/resolve action)) - (when edit (eglot--apply-workspace-edit edit this-command)) - (when command (eglot--request server :workspace/executeCommand command))))))) - -(cl-defgeneric eglot-initialization-options (server) - "JSON object to send under `initializationOptions'." - (:method (s) - (let ((probe (plist-get (eglot--saved-initargs s) :initializationOptions))) - (cond ((functionp probe) (funcall probe s)) - (probe) - (t eglot--{}))))) - -(cl-defgeneric eglot-register-capability (server method id &rest params) - "Ask SERVER to register capability METHOD marked with ID." - (:method - (_s method _id &rest _params) - (eglot--warn "Server tried to register unsupported capability `%s'" - method))) - -(cl-defgeneric eglot-unregister-capability (server method id &rest params) - "Ask SERVER to register capability METHOD marked with ID." - (:method - (_s method _id &rest _params) - (eglot--warn "Server tried to unregister unsupported capability `%s'" - method))) - -(cl-defgeneric eglot-client-capabilities (server) - "What the Eglot LSP client supports for SERVER." - (:method (s) - (list - :workspace (list - :applyEdit t - :executeCommand `(:dynamicRegistration :json-false) - :workspaceEdit `(:documentChanges t) - :didChangeWatchedFiles - `(:dynamicRegistration - ,(if (eglot--trampish-p s) :json-false t)) - :symbol `(:dynamicRegistration :json-false) - :configuration t - :workspaceFolders t) - :textDocument - (list - :synchronization (list - :dynamicRegistration :json-false - :willSave t :willSaveWaitUntil t :didSave t) - :completion (list :dynamicRegistration :json-false - :completionItem - `(:snippetSupport - ,(if (and - (not (eglot--stay-out-of-p 'yasnippet)) - (eglot--snippet-expansion-fn)) - t - :json-false) - :deprecatedSupport t - :resolveSupport (:properties - ["documentation" - "details" - "additionalTextEdits"]) - :tagSupport (:valueSet [1])) - :contextSupport t) - :hover (list :dynamicRegistration :json-false - :contentFormat (eglot--accepted-formats)) - :signatureHelp (list :dynamicRegistration :json-false - :signatureInformation - `(:parameterInformation - (:labelOffsetSupport t) - :documentationFormat ,(eglot--accepted-formats) - :activeParameterSupport t)) - :references `(:dynamicRegistration :json-false) - :definition (list :dynamicRegistration :json-false - :linkSupport t) - :declaration (list :dynamicRegistration :json-false - :linkSupport t) - :implementation (list :dynamicRegistration :json-false - :linkSupport t) - :typeDefinition (list :dynamicRegistration :json-false - :linkSupport t) - :documentSymbol (list - :dynamicRegistration :json-false - :hierarchicalDocumentSymbolSupport t - :symbolKind `(:valueSet - [,@(mapcar - #'car eglot--symbol-kind-names)])) - :documentHighlight `(:dynamicRegistration :json-false) - :codeAction (list - :dynamicRegistration :json-false - :resolveSupport `(:properties ["edit" "command"]) - :dataSupport t - :codeActionLiteralSupport - '(:codeActionKind - (:valueSet - ["quickfix" - "refactor" "refactor.extract" - "refactor.inline" "refactor.rewrite" - "source" "source.organizeImports"])) - :isPreferredSupport t) - :formatting `(:dynamicRegistration :json-false) - :rangeFormatting `(:dynamicRegistration :json-false) - :rename `(:dynamicRegistration :json-false) - :inlayHint `(:dynamicRegistration :json-false) - :publishDiagnostics (list :relatedInformation :json-false - ;; TODO: We can support :codeDescription after - ;; adding an appropriate UI to - ;; Flymake. - :codeDescriptionSupport :json-false - :tagSupport - `(:valueSet - [,@(mapcar - #'car eglot--tag-faces)]))) - :window `(:showDocument (:support t) - :workDoneProgress t) - :general (list :positionEncodings ["utf-32" "utf-8" "utf-16"]) - :experimental eglot--{}))) - -(cl-defgeneric eglot-workspace-folders (server) - "Return workspaceFolders for SERVER." - (let ((project (eglot--project server))) - (vconcat - (mapcar (lambda (dir) - (list :uri (eglot-path-to-uri dir) - :name (abbreviate-file-name dir))) - `(,(project-root project) ,@(project-external-roots project)))))) - -(defclass eglot-lsp-server (jsonrpc-process-connection) - ((project-nickname - :documentation "Short nickname for the associated project." - :accessor eglot--project-nickname - :reader eglot-project-nickname) - (languages - :initform nil - :documentation "Alist ((MODE . LANGUAGE-ID-STRING)...) of managed languages." - :accessor eglot--languages) - (capabilities - :initform nil - :documentation "JSON object containing server capabilities." - :accessor eglot--capabilities) - (server-info - :initform nil - :documentation "JSON object containing server info." - :accessor eglot--server-info) - (shutdown-requested - :initform nil - :documentation "Flag set when server is shutting down." - :accessor eglot--shutdown-requested) - (project - :initform nil - :documentation "Project associated with server." - :accessor eglot--project) - (progress-reporters - :initform (make-hash-table :test #'equal) :accessor eglot--progress-reporters - :documentation "Maps LSP progress tokens to progress reporters.") - (inhibit-autoreconnect - :initform t - :documentation "Generalized boolean inhibiting auto-reconnection if true." - :accessor eglot--inhibit-autoreconnect) - (file-watches - :documentation "Map (DIR -> (WATCH ID1 ID2...)) for `didChangeWatchedFiles'." - :initform (make-hash-table :test #'equal) :accessor eglot--file-watches) - (managed-buffers - :initform nil - :documentation "List of buffers managed by server." - :accessor eglot--managed-buffers) - (saved-initargs - :documentation "Saved initargs for reconnection purposes." - :accessor eglot--saved-initargs)) - :documentation - "Represents a server. Wraps a process for LSP communication.") - -(declare-function w32-long-file-name "w32proc.c" (fn)) -(defun eglot-uri-to-path (uri) - "Convert URI to file path, helped by `eglot--current-server'." - (when (keywordp uri) (setq uri (substring (symbol-name uri) 1))) - (let* ((server (eglot-current-server)) - (remote-prefix (and server (eglot--trampish-p server))) - (url (url-generic-parse-url uri))) - ;; Only parse file:// URIs, leave other URI untouched as - ;; `file-name-handler-alist' should know how to handle them - ;; (bug#58790). - (if (string= "file" (url-type url)) - (let* ((retval (url-unhex-string (url-filename url))) - ;; Remove the leading "/" for local MS Windows-style paths. - (normalized (if (and (not remote-prefix) - (eq system-type 'windows-nt) - (cl-plusp (length retval))) - (w32-long-file-name (substring retval 1)) - retval))) - (concat remote-prefix normalized)) - uri))) - -(defun eglot-path-to-uri (path) - "Convert PATH, a file name, to LSP URI string and return it." - (let ((truepath (file-truename path))) - (if (and (url-type (url-generic-parse-url path)) - ;; It might be MS Windows path which includes a drive - ;; letter that looks like a URL scheme (bug#59338) - (not (and (eq system-type 'windows-nt) - (file-name-absolute-p truepath)))) - ;; Path is already a URI, so forward it to the LSP server - ;; untouched. The server should be able to handle it, since - ;; it provided this URI to clients in the first place. - path - (concat "file://" - ;; Add a leading "/" for local MS Windows-style paths. - (if (and (eq system-type 'windows-nt) - (not (file-remote-p truepath))) - "/") - (url-hexify-string - ;; Again watch out for trampy paths. - (directory-file-name (file-local-name truepath)) - eglot--uri-path-allowed-chars))))) - -(defun eglot-range-region (range &optional markers) - "Return a cons (BEG . END) of positions representing LSP RANGE. -If optional MARKERS, make markers instead." - (let* ((st (plist-get range :start)) - (beg (eglot--lsp-position-to-point st markers)) - (end (eglot--lsp-position-to-point (plist-get range :end) markers))) - (cons beg end))) - -(defun eglot-server-capable (&rest feats) - "Determine if current server is capable of FEATS." - (unless (cl-some (lambda (feat) - (memq feat eglot-ignored-server-capabilities)) - feats) - (cl-loop for caps = (eglot--capabilities (eglot--current-server-or-lose)) - then (cadr probe) - for (feat . more) on feats - for probe = (plist-member caps feat) - if (not probe) do (cl-return nil) - if (eq (cadr probe) :json-false) do (cl-return nil) - if (not (listp (cadr probe))) do (cl-return (if more nil (cadr probe))) - finally (cl-return (or (cadr probe) t))))) - -(defun eglot-server-capable-or-lose (&rest feats) - "Like `eglot-server-capable', but maybe error out." - (let ((retval (apply #'eglot-server-capable feats))) - (unless retval - (eglot--error "Unsupported or ignored LSP capability `%s'" - (mapconcat #'symbol-name feats " "))) - retval)) - - -;;; Process/server management -(defun eglot--major-modes (s) "Major modes server S is responsible for." - (mapcar #'car (eglot--languages s))) - -(defun eglot--language-ids (s) "LSP Language ID strings for server S's modes." - (mapcar #'cdr (eglot--languages s))) - -(cl-defmethod initialize-instance :before ((_server eglot-lsp-server) &optional args) - (cl-remf args :initializationOptions)) - -(defvar eglot--servers-by-project (make-hash-table :test #'equal) - "Keys are projects. Values are lists of processes.") - -(defun eglot-shutdown (server &optional _interactive timeout preserve-buffers) - "Politely ask SERVER to quit. -Interactively, read SERVER from the minibuffer unless there is -only one and it's managing the current buffer. - -Forcefully quit it if it doesn't respond within TIMEOUT seconds. -TIMEOUT defaults to 1.5 seconds. Don't leave this function with -the server still running. - -If PRESERVE-BUFFERS is non-nil (interactively, when called with a -prefix argument), do not kill events and output buffers of -SERVER." - (interactive (list (eglot--read-server "Shutdown which server" - (eglot-current-server)) - t nil current-prefix-arg)) - (eglot--message "Asking %s politely to terminate" (jsonrpc-name server)) - (unwind-protect - (progn - (setf (eglot--shutdown-requested server) t) - (eglot--request server :shutdown nil :timeout (or timeout 1.5)) - (jsonrpc-notify server :exit nil)) - ;; Now ask jsonrpc.el to shut down the server. - (jsonrpc-shutdown server (not preserve-buffers)) - (unless preserve-buffers (kill-buffer (jsonrpc-events-buffer server))))) - -(defun eglot-shutdown-all (&optional preserve-buffers) - "Politely ask all language servers to quit, in order. -PRESERVE-BUFFERS as in `eglot-shutdown', which see." - (interactive (list current-prefix-arg)) - (cl-loop for ss being the hash-values of eglot--servers-by-project - do (with-demoted-errors "[eglot] shutdown all: %s" - (cl-loop for s in ss do (eglot-shutdown s nil nil preserve-buffers))))) - -(defvar eglot--servers-by-xrefed-file (make-hash-table :test 'equal)) - -(defun eglot--on-shutdown (server) - "Called by jsonrpc.el when SERVER is already dead." - ;; Turn off `eglot--managed-mode' where appropriate. - (dolist (buffer (eglot--managed-buffers server)) - (let (;; Avoid duplicate shutdowns (github#389) - (eglot-autoshutdown nil)) - (eglot--when-live-buffer buffer (eglot--managed-mode-off)))) - ;; Kill any expensive watches - (maphash (lambda (_dir watch-and-ids) - (file-notify-rm-watch (car watch-and-ids))) - (eglot--file-watches server)) - ;; Sever the project/server relationship for `server' - (setf (gethash (eglot--project server) eglot--servers-by-project) - (delq server - (gethash (eglot--project server) eglot--servers-by-project))) - (maphash (lambda (f s) - (when (eq s server) (remhash f eglot--servers-by-xrefed-file))) - eglot--servers-by-xrefed-file) - (cond ((eglot--shutdown-requested server) - t) - ((not (eglot--inhibit-autoreconnect server)) - (eglot--warn "Reconnecting after unexpected server exit.") - (eglot-reconnect server)) - ((timerp (eglot--inhibit-autoreconnect server)) - (eglot--warn "Not auto-reconnecting, last one didn't last long.")))) - -(defun eglot--all-major-modes () - "Return all known major modes." - (let ((retval)) - (mapatoms (lambda (sym) - (when (plist-member (symbol-plist sym) 'derived-mode-parent) - (push sym retval)))) - retval)) - -(defvar eglot-command-history nil - "History of CONTACT arguments to `eglot'.") - -(defun eglot--lookup-mode (mode) - "Lookup `eglot-server-programs' for MODE. -Return (LANGUAGES . CONTACT-PROXY). - -MANAGED-MODES is a list with MODE as its first element. -Subsequent elements are other major modes also potentially -managed by the server that is to manage MODE. - -LANGUAGE-IDS is a list of the same length as MANAGED-MODES. Each -elem is derived from the corresponding mode name, if not -specified in `eglot-server-programs' (which see). - -CONTACT-PROXY is the value of the corresponding -`eglot-server-programs' entry." - (cl-flet ((languages (main-mode-sym specs) - (let* ((res - (mapcar (jsonrpc-lambda (sym &key language-id &allow-other-keys) - (cons sym - (or language-id - (or (get sym 'eglot-language-id) - (replace-regexp-in-string - "\\(?:-ts\\)?-mode$" "" - (symbol-name sym)))))) - specs)) - (head (cl-find main-mode-sym res :key #'car))) - (cons head (delq head res))))) - (cl-loop - for (modes . contact) in eglot-server-programs - for specs = (mapcar #'eglot--ensure-list - (if (or (symbolp modes) (keywordp (cadr modes))) - (list modes) modes)) - thereis (cl-some (lambda (spec) - (cl-destructuring-bind (sym &key &allow-other-keys) spec - (and (provided-mode-derived-p mode sym) - (cons (languages sym specs) contact)))) - specs)))) - -(defun eglot--guess-contact (&optional interactive) - "Helper for `eglot'. -Return (MANAGED-MODES PROJECT CLASS CONTACT LANG-IDS). If INTERACTIVE is -non-nil, maybe prompt user, else error as soon as something can't -be guessed." - (let* ((project (eglot--current-project)) - (guessed-mode (if buffer-file-name major-mode)) - (guessed-mode-name (and guessed-mode (symbol-name guessed-mode))) - (main-mode - (cond - ((and interactive - (or (>= (prefix-numeric-value current-prefix-arg) 16) - (not guessed-mode))) - (intern - (completing-read - "[eglot] Start a server to manage buffers of what major mode? " - (mapcar #'symbol-name (eglot--all-major-modes)) nil t - guessed-mode-name nil guessed-mode-name nil))) - ((not guessed-mode) - (eglot--error "Can't guess mode to manage for `%s'" (current-buffer))) - (t guessed-mode))) - (languages-and-contact (eglot--lookup-mode main-mode)) - (managed-modes (mapcar #'car (car languages-and-contact))) - (language-ids (mapcar #'cdr (car languages-and-contact))) - (guess (cdr languages-and-contact)) - (guess (if (functionp guess) - (pcase (cdr (func-arity guess)) - (1 (funcall guess interactive)) - (_ (funcall guess interactive project))) - guess)) - (class (or (and (consp guess) (symbolp (car guess)) - (prog1 (unless current-prefix-arg (car guess)) - (setq guess (cdr guess)))) - 'eglot-lsp-server)) - (program (and (listp guess) - (stringp (car guess)) - ;; A second element might be the port of a (host, port) - ;; pair, but in that case it is not a string. - (or (null (cdr guess)) (stringp (cadr guess))) - (car guess))) - (base-prompt - (and interactive - "Enter program to execute (or :): ")) - (full-program-invocation - (and program - (cl-every #'stringp guess) - (combine-and-quote-strings guess))) - (prompt - (and base-prompt - (cond (current-prefix-arg base-prompt) - ((null guess) - (format "[eglot] Couldn't guess LSP server for `%s'\n%s" - main-mode base-prompt)) - ((and program - (not (file-name-absolute-p program)) - (not (eglot--executable-find program t))) - (if full-program-invocation - (concat (format "[eglot] I guess you want to run `%s'" - full-program-invocation) - (format ", but I can't find `%s' in PATH!" - program) - "\n" base-prompt) - (eglot--error - (concat "`%s' not found in PATH, but can't form" - " an interactive prompt for help you fix" - " this.") - program guess)))))) - (input (and prompt (read-shell-command prompt - full-program-invocation - 'eglot-command-history))) - (contact - (if input - (if (string-match - "^[\s\t]*\\(.*\\):\\([[:digit:]]+\\)[\s\t]*$" input) - ;; : special case (bug#67682) - (list (match-string 1 input) - (string-to-number (match-string 2 input))) - (split-string-and-unquote input)) - guess))) - (list managed-modes project class contact language-ids))) - -(defvar eglot-lsp-context nil - "Dynamically non-nil when searching for projects in LSP context.") - -(defun eglot--current-project () - "Return a project object for Eglot's LSP purposes. -This relies on `project-current' and thus on -`project-find-functions'. Functions in the latter -variable (which see) can query the value `eglot-lsp-context' to -decide whether a given directory is a project containing a -suitable root directory for a given LSP server's purposes." - (let ((eglot-lsp-context t)) - (or (project-current) - `(transient . ,(expand-file-name default-directory))))) - -(cl-defmethod project-root ((project (head eglot--project))) - (cadr project)) - -;;;###autoload -(defun eglot (managed-major-modes project class contact language-ids - &optional _interactive) - "Start LSP server for PROJECT's buffers under MANAGED-MAJOR-MODES. - -This starts a Language Server Protocol (LSP) server suitable for -the buffers of PROJECT whose `major-mode' is among -MANAGED-MAJOR-MODES. CLASS is the class of the LSP server to -start and CONTACT specifies how to connect to the server. - -Interactively, the command attempts to guess MANAGED-MAJOR-MODES, -CLASS, CONTACT, and LANGUAGE-IDS from `eglot-server-programs', -according to the current buffer's `major-mode'. PROJECT is -guessed from `project-find-functions'. The search for active -projects in this context binds `eglot-lsp-context' (which see). - -If it can't guess, it prompts the user for the mode and the -server. With a single \\[universal-argument] prefix arg, it -always prompts for COMMAND. With two \\[universal-argument], it -also always prompts for MANAGED-MAJOR-MODE. - -The LSP server of CLASS is started (or contacted) via CONTACT. -If this operation is successful, current *and future* file -buffers of MANAGED-MAJOR-MODE inside PROJECT become \"managed\" -by the LSP server, meaning the information about their contents is -exchanged periodically with the server to provide enhanced -code-analysis via `xref-find-definitions', `flymake-mode', -`eldoc-mode', and `completion-at-point', among others. - -PROJECT is a project object as returned by `project-current'. - -CLASS is a subclass of `eglot-lsp-server'. - -CONTACT specifies how to contact the server. It is a -keyword-value plist used to initialize CLASS or a plain list as -described in `eglot-server-programs', which see. - -LANGUAGE-IDS is a list of language ID string to send to the -server for each element in MANAGED-MAJOR-MODES. - -INTERACTIVE is ignored and provided for backward compatibility." - (interactive - (let ((current-server (eglot-current-server))) - (unless (or (null current-server) - (y-or-n-p "\ -[eglot] Shut down current connection before attempting new one?")) - (user-error "[eglot] Connection attempt aborted by user.")) - (prog1 (append (eglot--guess-contact t) '(t)) - (when current-server (ignore-errors (eglot-shutdown current-server)))))) - (eglot--connect (eglot--ensure-list managed-major-modes) - project class contact - (eglot--ensure-list language-ids))) - -(defun eglot-reconnect (server &optional interactive) - "Reconnect to SERVER. -INTERACTIVE is t if called interactively." - (interactive (list (eglot--current-server-or-lose) t)) - (when (jsonrpc-running-p server) - (ignore-errors (eglot-shutdown server interactive nil 'preserve-buffers))) - (eglot--connect (eglot--major-modes server) - (eglot--project server) - (eieio-object-class-name server) - (eglot--saved-initargs server) - (eglot--language-ids server)) - (eglot--message "Reconnected!")) - -(defvar eglot--managed-mode) ; forward decl - -;;;###autoload -(defun eglot-ensure () - "Start Eglot session for current buffer if there isn't one. - -Only use this function (in major mode hooks, etc) if you are -confident that Eglot can be started safely and efficiently for -*every* buffer visited where these hooks may execute. - -Since it is difficult to establish this confidence fully, it's -often wise to use the interactive command `eglot' instead. This -command only needs to be invoked once per project, as all other -files of a given major mode visited within the same project will -automatically become managed with no further user intervention -needed." - (let ((buffer (current-buffer))) - (cl-labels - ((maybe-connect - () - (eglot--when-live-buffer buffer - (remove-hook 'post-command-hook #'maybe-connect t) - (unless eglot--managed-mode - (condition-case-unless-debug oops - (apply #'eglot--connect (eglot--guess-contact)) - (error (eglot--warn (error-message-string oops)))))))) - (when buffer-file-name - (add-hook 'post-command-hook #'maybe-connect 'append t))))) - -(defun eglot-events-buffer (server) - "Display events buffer for SERVER. -Use current server's or first available Eglot events buffer." - (interactive (list (eglot-current-server))) - (let ((buffer (if server (jsonrpc-events-buffer server) - (cl-find "\\*EGLOT.*events\\*" - (buffer-list) - :key #'buffer-name :test #'string-match)))) - (if buffer (display-buffer buffer) - (eglot--error "Can't find an Eglot events buffer!")))) - -(defun eglot-stderr-buffer (server) - "Display stderr buffer for SERVER." - (interactive (list (eglot--current-server-or-lose))) - (display-buffer (jsonrpc-stderr-buffer server))) - -(defun eglot-forget-pending-continuations (server) - "Forget pending requests for SERVER." - (interactive (list (eglot--current-server-or-lose))) - (jsonrpc-forget-pending-continuations server)) - -(defvar eglot-connect-hook - '(eglot-signal-didChangeConfiguration) - "Hook run after connecting in `eglot--connect'.") - -(defvar eglot-server-initialized-hook - '() - "Hook run after a `eglot-lsp-server' instance is created. - -That is before a connection was established. Use -`eglot-connect-hook' to hook into when a connection was -successfully established and the server on the other side has -received the initializing configuration. - -Each function is passed the server as an argument") - -(defun eglot--cmd (contact) - "Helper for `eglot--connect'." - (if (file-remote-p default-directory) - ;; TODO: this seems like a bug, although it’s everywhere. For - ;; some reason, for remote connections only, over a pipe, we - ;; need to turn off line buffering on the tty. - ;; - ;; Not only does this seem like there should be a better way, - ;; but it almost certainly doesn’t work on non-unix systems. - (list shell-file-name "-c" - (string-join (cons "stty raw > /dev/null;" - (mapcar #'shell-quote-argument contact)) - " ")) - contact)) - -(defvar-local eglot--cached-server nil - "A cached reference to the current Eglot server.") - -(defun eglot--connect (managed-modes project class contact language-ids) - "Connect to MANAGED-MODES, LANGUAGE-IDS, PROJECT, CLASS and CONTACT. -This docstring appeases checkdoc, that's all." - (let* ((default-directory (project-root project)) - (nickname (project-name project)) - (readable-name (format "EGLOT (%s/%s)" nickname managed-modes)) - server-info - (contact (if (functionp contact) (funcall contact) contact)) - (initargs - (cond ((keywordp (car contact)) contact) - ((integerp (cadr contact)) - (setq server-info (list (format "%s:%s" (car contact) - (cadr contact)))) - `(:process ,(lambda () - (apply #'open-network-stream - readable-name nil - (car contact) (cadr contact) - (cddr contact))))) - ((and (stringp (car contact)) - (cl-find-if (lambda (x) - (or (eq x :autoport) - (eq (car-safe x) :autoport))) - contact)) - (setq server-info (list "")) - `(:process ,(jsonrpc-autoport-bootstrap - readable-name - contact - :connect-args '(:noquery t)))) - ((stringp (car contact)) - (let* ((probe (cl-position-if #'keywordp contact)) - (more-initargs (and probe (cl-subseq contact probe))) - (contact (cl-subseq contact 0 probe))) - `(:process - ,(lambda () - (let ((default-directory default-directory) - ;; bug#61350: Tramp turns on a feature - ;; by default that can't (yet) handle - ;; very much data so we turn it off - ;; unconditionally -- just for our - ;; process. - (tramp-use-ssh-controlmaster-options 'suppress) - (tramp-ssh-controlmaster-options - "-o ControlMaster=no -o ControlPath=none")) - (make-process - :name readable-name - :command (setq server-info (eglot--cmd contact)) - :connection-type 'pipe - :coding 'utf-8-emacs-unix - :noquery t - :stderr (get-buffer-create - (format "*%s stderr*" readable-name)) - :file-handler t))) - ,@more-initargs))))) - (spread (lambda (fn) (lambda (server method params) - (let ((eglot--cached-server server)) - (apply fn server method (append params nil)))))) - (server - (apply - #'make-instance class - :name readable-name - :events-buffer-config eglot-events-buffer-config - :notification-dispatcher (funcall spread #'eglot-handle-notification) - :request-dispatcher (funcall spread #'eglot-handle-request) - :on-shutdown #'eglot--on-shutdown - initargs)) - (canceled nil) - (tag (make-symbol "connected-catch-tag"))) - (when server-info - (jsonrpc--debug server "Running language server: %s" - (string-join server-info " "))) - (setf (eglot--saved-initargs server) initargs) - (setf (eglot--project server) project) - (setf (eglot--project-nickname server) nickname) - (setf (eglot--languages server) - (cl-loop for m in managed-modes for l in language-ids - collect (cons m l))) - (run-hook-with-args 'eglot-server-initialized-hook server) - ;; Now start the handshake. To honor `eglot-sync-connect' - ;; maybe-sync-maybe-async semantics we use `jsonrpc-async-request' - ;; and mimic most of `jsonrpc-request'. - (unwind-protect - (condition-case _quit - (let ((retval - (catch tag - (jsonrpc-async-request - server - :initialize - (list :processId - (unless (or eglot-withhold-process-id - (file-remote-p default-directory) - (eq (jsonrpc-process-type server) - 'network)) - (emacs-pid)) - :clientInfo - `(:name "Eglot" ,@(when eglot--version - `(:version ,eglot--version))) - ;; Maybe turn trampy `/ssh:foo@bar:/path/to/baz.py' - ;; into `/path/to/baz.py', so LSP groks it. - :rootPath (file-local-name - (expand-file-name default-directory)) - :rootUri (eglot-path-to-uri default-directory) - :initializationOptions (eglot-initialization-options - server) - :capabilities (eglot-client-capabilities server) - :workspaceFolders (eglot-workspace-folders server)) - :success-fn - (eglot--lambda ((InitializeResult) capabilities serverInfo) - (unless canceled - (push server - (gethash project eglot--servers-by-project)) - (setf (eglot--capabilities server) capabilities) - (setf (eglot--server-info server) serverInfo) - (jsonrpc-notify server :initialized eglot--{}) - (dolist (buffer (buffer-list)) - (with-current-buffer buffer - ;; No need to pass SERVER as an argument: it has - ;; been registered in `eglot--servers-by-project', - ;; so that it can be found (and cached) from - ;; `eglot--maybe-activate-editing-mode' in any - ;; managed buffer. - (eglot--maybe-activate-editing-mode))) - (setf (eglot--inhibit-autoreconnect server) - (cond - ((booleanp eglot-autoreconnect) - (not eglot-autoreconnect)) - ((cl-plusp eglot-autoreconnect) - (run-with-timer - eglot-autoreconnect nil - (lambda () - (setf (eglot--inhibit-autoreconnect server) - (null eglot-autoreconnect))))))) - (run-hook-with-args 'eglot-connect-hook server) - (eglot--message - "Connected! Server `%s' now managing `%s' buffers \ -in project `%s'." - (or (plist-get serverInfo :name) - (jsonrpc-name server)) - managed-modes - (eglot-project-nickname server)) - (when tag (throw tag t)))) - :timeout eglot-connect-timeout - :error-fn (eglot--lambda ((ResponseError) code message) - (unless canceled - (jsonrpc-shutdown server) - (let ((msg (format "%s: %s" code message))) - (if tag (throw tag `(error . ,msg)) - (eglot--error msg))))) - :timeout-fn (lambda () - (unless canceled - (jsonrpc-shutdown server) - (let ((msg (format "Timed out after %s seconds" - eglot-connect-timeout))) - (if tag (throw tag `(error . ,msg)) - (eglot--error msg)))))) - (cond ((numberp eglot-sync-connect) - (accept-process-output nil eglot-sync-connect)) - (eglot-sync-connect - (while t (accept-process-output - nil eglot-connect-timeout))))))) - (pcase retval - (`(error . ,msg) (eglot--error msg)) - (`nil (eglot--message "Waiting in background for server `%s'" - (jsonrpc-name server)) - nil) - (_ server))) - (quit (jsonrpc-shutdown server) (setq canceled 'quit))) - (setq tag nil)))) - - -;;; Helpers (move these to API?) -;;; -(defun eglot--error (format &rest args) - "Error out with FORMAT with ARGS." - (error "[eglot] %s" (apply #'format format args))) - -(defun eglot--message (format &rest args) - "Message out with FORMAT with ARGS." - (message "[eglot] %s" (apply #'format format args))) - -(defun eglot--warn (format &rest args) - "Warning message with FORMAT and ARGS." - (apply #'eglot--message (concat "(warning) " format) args) - (let ((warning-minimum-level :error)) - (display-warning 'eglot (apply #'format format args) :warning))) - -(defalias 'eglot--bol - (if (fboundp 'pos-bol) #'pos-bol - (lambda (&optional n) (let ((inhibit-field-text-motion t)) - (line-beginning-position n)))) - "Return position of first character in current line.") - -(cl-defun eglot--request (server method params &key - immediate - timeout cancel-on-input - cancel-on-input-retval) - "Like `jsonrpc-request', but for Eglot LSP requests. -Unless IMMEDIATE, send pending changes before making request." - (unless immediate (eglot--signal-textDocument/didChange)) - (jsonrpc-request server method params - :timeout timeout - :cancel-on-input cancel-on-input - :cancel-on-input-retval cancel-on-input-retval)) - - -;;; Encoding fever -;;; -(defvar eglot-current-linepos-function #'eglot-utf-16-linepos - "Function calculating position relative to line beginning. - -It is a function of no arguments considering the text from line -beginning up to current point. The return value is the number of -UTF code units needed to encode that text from the LSP server's -perspective. This may be a number of octets, 16-bit words or -Unicode code points, depending on whether the LSP server's -`positionEncoding' capability is UTF-8, UTF-16 or UTF-32, -respectively. Position of point should remain unaltered if that -return value is fed through the corresponding inverse function -`eglot-move-to-linepos-function' (which see).") - -(defun eglot-utf-8-linepos () - "Calculate number of UTF-8 bytes from line beginning." - (length (encode-coding-region (eglot--bol) (point) 'utf-8-unix t))) - -(defun eglot-utf-16-linepos (&optional lbp) - "Calculate number of UTF-16 code units from position given by LBP. -LBP defaults to `eglot--bol'." - (/ (- (length (encode-coding-region (or lbp (eglot--bol)) - ;; Fix github#860 - (min (point) (point-max)) 'utf-16 t)) - 2) - 2)) - -(defun eglot-utf-32-linepos () - "Calculate number of Unicode codepoints from line beginning." - (- (point) (eglot--bol))) - -(defun eglot--pos-to-lsp-position (&optional pos) - "Convert point POS to LSP position." - (eglot--widening - ;; LSP line is zero-origin; emacs is one-origin. - (list :line (1- (line-number-at-pos pos t)) - :character (progn (when pos (goto-char pos)) - (funcall eglot-current-linepos-function))))) - -(defvar eglot-move-to-linepos-function #'eglot-move-to-utf-16-linepos - "Function to move to a position within a line reported by the LSP server. - -Per the LSP spec, character offsets in LSP Position objects count -UTF-16 code units, not actual code points. So when LSP says -position 3 of a line containing just \"aXbc\", where X is a funny -looking character in the UTF-16 \"supplementary plane\", it -actually means `b', not `c'. The default value -`eglot-move-to-utf-16-linepos' accounts for this. - -This variable can also be set to `eglot-move-to-utf-8-linepos' or -`eglot-move-to-utf-32-linepos' for servers not closely following -the spec. Also, since LSP 3.17 server and client may agree on an -encoding and Eglot will set this variable automatically.") - -(defun eglot-move-to-utf-8-linepos (n) - "Move to line's Nth byte as computed by LSP's UTF-8 criterion." - (let* ((bol (eglot--bol)) - (goal-byte (+ (position-bytes bol) n)) - (eol (line-end-position))) - (goto-char bol) - (while (and (< (position-bytes (point)) goal-byte) (< (point) eol)) - ;; raw bytes take 2 bytes in the buffer - (when (>= (char-after) #x3fff80) (setq goal-byte (1+ goal-byte))) - (forward-char 1)))) - -(defun eglot-move-to-utf-16-linepos (n) - "Move to line's Nth code unit as computed by LSP's UTF-16 criterion." - (let* ((bol (eglot--bol)) - (goal-char (+ bol n)) - (eol (line-end-position))) - (goto-char bol) - (while (and (< (point) goal-char) (< (point) eol)) - ;; code points in the "supplementary place" use two code units - (when (<= #x010000 (char-after) #x10ffff) (setq goal-char (1- goal-char))) - (forward-char 1)))) - -(defun eglot-move-to-utf-32-linepos (n) - "Move to line's Nth codepoint as computed by LSP's UTF-32 criterion." - ;; We cannot use `move-to-column' here, because it moves to *visual* - ;; columns, which can be different from LSP characters in case of - ;; `whitespace-mode', `prettify-symbols-mode', etc. (github#296, - ;; github#297) - (goto-char (min (+ (eglot--bol) n) (line-end-position)))) - -(defun eglot--lsp-position-to-point (pos-plist &optional marker) - "Convert LSP position POS-PLIST to Emacs point. -If optional MARKER, return a marker instead" - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (forward-line (min most-positive-fixnum - (plist-get pos-plist :line))) - (unless (eobp) ;; if line was excessive leave point at eob - (let ((col (plist-get pos-plist :character))) - (unless (wholenump col) - (eglot--warn - "Caution: LSP server sent invalid character position %s. Using 0 instead." - col) - (setq col 0)) - (funcall eglot-move-to-linepos-function col))) - (if marker (copy-marker (point-marker)) (point))))) - - -;;; More helpers -(defconst eglot--uri-path-allowed-chars - (let ((vec (copy-sequence url-path-allowed-chars))) - (aset vec ?: nil) ;; see github#639 - vec) - "Like `url-path-allowed-chars' but more restrictive.") - -(defun eglot--snippet-expansion-fn () - "Compute a function to expand snippets. -Doubles as an indicator of snippet support." - (and (fboundp 'yas-minor-mode) - (lambda (&rest args) - (with-no-warnings - (unless (bound-and-true-p yas-minor-mode) (yas-minor-mode 1)) - (apply #'yas-expand-snippet args))))) - -(defun eglot--format-markup (markup) - "Format MARKUP according to LSP's spec." - (pcase-let ((`(,string ,mode) - (if (stringp markup) (list markup 'gfm-view-mode) - (list (plist-get markup :value) - (pcase (plist-get markup :kind) - ("markdown" 'gfm-view-mode) - ("plaintext" 'text-mode) - (_ major-mode)))))) - (with-temp-buffer - (setq-local markdown-fontify-code-blocks-natively t) - (insert string) - (let ((inhibit-message t) - (message-log-max nil) - match) - (ignore-errors (delay-mode-hooks (funcall mode))) - (font-lock-ensure) - (goto-char (point-min)) - (let ((inhibit-read-only t)) - (when (fboundp 'text-property-search-forward) ;; FIXME: use compat - (while (setq match (text-property-search-forward 'invisible)) - (delete-region (prop-match-beginning match) - (prop-match-end match))))) - (string-trim (buffer-string)))))) - -(defun eglot--read-server (prompt &optional dont-if-just-the-one) - "Read a running Eglot server from minibuffer using PROMPT. -If DONT-IF-JUST-THE-ONE and there's only one server, don't prompt -and just return it. PROMPT shouldn't end with a question mark." - (let ((servers (cl-loop for servers - being hash-values of eglot--servers-by-project - append servers)) - (name (lambda (srv) - (format "%s %s" (eglot-project-nickname srv) - (eglot--major-modes srv))))) - (cond ((null servers) - (eglot--error "No servers!")) - ((or (cdr servers) (not dont-if-just-the-one)) - (let* ((default (when-let ((current (eglot-current-server))) - (funcall name current))) - (read (completing-read - (if default - (format "%s (default %s)? " prompt default) - (concat prompt "? ")) - (mapcar name servers) - nil t - nil nil - default))) - (cl-find read servers :key name :test #'equal))) - (t (car servers))))) - -(defun eglot--trampish-p (server) - "Tell if SERVER's project root is `file-remote-p'." - (file-remote-p (project-root (eglot--project server)))) - -(defun eglot--plist-keys (plist) "Get keys of a plist." - (cl-loop for (k _v) on plist by #'cddr collect k)) - -(defalias 'eglot--ensure-list - (if (fboundp 'ensure-list) #'ensure-list - (lambda (x) (if (listp x) x (list x))))) - - -;;; Minor modes -;;; -(defvar eglot-mode-map - (let ((map (make-sparse-keymap))) - (define-key map [remap display-local-help] #'eldoc-doc-buffer) - map)) - -(defvar-local eglot--current-flymake-report-fn nil - "Current flymake report function for this buffer.") - -(defvar-local eglot--saved-bindings nil - "Bindings saved by `eglot--setq-saving'.") - -(defvar eglot-stay-out-of '() - "List of Emacs things that Eglot should try to stay of. -Each element is a string, a symbol, or a regexp which is matched -against a variable's name. Examples include the string -\"company\" or the symbol `xref'. - -Before Eglot starts \"managing\" a particular buffer, it -opinionatedly sets some peripheral Emacs facilities, such as -Flymake, Xref and Company. These overriding settings help ensure -consistent Eglot behavior and only stay in place until -\"managing\" stops (usually via `eglot-shutdown'), whereupon the -previous settings are restored. - -However, if you wish for Eglot to stay out of a particular Emacs -facility that you'd like to keep control of add an element to -this list and Eglot will refrain from setting it. - -For example, to keep your Company customization, add the symbol -`company' to this variable.") - -(defun eglot--stay-out-of-p (symbol) - "Tell if Eglot should stay out of SYMBOL." - (cl-find (symbol-name symbol) eglot-stay-out-of - :test (lambda (s thing) - (let ((re (if (symbolp thing) (symbol-name thing) thing))) - (string-match re s))))) - -(defmacro eglot--setq-saving (symbol binding) - `(unless (or (not (boundp ',symbol)) (eglot--stay-out-of-p ',symbol)) - (push (cons ',symbol (symbol-value ',symbol)) eglot--saved-bindings) - (setq-local ,symbol ,binding))) - -(defun eglot-managed-p () - "Tell if current buffer is managed by Eglot." - eglot--managed-mode) - -(defvar eglot-managed-mode-hook nil - "A hook run by Eglot after it started/stopped managing a buffer. -Use `eglot-managed-p' to determine if current buffer is managed.") - -(define-minor-mode eglot--managed-mode - "Mode for source buffers managed by some Eglot project." - :init-value nil :lighter nil :keymap eglot-mode-map - (cond - (eglot--managed-mode - (pcase (plist-get (eglot--capabilities (eglot-current-server)) - :positionEncoding) - ("utf-32" - (eglot--setq-saving eglot-current-linepos-function #'eglot-utf-32-linepos) - (eglot--setq-saving eglot-move-to-linepos-function #'eglot-move-to-utf-32-linepos)) - ("utf-8" - (eglot--setq-saving eglot-current-linepos-function #'eglot-utf-8-linepos) - (eglot--setq-saving eglot-move-to-linepos-function #'eglot-move-to-utf-8-linepos))) - (add-hook 'after-change-functions #'eglot--after-change nil t) - (add-hook 'before-change-functions #'eglot--before-change nil t) - (add-hook 'kill-buffer-hook #'eglot--managed-mode-off nil t) - ;; Prepend "didClose" to the hook after the "nonoff", so it will run first - (add-hook 'kill-buffer-hook #'eglot--signal-textDocument/didClose nil t) - (add-hook 'before-revert-hook #'eglot--signal-textDocument/didClose nil t) - (add-hook 'after-revert-hook #'eglot--after-revert-hook nil t) - (add-hook 'before-save-hook #'eglot--signal-textDocument/willSave nil t) - (add-hook 'after-save-hook #'eglot--signal-textDocument/didSave nil t) - (unless (eglot--stay-out-of-p 'xref) - (add-hook 'xref-backend-functions #'eglot-xref-backend nil t)) - (add-hook 'completion-at-point-functions #'eglot-completion-at-point nil t) - (add-hook 'completion-in-region-mode-hook #'eglot--capf-session-flush nil t) - (add-hook 'company-after-completion-hook #'eglot--capf-session-flush nil t) - (add-hook 'change-major-mode-hook #'eglot--managed-mode-off nil t) - (add-hook 'post-self-insert-hook #'eglot--post-self-insert-hook nil t) - (add-hook 'pre-command-hook #'eglot--pre-command-hook nil t) - (eglot--setq-saving xref-prompt-for-identifier nil) - (eglot--setq-saving flymake-diagnostic-functions '(eglot-flymake-backend)) - (eglot--setq-saving company-backends '(company-capf)) - (eglot--setq-saving company-tooltip-align-annotations t) - (eglot--setq-saving eldoc-documentation-strategy - #'eldoc-documentation-compose) - (unless (eglot--stay-out-of-p 'imenu) - (add-function :before-until (local 'imenu-create-index-function) - #'eglot-imenu)) - (unless (eglot--stay-out-of-p 'flymake) (flymake-mode 1)) - (unless (eglot--stay-out-of-p 'eldoc) - (add-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function - nil t) - (add-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function - nil t) - (eldoc-mode 1)) - (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server)))) - (t - (remove-hook 'after-change-functions #'eglot--after-change t) - (remove-hook 'before-change-functions #'eglot--before-change t) - (remove-hook 'kill-buffer-hook #'eglot--managed-mode-off t) - (remove-hook 'kill-buffer-hook #'eglot--signal-textDocument/didClose t) - (remove-hook 'before-revert-hook #'eglot--signal-textDocument/didClose t) - (remove-hook 'after-revert-hook #'eglot--after-revert-hook t) - (remove-hook 'before-save-hook #'eglot--signal-textDocument/willSave t) - (remove-hook 'after-save-hook #'eglot--signal-textDocument/didSave t) - (remove-hook 'xref-backend-functions #'eglot-xref-backend t) - (remove-hook 'completion-at-point-functions #'eglot-completion-at-point t) - (remove-hook 'completion-in-region-mode-hook #'eglot--capf-session-flush t) - (remove-hook 'company-after-completion-hook #'eglot--capf-session-flush t) - (remove-hook 'change-major-mode-hook #'eglot--managed-mode-off t) - (remove-hook 'post-self-insert-hook #'eglot--post-self-insert-hook t) - (remove-hook 'pre-command-hook #'eglot--pre-command-hook t) - (remove-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function t) - (remove-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function t) - (cl-loop for (var . saved-binding) in eglot--saved-bindings - do (set (make-local-variable var) saved-binding)) - (remove-function (local 'imenu-create-index-function) #'eglot-imenu) - (when eglot--current-flymake-report-fn - (eglot--report-to-flymake nil) - (setq eglot--current-flymake-report-fn nil)) - (let ((server eglot--cached-server)) - (setq eglot--cached-server nil) - (when server - (setf (eglot--managed-buffers server) - (delq (current-buffer) (eglot--managed-buffers server))) - (when (and eglot-autoshutdown - (null (eglot--managed-buffers server))) - (eglot-shutdown server))))))) - -(defun eglot--managed-mode-off () - "Turn off `eglot--managed-mode' unconditionally." - (remove-overlays nil nil 'eglot--overlay t) - (eglot-inlay-hints-mode -1) - (eglot--managed-mode -1)) - -(defun eglot-current-server () - "Return logical Eglot server for current buffer, nil if none." - (setq eglot--cached-server - (or eglot--cached-server - (and (not (eq major-mode 'fundamental-mode)) ; gh#1330 - (or - (cl-find-if #'eglot--languageId - (gethash (eglot--current-project) - eglot--servers-by-project)) - (and eglot-extend-to-xref - buffer-file-name - (gethash (expand-file-name buffer-file-name) - eglot--servers-by-xrefed-file))))))) - -(defun eglot--current-server-or-lose () - "Return current logical Eglot server connection or error." - (or (eglot-current-server) - (jsonrpc-error "No current JSON-RPC connection"))) - -(defvar-local eglot--diagnostics nil - "Flymake diagnostics for this buffer.") - -(defvar revert-buffer-preserve-modes) -(defun eglot--after-revert-hook () - "Eglot's `after-revert-hook'." - (when revert-buffer-preserve-modes (eglot--signal-textDocument/didOpen))) - -(defun eglot--maybe-activate-editing-mode () - "Maybe activate `eglot--managed-mode'. - -If it is activated, also signal textDocument/didOpen." - (unless eglot--managed-mode - ;; Called when `revert-buffer-in-progress-p' is t but - ;; `revert-buffer-preserve-modes' is nil. - (when (and buffer-file-name (eglot-current-server)) - (setq eglot--diagnostics nil) - (eglot--managed-mode) - (eglot--signal-textDocument/didOpen) - ;; Run user hook after 'textDocument/didOpen' so server knows - ;; about the buffer. - (eglot-inlay-hints-mode 1) - (run-hooks 'eglot-managed-mode-hook)))) - -(add-hook 'after-change-major-mode-hook #'eglot--maybe-activate-editing-mode) - -(defun eglot-clear-status (server) - "Clear the last JSONRPC error for SERVER." - (interactive (list (eglot--current-server-or-lose))) - (setf (jsonrpc-last-error server) nil)) - - -;;; Mode-line, menu and other sugar -;;; -(defvar eglot--mode-line-format `(:eval (eglot--mode-line-format))) - -(put 'eglot--mode-line-format 'risky-local-variable t) - -(defun eglot--mouse-call (what &optional update-mode-line) - "Make an interactive lambda for calling WHAT with the mouse." - (lambda (event) - (interactive "e") - (let ((start (event-start event))) (with-selected-window (posn-window start) - (save-excursion - (goto-char (or (posn-point start) - (point))) - (call-interactively what) - (when update-mode-line - (force-mode-line-update t))))))) - -(defun eglot-manual () "Read Eglot's manual." - (declare (obsolete info "1.10")) - (interactive) (info "(eglot)")) - -;;;###autoload -(defun eglot-upgrade-eglot (&rest _) "Update Eglot to latest version." - (interactive) - (with-no-warnings - (require 'package) - (unless package-archive-contents (package-refresh-contents)) - (when-let ((existing (cadr (assoc 'eglot package-alist)))) - (package-delete existing t)) - (package-install (cadr (assoc 'eglot package-archive-contents))))) - -(easy-menu-define eglot-menu nil "Eglot" - `("Eglot" - ;; Commands for getting information and customization. - ["Customize Eglot" (lambda () (interactive) (customize-group "eglot"))] - "--" - ;; xref like commands. - ["Find definitions" xref-find-definitions - :help "Find definitions of identifier at point" - :active (eglot-server-capable :definitionProvider)] - ["Find references" xref-find-references - :help "Find references to identifier at point" - :active (eglot-server-capable :referencesProvider)] - ["Find symbols in workspace (apropos)" xref-find-apropos - :help "Find symbols matching a query" - :active (eglot-server-capable :workspaceSymbolProvider)] - ["Find declaration" eglot-find-declaration - :help "Find declaration for identifier at point" - :active (eglot-server-capable :declarationProvider)] - ["Find implementation" eglot-find-implementation - :help "Find implementation for identifier at point" - :active (eglot-server-capable :implementationProvider)] - ["Find type definition" eglot-find-typeDefinition - :help "Find type definition for identifier at point" - :active (eglot-server-capable :typeDefinitionProvider)] - "--" - ;; LSP-related commands (mostly Eglot's own commands). - ["Rename symbol" eglot-rename - :active (eglot-server-capable :renameProvider)] - ["Format buffer" eglot-format-buffer - :active (eglot-server-capable :documentFormattingProvider)] - ["Format active region" eglot-format - :active (and (region-active-p) - (eglot-server-capable :documentRangeFormattingProvider))] - ["Show Flymake diagnostics for buffer" flymake-show-buffer-diagnostics] - ["Show Flymake diagnostics for project" flymake-show-project-diagnostics] - ["Show Eldoc documentation at point" eldoc-doc-buffer] - "--" - ["All possible code actions" eglot-code-actions - :active (eglot-server-capable :codeActionProvider)] - ["Organize imports" eglot-code-action-organize-imports - :visible (eglot-server-capable :codeActionProvider)] - ["Extract" eglot-code-action-extract - :visible (eglot-server-capable :codeActionProvider)] - ["Inline" eglot-code-action-inline - :visible (eglot-server-capable :codeActionProvider)] - ["Rewrite" eglot-code-action-rewrite - :visible (eglot-server-capable :codeActionProvider)] - ["Quickfix" eglot-code-action-quickfix - :visible (eglot-server-capable :codeActionProvider)])) - -(easy-menu-define eglot-server-menu nil "Monitor server communication" - '("Debugging the server communication" - ["Reconnect to server" eglot-reconnect] - ["Quit server" eglot-shutdown] - "--" - ["LSP events buffer" eglot-events-buffer] - ["Server stderr buffer" eglot-stderr-buffer] - ["Customize event buffer size" - (lambda () - (interactive) - (customize-variable 'eglot-events-buffer-size))])) - -(defun eglot--mode-line-props (thing face defs &optional prepend) - "Helper for function `eglot--mode-line-format'. -Uses THING, FACE, DEFS and PREPEND." - (cl-loop with map = (make-sparse-keymap) - for (elem . rest) on defs - for (key def help) = elem - do (define-key map `[mode-line ,key] (eglot--mouse-call def t)) - concat (format "%s: %s" key help) into blurb - when rest concat "\n" into blurb - finally (return `(:propertize ,thing - face ,face - keymap ,map help-echo ,(concat prepend blurb) - mouse-face mode-line-highlight)))) - -(defun eglot--mode-line-format () - "Compose Eglot's mode-line." - (let* ((server (eglot-current-server)) - (nick (and server (eglot-project-nickname server))) - (pending (and server (jsonrpc-continuation-count server))) - (last-error (and server (jsonrpc-last-error server)))) - (append - `(,(propertize - eglot-menu-string - 'face 'eglot-mode-line - 'mouse-face 'mode-line-highlight - 'help-echo "Eglot: Emacs LSP client\nmouse-1: Display minor mode menu" - 'keymap (let ((map (make-sparse-keymap))) - (define-key map [mode-line down-mouse-1] eglot-menu) - map))) - (when nick - `(":" - ,(propertize - nick - 'face 'eglot-mode-line - 'mouse-face 'mode-line-highlight - 'help-echo (format "Project '%s'\nmouse-1: LSP server control menu" nick) - 'keymap (let ((map (make-sparse-keymap))) - (define-key map [mode-line down-mouse-1] eglot-server-menu) - map)) - ,@(when last-error - `("/" ,(eglot--mode-line-props - "error" 'compilation-mode-line-fail - '((mouse-3 eglot-clear-status "Clear this status")) - (format "An error occurred: %s\n" (plist-get last-error - :message))))) - ,@(when (cl-plusp pending) - `("/" ,(eglot--mode-line-props - (format "%d" pending) 'warning - '((mouse-3 eglot-forget-pending-continuations - "Forget pending continuations")) - "Number of outgoing, \ -still unanswered LSP requests to the server\n"))) - ,@(cl-loop for pr hash-values of (eglot--progress-reporters server) - when (eq (car pr) 'eglot--mode-line-reporter) - append `("/" ,(eglot--mode-line-props - (format "%s%%%%" (or (nth 4 pr) "?")) - 'eglot-mode-line - nil - (format "(%s) %s %s" (nth 1 pr) - (nth 2 pr) (nth 3 pr)))))))))) - -(add-to-list 'mode-line-misc-info - `(eglot--managed-mode (" [" eglot--mode-line-format "] "))) - - -;;; Flymake customization -;;; -(put 'eglot-note 'flymake-category 'flymake-note) -(put 'eglot-warning 'flymake-category 'flymake-warning) -(put 'eglot-error 'flymake-category 'flymake-error) - -(defalias 'eglot--make-diag #'flymake-make-diagnostic) -(defalias 'eglot--diag-data #'flymake-diagnostic-data) - -(defvar eglot-diagnostics-map - (let ((map (make-sparse-keymap))) - (define-key map [mouse-2] #'eglot-code-actions-at-mouse) - map) - "Keymap active in Eglot-backed Flymake diagnostic overlays.") - -(cl-loop for i from 1 - for type in '(eglot-note eglot-warning eglot-error) - do (put type 'flymake-overlay-control - `((mouse-face . highlight) - (priority . ,(+ 50 i)) - (keymap . ,eglot-diagnostics-map)))) - - -;;; Protocol implementation (Requests, notifications, etc) -;;; -(cl-defmethod eglot-handle-notification - (_server method &key &allow-other-keys) - "Handle unknown notification." - (unless (or (string-prefix-p "$" (format "%s" method)) - (not (memq 'disallow-unknown-methods eglot-strict-mode))) - (eglot--warn "Server sent unknown notification method `%s'" method))) - -(cl-defmethod eglot-handle-request - (_server method &key &allow-other-keys) - "Handle unknown request." - (when (memq 'disallow-unknown-methods eglot-strict-mode) - (jsonrpc-error "Unknown request method `%s'" method))) - -(cl-defmethod eglot-handle-notification - (_server (_method (eql window/showMessage)) &key type message) - "Handle notification window/showMessage." - (eglot--message (propertize "Server reports (type=%s): %s" - 'face (if (<= type 1) 'error)) - type message)) - -(cl-defmethod eglot-handle-request - (_server (_method (eql window/showMessageRequest)) - &key type message actions &allow-other-keys) - "Handle server request window/showMessageRequest." - (let* ((actions (append actions nil)) ;; gh#627 - (label (completing-read - (concat - (format (propertize "[eglot] Server reports (type=%s): %s" - 'face (if (or (not type) (<= type 1)) 'error)) - type message) - "\nChoose an option: ") - (or (mapcar (lambda (obj) (plist-get obj :title)) actions) - '("OK")) - nil t (plist-get (elt actions 0) :title)))) - (if label `(:title ,label) :null))) - -(cl-defmethod eglot-handle-notification - (_server (_method (eql window/logMessage)) &key _type _message) - "Handle notification window/logMessage.") ;; noop, use events buffer - -(cl-defmethod eglot-handle-notification - (_server (_method (eql telemetry/event)) &rest _any) - "Handle notification telemetry/event.") ;; noop, use events buffer - -(defalias 'eglot--reporter-update - (if (> emacs-major-version 26) #'progress-reporter-update - (lambda (a b &optional _c) (progress-reporter-update a b)))) - -(cl-defmethod eglot-handle-notification - (server (_method (eql $/progress)) &key token value) - "Handle $/progress notification identified by TOKEN from SERVER." - (when eglot-report-progress - (cl-flet ((fmt (&rest args) (mapconcat #'identity args " ")) - (mkpr (title) - (if (eq eglot-report-progress 'messages) - (make-progress-reporter - (format "[eglot] %s %s: %s" - (eglot-project-nickname server) token title)) - (list 'eglot--mode-line-reporter token title))) - (upd (pcnt msg &optional - (pr (gethash token (eglot--progress-reporters server)))) - (cond - ((eq (car pr) 'eglot--mode-line-reporter) - (setcdr (cddr pr) (list msg pcnt)) - (force-mode-line-update t)) - (pr (eglot--reporter-update pr pcnt msg))))) - (eglot--dbind ((WorkDoneProgress) kind title percentage message) value - (pcase kind - ("begin" - (upd percentage (fmt title message) - (puthash token (mkpr title) - (eglot--progress-reporters server)))) - ("report" (upd percentage message)) - ("end" (upd (or percentage 100) message) - (run-at-time 2 nil - (lambda () - (remhash token (eglot--progress-reporters server)))))))))) - -(cl-defmethod eglot-handle-notification - (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics - &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode' - "Handle notification publishDiagnostics." - (cl-flet ((eglot--diag-type (sev) - (cond ((null sev) 'eglot-error) - ((<= sev 1) 'eglot-error) - ((= sev 2) 'eglot-warning) - (t 'eglot-note))) - (mess (source code message) - (concat source (and code (format " [%s]" code)) ": " message))) - (if-let* ((path (expand-file-name (eglot-uri-to-path uri))) - (buffer (find-buffer-visiting path))) - (with-current-buffer buffer - (cl-loop - initially - (setq flymake-list-only-diagnostics - (assoc-delete-all path flymake-list-only-diagnostics)) - for diag-spec across diagnostics - collect (eglot--dbind ((Diagnostic) range code message severity source tags) - diag-spec - (setq message (mess source code message)) - (pcase-let - ((`(,beg . ,end) (eglot-range-region range))) - ;; Fallback to `flymake-diag-region' if server - ;; botched the range - (when (= beg end) - (if-let* ((st (plist-get range :start)) - (diag-region - (flymake-diag-region - (current-buffer) (1+ (plist-get st :line)) - (plist-get st :character)))) - (setq beg (car diag-region) end (cdr diag-region)) - (eglot--widening - (goto-char (point-min)) - (setq beg - (eglot--bol - (1+ (plist-get (plist-get range :start) :line)))) - (setq end - (line-end-position - (1+ (plist-get (plist-get range :end) :line))))))) - (eglot--make-diag - (current-buffer) beg end - (eglot--diag-type severity) - message `((eglot-lsp-diag . ,diag-spec)) - (when-let ((faces - (cl-loop for tag across tags - when (alist-get tag eglot--tag-faces) - collect it))) - `((face . ,faces)))))) - into diags - finally (cond ((and - ;; only add to current report if Flymake - ;; starts on idle-timer (github#958) - (not (null flymake-no-changes-timeout)) - eglot--current-flymake-report-fn) - (eglot--report-to-flymake diags)) - (t - (setq eglot--diagnostics diags))))) - (cl-loop - for diag-spec across diagnostics - collect (eglot--dbind ((Diagnostic) code range message severity source) diag-spec - (setq message (mess source code message)) - (let* ((start (plist-get range :start)) - (line (1+ (plist-get start :line))) - (char (1+ (plist-get start :character)))) - (eglot--make-diag - path (cons line char) nil (eglot--diag-type severity) message))) - into diags - finally - (setq flymake-list-only-diagnostics - (assoc-delete-all path flymake-list-only-diagnostics)) - (push (cons path diags) flymake-list-only-diagnostics))))) - -(cl-defun eglot--register-unregister (server things how) - "Helper for `registerCapability'. -THINGS are either registrations or unregisterations (sic)." - (cl-loop - for thing in (cl-coerce things 'list) - do (eglot--dbind ((Registration) id method registerOptions) thing - (apply (cl-ecase how - (register 'eglot-register-capability) - (unregister 'eglot-unregister-capability)) - server (intern method) id registerOptions)))) - -(cl-defmethod eglot-handle-request - (server (_method (eql client/registerCapability)) &key registrations) - "Handle server request client/registerCapability." - (eglot--register-unregister server registrations 'register)) - -(cl-defmethod eglot-handle-request - (server (_method (eql client/unregisterCapability)) - &key unregisterations) ;; XXX: "unregisterations" (sic) - "Handle server request client/unregisterCapability." - (eglot--register-unregister server unregisterations 'unregister)) - -(cl-defmethod eglot-handle-request - (_server (_method (eql workspace/applyEdit)) &key _label edit) - "Handle server request workspace/applyEdit." - (eglot--apply-workspace-edit edit last-command) - `(:applied t)) - -(cl-defmethod eglot-handle-request - (server (_method (eql workspace/workspaceFolders))) - "Handle server request workspace/workspaceFolders." - (eglot-workspace-folders server)) - -(cl-defmethod eglot-handle-request - (_server (_method (eql window/showDocument)) &key - uri external takeFocus selection) - "Handle request window/showDocument." - (let ((success t) - (filename)) - (cond - ((eq external t) (browse-url uri)) - ((file-readable-p (setq filename (eglot-uri-to-path uri))) - ;; Use run-with-timer to avoid nested client requests like the - ;; "synchronous imenu" floated in bug#62116 presumably caused by - ;; which-func-mode. - (run-with-timer - 0 nil - (lambda () - (with-current-buffer (find-file-noselect filename) - (cond (takeFocus - (pop-to-buffer (current-buffer)) - (select-frame-set-input-focus (selected-frame))) - ((display-buffer (current-buffer)))) - (when selection - (pcase-let ((`(,beg . ,end) (eglot-range-region selection))) - ;; FIXME: it is very naughty to use someone else's `--' - ;; function, but `xref--goto-char' happens to have - ;; exactly the semantics we want vis-a-vis widening. - (xref--goto-char beg) - (pulse-momentary-highlight-region beg end 'highlight))))))) - (t (setq success :json-false))) - `(:success ,success))) - -(defun eglot--TextDocumentIdentifier () - "Compute TextDocumentIdentifier object for current buffer." - `(:uri ,(eglot-path-to-uri (or buffer-file-name - (ignore-errors - (buffer-file-name - (buffer-base-buffer))))))) - -(defvar-local eglot--versioned-identifier 0) - -(defun eglot--VersionedTextDocumentIdentifier () - "Compute VersionedTextDocumentIdentifier object for current buffer." - (append (eglot--TextDocumentIdentifier) - `(:version ,eglot--versioned-identifier))) - -(cl-defun eglot--languageId (&optional (server (eglot--current-server-or-lose))) - "Compute LSP \\='languageId\\=' string for current buffer. -Doubles as an predicate telling if SERVER can manage current -buffer." - (cl-loop for (mode . languageid) in - (eglot--languages server) - when (provided-mode-derived-p major-mode mode) - return languageid)) - -(defun eglot--TextDocumentItem () - "Compute TextDocumentItem object for current buffer." - (append - (eglot--VersionedTextDocumentIdentifier) - (list :languageId (eglot--languageId) - :text - (eglot--widening - (buffer-substring-no-properties (point-min) (point-max)))))) - -(defun eglot--TextDocumentPositionParams () - "Compute TextDocumentPositionParams." - (list :textDocument (eglot--TextDocumentIdentifier) - :position (eglot--pos-to-lsp-position))) - -(defvar-local eglot--last-inserted-char nil - "If non-nil, value of the last inserted character in buffer.") - -(defun eglot--post-self-insert-hook () - "Set `eglot--last-inserted-char', maybe call on-type-formatting." - (setq eglot--last-inserted-char last-command-event) - (let ((ot-provider (eglot-server-capable :documentOnTypeFormattingProvider))) - (when (and ot-provider - (ignore-errors ; github#906, some LS's send empty strings - (or (eq eglot--last-inserted-char - (seq-first (plist-get ot-provider :firstTriggerCharacter))) - (cl-find eglot--last-inserted-char - (plist-get ot-provider :moreTriggerCharacter) - :key #'seq-first)))) - (eglot-format (point) nil eglot--last-inserted-char)))) - -(defvar eglot--workspace-symbols-cache (make-hash-table :test #'equal) - "Cache of `workspace/Symbol' results used by `xref-find-definitions'.") - -(defun eglot--pre-command-hook () - "Reset some temporary variables." - (clrhash eglot--workspace-symbols-cache) - (setq eglot--last-inserted-char nil)) - -(defun eglot--CompletionParams () - (append - (eglot--TextDocumentPositionParams) - `(:context - ,(if-let (trigger (and (characterp eglot--last-inserted-char) - (cl-find eglot--last-inserted-char - (eglot-server-capable :completionProvider - :triggerCharacters) - :key (lambda (str) (aref str 0)) - :test #'char-equal))) - `(:triggerKind 2 :triggerCharacter ,trigger) `(:triggerKind 1))))) - -(defvar-local eglot--recent-changes nil - "Recent buffer changes as collected by `eglot--before-change'.") - -(cl-defmethod jsonrpc-connection-ready-p ((_server eglot-lsp-server) _what) - "Tell if SERVER is ready for WHAT in current buffer." - (and (cl-call-next-method) (not eglot--recent-changes))) - -(defvar-local eglot--change-idle-timer nil "Idle timer for didChange signals.") - -(defun eglot--before-change (beg end) - "Hook onto `before-change-functions' with BEG and END." - (when (listp eglot--recent-changes) - ;; Records BEG and END, crucially convert them into LSP - ;; (line/char) positions before that information is lost (because - ;; the after-change thingy doesn't know if newlines were - ;; deleted/added). Also record markers of BEG and END - ;; (github#259) - (push `(,(eglot--pos-to-lsp-position beg) - ,(eglot--pos-to-lsp-position end) - (,beg . ,(copy-marker beg nil)) - (,end . ,(copy-marker end t))) - eglot--recent-changes))) - -(defvar eglot--document-changed-hook '(eglot--signal-textDocument/didChange) - "Internal hook for doing things when the document changes.") - -(defun eglot--after-change (beg end pre-change-length) - "Hook onto `after-change-functions'. -Records BEG, END and PRE-CHANGE-LENGTH locally." - (cl-incf eglot--versioned-identifier) - (pcase (car-safe eglot--recent-changes) - (`(,lsp-beg ,lsp-end - (,b-beg . ,b-beg-marker) - (,b-end . ,b-end-marker)) - ;; github#259 and github#367: with `capitalize-word' & friends, - ;; `before-change-functions' records the whole word's `b-beg' and - ;; `b-end'. Similarly, when `fill-paragraph' coalesces two - ;; lines, `b-beg' and `b-end' mark end of first line and end of - ;; second line, resp. In both situations, `beg' and `end' - ;; received here seemingly contradict that: they will differ by 1 - ;; and encompass the capitalized character or, in the coalescing - ;; case, the replacement of the newline with a space. We keep - ;; both markers and positions to detect and correct this. In - ;; this specific case, we ignore `beg', `len' and - ;; `pre-change-len' and send richer information about the region - ;; from the markers. I've also experimented with doing this - ;; unconditionally but it seems to break when newlines are added. - (if (and (= b-end b-end-marker) (= b-beg b-beg-marker) - (or (/= beg b-beg) (/= end b-end))) - (setcar eglot--recent-changes - `(,lsp-beg ,lsp-end ,(- b-end-marker b-beg-marker) - ,(buffer-substring-no-properties b-beg-marker - b-end-marker))) - (setcar eglot--recent-changes - `(,lsp-beg ,lsp-end ,pre-change-length - ,(buffer-substring-no-properties beg end))))) - (_ (setf eglot--recent-changes :emacs-messup))) - (when eglot--change-idle-timer (cancel-timer eglot--change-idle-timer)) - (let ((buf (current-buffer))) - (setq eglot--change-idle-timer - (run-with-idle-timer - eglot-send-changes-idle-time - nil (lambda () (eglot--when-live-buffer buf - (when eglot--managed-mode - (run-hooks 'eglot--document-changed-hook) - (setq eglot--change-idle-timer nil)))))))) - -(defvar-local eglot-workspace-configuration () - "Configure LSP servers specifically for a given project. - -This variable's value should be a plist (SECTION VALUE ...). -SECTION is a keyword naming a parameter section relevant to a -particular server. VALUE is a plist or a primitive type -converted to JSON also understood by that server. - -Instead of a plist, an alist ((SECTION . VALUE) ...) can be used -instead, but this variant is less reliable and not recommended. - -This variable should be set as a directory-local variable. See -info node `(emacs)Directory Variables' for various ways to do that. - -Here's an example value that establishes two sections relevant to -the Pylsp and Gopls LSP servers: - - (:pylsp (:plugins (:jedi_completion (:include_params t - :fuzzy t) - :pylint (:enabled :json-false))) - :gopls (:usePlaceholders t)) - -The value of this variable can also be a unary function of a -single argument, which will be a connected `eglot-lsp-server' -instance. The function runs with `default-directory' set to the -root of the current project. It should return an object of the -format described above.") - -;;;###autoload -(put 'eglot-workspace-configuration 'safe-local-variable #'listp) - -(defun eglot-show-workspace-configuration (&optional server) - "Dump `eglot-workspace-configuration' as JSON for debugging." - (interactive (list (eglot--read-server "Show workspace configuration for" t))) - (let ((conf (eglot--workspace-configuration-plist server))) - (with-current-buffer (get-buffer-create "*EGLOT workspace configuration*") - (erase-buffer) - (insert (jsonrpc--json-encode conf)) - (with-no-warnings - (require 'json) - (when (require 'json-mode nil t) (json-mode)) - (json-pretty-print-buffer)) - (pop-to-buffer (current-buffer))))) - -(defun eglot--workspace-configuration-plist (server &optional path) - "Returns SERVER's workspace configuration as a plist. -If PATH consider that file's `file-name-directory' to get the -local value of the `eglot-workspace-configuration' variable, else -use the root of SERVER's `eglot--project'." - (let ((val (with-temp-buffer - (setq default-directory - ;; See github#1281 - (if path (if (file-directory-p path) - (file-name-as-directory path) - (file-name-directory path)) - (project-root (eglot--project server)))) - ;; Set the major mode to be the first of the managed - ;; modes. This is the one the user started eglot in. - (setq major-mode (car (eglot--major-modes server))) - (hack-dir-local-variables-non-file-buffer) - (if (functionp eglot-workspace-configuration) - (funcall eglot-workspace-configuration server) - eglot-workspace-configuration)))) - (or (and (consp (car val)) - (cl-loop for (section . v) in val - collect (if (keywordp section) section - (intern (format ":%s" section))) - collect v)) - val))) - -(defun eglot-signal-didChangeConfiguration (server) - "Send a `:workspace/didChangeConfiguration' signal to SERVER. -When called interactively, use the currently active server" - (interactive (list (eglot--current-server-or-lose))) - (jsonrpc-notify - server :workspace/didChangeConfiguration - (list - :settings - (or (eglot--workspace-configuration-plist server) - eglot--{})))) - -(cl-defmethod eglot-handle-request - (server (_method (eql workspace/configuration)) &key items) - "Handle server request workspace/configuration." - (apply #'vector - (mapcar - (eglot--lambda ((ConfigurationItem) scopeUri section) - (cl-loop - with scope-uri-path = (and scopeUri (eglot-uri-to-path scopeUri)) - for (wsection o) - on (eglot--workspace-configuration-plist server scope-uri-path) - by #'cddr - when (string= - (if (keywordp wsection) - (substring (symbol-name wsection) 1) - wsection) - section) - return o)) - items))) - -(defun eglot--signal-textDocument/didChange () - "Send textDocument/didChange to server." - (when eglot--recent-changes - (let* ((server (eglot--current-server-or-lose)) - (sync-capability (eglot-server-capable :textDocumentSync)) - (sync-kind (if (numberp sync-capability) sync-capability - (plist-get sync-capability :change))) - (full-sync-p (or (eq sync-kind 1) - (eq :emacs-messup eglot--recent-changes)))) - (jsonrpc-notify - server :textDocument/didChange - (list - :textDocument (eglot--VersionedTextDocumentIdentifier) - :contentChanges - (if full-sync-p - (vector `(:text ,(eglot--widening - (buffer-substring-no-properties (point-min) - (point-max))))) - (cl-loop for (beg end len text) in (reverse eglot--recent-changes) - ;; github#259: `capitalize-word' and commands based - ;; on `casify_region' will cause multiple duplicate - ;; empty entries in `eglot--before-change' calls - ;; without an `eglot--after-change' reciprocal. - ;; Weed them out here. - when (numberp len) - vconcat `[,(list :range `(:start ,beg :end ,end) - :rangeLength len :text text)])))) - (setq eglot--recent-changes nil) - (jsonrpc--call-deferred server)))) - -(defun eglot--signal-textDocument/didOpen () - "Send textDocument/didOpen to server." - (setq eglot--recent-changes nil eglot--versioned-identifier 0) - (jsonrpc-notify - (eglot--current-server-or-lose) - :textDocument/didOpen `(:textDocument ,(eglot--TextDocumentItem)))) - -(defun eglot--signal-textDocument/didClose () - "Send textDocument/didClose to server." - (with-demoted-errors - "[eglot] error sending textDocument/didClose: %s" - (jsonrpc-notify - (eglot--current-server-or-lose) - :textDocument/didClose `(:textDocument ,(eglot--TextDocumentIdentifier))))) - -(defun eglot--signal-textDocument/willSave () - "Maybe send textDocument/willSave to server." - (let ((server (eglot--current-server-or-lose)) - (params `(:reason 1 :textDocument ,(eglot--TextDocumentIdentifier)))) - (when (eglot-server-capable :textDocumentSync :willSave) - (jsonrpc-notify server :textDocument/willSave params)) - (when (eglot-server-capable :textDocumentSync :willSaveWaitUntil) - (ignore-errors - (eglot--apply-text-edits - (eglot--request server :textDocument/willSaveWaitUntil params - :timeout 0.5)))))) - -(defun eglot--signal-textDocument/didSave () - "Maybe send textDocument/didSave to server." - (eglot--signal-textDocument/didChange) - (when (eglot-server-capable :textDocumentSync :save) - (jsonrpc-notify - (eglot--current-server-or-lose) - :textDocument/didSave - (list - ;; TODO: Handle TextDocumentSaveRegistrationOptions to control this. - :text (buffer-substring-no-properties (point-min) (point-max)) - :textDocument (eglot--TextDocumentIdentifier))))) - -(defun eglot-flymake-backend (report-fn &rest _more) - "A Flymake backend for Eglot. -Calls REPORT-FN (or arranges for it to be called) when the server -publishes diagnostics. Between calls to this function, REPORT-FN -may be called multiple times (respecting the protocol of -`flymake-diagnostic-functions')." - (cond (eglot--managed-mode - (setq eglot--current-flymake-report-fn report-fn) - (eglot--report-to-flymake eglot--diagnostics)) - (t - (funcall report-fn nil)))) - -(defun eglot--report-to-flymake (diags) - "Internal helper for `eglot-flymake-backend'." - (save-restriction - (widen) - (funcall eglot--current-flymake-report-fn diags - ;; If the buffer hasn't changed since last - ;; call to the report function, flymake won't - ;; delete old diagnostics. Using :region - ;; keyword forces flymake to delete - ;; them (github#159). - :region (cons (point-min) (point-max)))) - (setq eglot--diagnostics diags)) - -(defun eglot-xref-backend () "Eglot xref backend." 'eglot) - -(defvar eglot--temp-location-buffers (make-hash-table :test #'equal) - "Helper variable for `eglot--collecting-xrefs'.") - -(defvar eglot-xref-lessp-function #'ignore - "Compare two `xref-item' objects for sorting.") - -(cl-defmacro eglot--collecting-xrefs ((collector) &rest body) - "Sort and handle xrefs collected with COLLECTOR in BODY." - (declare (indent 1) (debug (sexp &rest form))) - (let ((collected (cl-gensym "collected"))) - `(unwind-protect - (let (,collected) - (cl-flet ((,collector (xref) (push xref ,collected))) - ,@body) - (setq ,collected (nreverse ,collected)) - (sort ,collected eglot-xref-lessp-function)) - (maphash (lambda (_uri buf) (kill-buffer buf)) eglot--temp-location-buffers) - (clrhash eglot--temp-location-buffers)))) - -(defun eglot--xref-make-match (name uri range) - "Like `xref-make-match' but with LSP's NAME, URI and RANGE. -Try to visit the target file for a richer summary line." - (pcase-let* - ((file (eglot-uri-to-path uri)) - (visiting (or (find-buffer-visiting file) - (gethash uri eglot--temp-location-buffers))) - (collect (lambda () - (eglot--widening - (pcase-let* ((`(,beg . ,end) (eglot-range-region range)) - (bol (progn (goto-char beg) (eglot--bol))) - (substring (buffer-substring bol (line-end-position))) - (hi-beg (- beg bol)) - (hi-end (- (min (line-end-position) end) bol))) - (add-face-text-property hi-beg hi-end 'xref-match - t substring) - (list substring (line-number-at-pos (point) t) - (eglot-utf-32-linepos) (- end beg)))))) - (`(,summary ,line ,column ,length) - (cond - (visiting (with-current-buffer visiting (funcall collect))) - ((file-readable-p file) (with-current-buffer - (puthash uri (generate-new-buffer " *temp*") - eglot--temp-location-buffers) - (insert-file-contents file) - (funcall collect))) - (t ;; fall back to the "dumb strategy" - (let* ((start (cl-getf range :start)) - (line (1+ (cl-getf start :line))) - (start-pos (cl-getf start :character)) - (end-pos (cl-getf (cl-getf range :end) :character))) - (list name line start-pos (- end-pos start-pos))))))) - (setf (gethash (expand-file-name file) eglot--servers-by-xrefed-file) - (eglot--current-server-or-lose)) - (xref-make-match summary (xref-make-file-location file line column) length))) - -(defun eglot--workspace-symbols (pat &optional buffer) - "Ask for :workspace/symbol on PAT, return list of formatted strings. -If BUFFER, switch to it before." - (with-current-buffer (or buffer (current-buffer)) - (eglot-server-capable-or-lose :workspaceSymbolProvider) - (mapcar - (lambda (wss) - (eglot--dbind ((WorkspaceSymbol) name containerName kind) wss - (propertize - (format "%s%s %s" - (if (zerop (length containerName)) "" - (concat (propertize containerName 'face 'shadow) " ")) - name - (propertize (alist-get kind eglot--symbol-kind-names "Unknown") - 'face 'shadow)) - 'eglot--lsp-workspaceSymbol wss))) - (eglot--request (eglot--current-server-or-lose) :workspace/symbol - `(:query ,pat))))) - -(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql eglot))) - "Yet another tricky connection between LSP and Elisp completion semantics." - (let ((buf (current-buffer)) (cache eglot--workspace-symbols-cache)) - (cl-labels ((refresh (pat) (eglot--workspace-symbols pat buf)) - (lookup-1 (pat) ;; check cache, else refresh - (let ((probe (gethash pat cache :missing))) - (if (eq probe :missing) (puthash pat (refresh pat) cache) - probe))) - (lookup (pat _point) - (let ((res (lookup-1 pat)) - (def (and (string= pat "") (gethash :default cache)))) - (append def res nil))) - (score (c) - (cl-getf (get-text-property - 0 'eglot--lsp-workspaceSymbol c) - :score 0))) - (external-completion-table - 'eglot-indirection-joy - #'lookup - `((cycle-sort-function - . ,(lambda (completions) - (cl-sort completions #'> :key #'score)))))))) - -(defun eglot--recover-workspace-symbol-meta (string) - "Search `eglot--workspace-symbols-cache' for rich entry of STRING." - (catch 'found - (maphash (lambda (_k v) - (while (consp v) - ;; Like mess? Ask minibuffer.el about improper lists. - (when (equal (car v) string) (throw 'found (car v))) - (setq v (cdr v)))) - eglot--workspace-symbols-cache))) - -(cl-defmethod xref-backend-identifier-at-point ((_backend (eql eglot))) - (let ((attempt - (and (xref--prompt-p this-command) - (puthash :default - (ignore-errors - (eglot--workspace-symbols (symbol-name (symbol-at-point)))) - eglot--workspace-symbols-cache)))) - (if attempt (car attempt) "LSP identifier at point"))) - -(defvar eglot--lsp-xref-refs nil - "`xref' objects for overriding `xref-backend-references''s.") - -(cl-defun eglot--lsp-xrefs-for-method (method &key extra-params capability) - "Make `xref''s for METHOD, EXTRA-PARAMS, check CAPABILITY." - (eglot-server-capable-or-lose - (or capability - (intern - (format ":%sProvider" - (cadr (split-string (symbol-name method) - "/")))))) - (let ((response - (eglot--request - (eglot--current-server-or-lose) - method (append (eglot--TextDocumentPositionParams) extra-params)))) - (eglot--collecting-xrefs (collect) - (mapc - (lambda (loc-or-loc-link) - (let ((sym-name (symbol-name (symbol-at-point)))) - (eglot--dcase loc-or-loc-link - (((LocationLink) targetUri targetSelectionRange) - (collect (eglot--xref-make-match sym-name - targetUri targetSelectionRange))) - (((Location) uri range) - (collect (eglot--xref-make-match sym-name - uri range)))))) - (if (vectorp response) response (and response (list response))))))) - -(cl-defun eglot--lsp-xref-helper (method &key extra-params capability) - "Helper for `eglot-find-declaration' & friends." - (let ((eglot--lsp-xref-refs (eglot--lsp-xrefs-for-method - method - :extra-params extra-params - :capability capability))) - (if eglot--lsp-xref-refs - (xref-find-references "LSP identifier at point.") - (eglot--message "%s returned no references" method)))) - -(defun eglot-find-declaration () - "Find declaration for SYM, the identifier at point." - (interactive) - (eglot--lsp-xref-helper :textDocument/declaration)) - -(defun eglot-find-implementation () - "Find implementation for SYM, the identifier at point." - (interactive) - (eglot--lsp-xref-helper :textDocument/implementation)) - -(defun eglot-find-typeDefinition () - "Find type definition for SYM, the identifier at point." - (interactive) - (eglot--lsp-xref-helper :textDocument/typeDefinition)) - -(cl-defmethod xref-backend-definitions ((_backend (eql eglot)) id) - (let ((probe (eglot--recover-workspace-symbol-meta id))) - (if probe - (eglot--dbind ((WorkspaceSymbol) name location) - (get-text-property 0 'eglot--lsp-workspaceSymbol probe) - (eglot--dbind ((Location) uri range) location - (list (eglot--xref-make-match name uri range)))) - (eglot--lsp-xrefs-for-method :textDocument/definition)))) - -(cl-defmethod xref-backend-references ((_backend (eql eglot)) _identifier) - (or - eglot--lsp-xref-refs - (eglot--lsp-xrefs-for-method - :textDocument/references :extra-params `(:context (:includeDeclaration t))))) - -(cl-defmethod xref-backend-apropos ((_backend (eql eglot)) pattern) - (when (eglot-server-capable :workspaceSymbolProvider) - (eglot--collecting-xrefs (collect) - (mapc - (eglot--lambda ((SymbolInformation) name location) - (eglot--dbind ((Location) uri range) location - (collect (eglot--xref-make-match name uri range)))) - (eglot--request (eglot--current-server-or-lose) - :workspace/symbol - `(:query ,pattern)))))) - -(defun eglot-format-buffer () - "Format contents of current buffer." - (interactive) - (eglot-format nil nil)) - -(defun eglot-format (&optional beg end on-type-format) - "Format region BEG END. -If either BEG or END is nil, format entire buffer. -Interactively, format active region, or entire buffer if region -is not active. - -If non-nil, ON-TYPE-FORMAT is a character just inserted at BEG -for which LSP on-type-formatting should be requested." - (interactive (and (region-active-p) (list (region-beginning) (region-end)))) - (pcase-let ((`(,method ,cap ,args) - (cond - ((and beg on-type-format) - `(:textDocument/onTypeFormatting - :documentOnTypeFormattingProvider - ,`(:position ,(eglot--pos-to-lsp-position beg) - :ch ,(string on-type-format)))) - ((and beg end) - `(:textDocument/rangeFormatting - :documentRangeFormattingProvider - (:range ,(list :start (eglot--pos-to-lsp-position beg) - :end (eglot--pos-to-lsp-position end))))) - (t - '(:textDocument/formatting :documentFormattingProvider nil))))) - (eglot-server-capable-or-lose cap) - (eglot--apply-text-edits - (eglot--request - (eglot--current-server-or-lose) - method - (cl-list* - :textDocument (eglot--TextDocumentIdentifier) - :options (list :tabSize tab-width - :insertSpaces (if indent-tabs-mode :json-false t) - :insertFinalNewline (if require-final-newline t :json-false) - :trimFinalNewlines (if delete-trailing-lines t :json-false)) - args)) - nil - on-type-format))) - -(defvar eglot-cache-session-completions t - "If non-nil Eglot caches data during completion sessions.") - -(defvar eglot--capf-session :none "A cache used by `eglot-completion-at-point'.") - -(defun eglot--capf-session-flush (&optional _) (setq eglot--capf-session :none)) - -(defun eglot--dumb-flex (pat comp ignorecase) - "Return destructively fontified COMP iff PAT matches it." - (cl-loop with lcomp = (length comp) - with case-fold-search = ignorecase - initially (remove-list-of-text-properties 0 lcomp '(face) comp) - for x across pat - for i = (cl-loop for j from (if i (1+ i) 0) below lcomp - when (char-equal x (aref comp j)) return j) - unless i do (cl-return nil) - ;; FIXME: could do much better here and coalesce intervals - do (add-face-text-property i (1+ i) 'completions-common-part - nil comp) - finally (cl-return comp))) - -(defun eglot--dumb-allc (pat table pred _point) (funcall table pat pred t)) -(defun eglot--dumb-tryc (pat table pred point) - (if-let ((probe (funcall table pat pred nil))) - (cons probe (length probe)) - (cons pat point))) - -(add-to-list 'completion-category-defaults '(eglot-capf (styles eglot--dumb-flex))) -(add-to-list 'completion-styles-alist '(eglot--dumb-flex eglot--dumb-tryc eglot--dumb-allc)) - -(defun eglot-completion-at-point () - "Eglot's `completion-at-point' function." - ;; Commit logs for this function help understand what's going on. - (when-let (completion-capability (eglot-server-capable :completionProvider)) - (let* ((server (eglot--current-server-or-lose)) - (bounds (or (bounds-of-thing-at-point 'symbol) - (cons (point) (point)))) - (bounds-string (buffer-substring (car bounds) (cdr bounds))) - (sort-completions - (lambda (completions) - (cl-sort completions - #'string-lessp - :key (lambda (c) - (plist-get - (get-text-property 0 'eglot--lsp-item c) - :sortText))))) - (metadata `(metadata (category . eglot-capf) - (display-sort-function . ,sort-completions))) - (local-cache :none) - (orig-pos (point)) - (resolved (make-hash-table)) - (proxies - (lambda () - (if (listp local-cache) local-cache - (let* ((resp (eglot--request server - :textDocument/completion - (eglot--CompletionParams) - :cancel-on-input t)) - (items (append - (if (vectorp resp) resp (plist-get resp :items)) - nil)) - (cachep (and (listp resp) items - eglot-cache-session-completions - (eq (plist-get resp :isIncomplete) :json-false))) - (retval - (mapcar - (jsonrpc-lambda - (&rest item &key label insertText insertTextFormat - textEdit &allow-other-keys) - (let ((proxy - ;; Snippet or textEdit, it's safe to - ;; display/insert the label since - ;; it'll be adjusted. If no usable - ;; insertText at all, label is best, - ;; too. - (cond ((or (eql insertTextFormat 2) - textEdit - (null insertText) - (string-empty-p insertText)) - (string-trim-left label)) - (t insertText)))) - (unless (zerop (length proxy)) - (put-text-property 0 1 'eglot--lsp-item item proxy)) - proxy)) - items))) - ;; (trace-values "Requested" (length proxies) cachep bounds) - (setq eglot--capf-session - (if cachep (list bounds retval resolved orig-pos - bounds-string) :none)) - (setq local-cache retval))))) - (resolve-maybe - ;; Maybe completion/resolve JSON object `lsp-comp' into - ;; another JSON object, if at all possible. Otherwise, - ;; just return lsp-comp. - (lambda (lsp-comp) - (or (gethash lsp-comp resolved) - (setf (gethash lsp-comp resolved) - (if (and (eglot-server-capable :completionProvider - :resolveProvider) - (plist-get lsp-comp :data)) - (eglot--request server :completionItem/resolve - lsp-comp :cancel-on-input t) - lsp-comp)))))) - (when (and (consp eglot--capf-session) - (= (car bounds) (car (nth 0 eglot--capf-session))) - (>= (cdr bounds) (cdr (nth 0 eglot--capf-session)))) - (setq local-cache (nth 1 eglot--capf-session) - resolved (nth 2 eglot--capf-session) - orig-pos (nth 3 eglot--capf-session) - bounds-string (nth 4 eglot--capf-session)) - ;; (trace-values "Recalling cache" (length local-cache) bounds orig-pos) - ) - (list - (car bounds) - (cdr bounds) - (lambda (pattern pred action) - (cond - ((eq action 'metadata) metadata) ; metadata - ((eq action 'lambda) ; test-completion - (test-completion pattern (funcall proxies))) - ((eq (car-safe action) 'boundaries) nil) ; boundaries - ((null action) ; try-completion - (try-completion pattern (funcall proxies))) - ((eq action t) ; all-completions - (let ((comps (funcall proxies))) - (dolist (c comps) (eglot--dumb-flex pattern c t)) - (all-completions - "" - comps - (lambda (proxy) - (let* ((item (get-text-property 0 'eglot--lsp-item proxy)) - (filterText (plist-get item :filterText))) - (and (or (null pred) (funcall pred proxy)) - (eglot--dumb-flex - pattern (or filterText proxy) completion-ignore-case))))))))) - :annotation-function - (lambda (proxy) - (eglot--dbind ((CompletionItem) detail kind) - (get-text-property 0 'eglot--lsp-item proxy) - (let* ((detail (and (stringp detail) - (not (string= detail "")) - detail)) - (annotation - (or detail - (cdr (assoc kind eglot--kind-names))))) - (when annotation - (concat " " - (propertize annotation - 'face 'font-lock-function-name-face)))))) - :company-kind - ;; Associate each lsp-item with a lsp-kind symbol. - (lambda (proxy) - (when-let* ((lsp-item (get-text-property 0 'eglot--lsp-item proxy)) - (kind (alist-get (plist-get lsp-item :kind) - eglot--kind-names))) - (pcase kind - ("EnumMember" 'enum-member) - ("TypeParameter" 'type-parameter) - (_ (intern (downcase kind)))))) - :company-deprecated - (lambda (proxy) - (when-let ((lsp-item (get-text-property 0 'eglot--lsp-item proxy))) - (or (seq-contains-p (plist-get lsp-item :tags) - 1) - (eq t (plist-get lsp-item :deprecated))))) - :company-docsig - ;; FIXME: autoImportText is specific to the pyright language server - (lambda (proxy) - (when-let* ((lsp-comp (get-text-property 0 'eglot--lsp-item proxy)) - (data (plist-get (funcall resolve-maybe lsp-comp) :data)) - (import-text (plist-get data :autoImportText))) - import-text)) - :company-doc-buffer - (lambda (proxy) - (let* ((documentation - (let ((lsp-comp (get-text-property 0 'eglot--lsp-item proxy))) - (plist-get (funcall resolve-maybe lsp-comp) :documentation))) - (formatted (and documentation - (eglot--format-markup documentation)))) - (when formatted - (with-current-buffer (get-buffer-create " *eglot doc*") - (erase-buffer) - (insert formatted) - (current-buffer))))) - :company-require-match 'never - :company-prefix-length - (save-excursion - (goto-char (car bounds)) - (when (listp completion-capability) - (looking-back - (regexp-opt - (cl-coerce (cl-getf completion-capability :triggerCharacters) 'list)) - (eglot--bol)))) - :exit-function - (lambda (proxy status) - (eglot--capf-session-flush) - (when (memq status '(finished exact)) - ;; To assist in using this whole `completion-at-point' - ;; function inside `completion-in-region', ensure the exit - ;; function runs in the buffer where the completion was - ;; triggered from. This should probably be in Emacs itself. - ;; (github#505) - (with-current-buffer (if (minibufferp) - (window-buffer (minibuffer-selected-window)) - (current-buffer)) - (eglot--dbind ((CompletionItem) insertTextFormat - insertText textEdit additionalTextEdits label) - (funcall - resolve-maybe - (or (get-text-property 0 'eglot--lsp-item proxy) - ;; When selecting from the *Completions* - ;; buffer, `proxy' won't have any properties. - ;; A lookup should fix that (github#148) - (get-text-property - 0 'eglot--lsp-item - (cl-find proxy (funcall proxies) :test #'string=)))) - (let ((snippet-fn (and (eql insertTextFormat 2) - (eglot--snippet-expansion-fn)))) - (cond (textEdit - ;; Revert buffer back to state when the edit - ;; was obtained from server. If a `proxy' - ;; "bar" was obtained from a buffer with - ;; "foo.b", the LSP edit applies to that - ;; state, _not_ the current "foo.bar". - (delete-region orig-pos (point)) - (insert (substring bounds-string (- orig-pos (car bounds)))) - (eglot--dbind ((TextEdit) range newText) textEdit - (pcase-let ((`(,beg . ,end) - (eglot-range-region range))) - (delete-region beg end) - (goto-char beg) - (funcall (or snippet-fn #'insert) newText)))) - (snippet-fn - ;; A snippet should be inserted, but using plain - ;; `insertText'. This requires us to delete the - ;; whole completion, since `insertText' is the full - ;; completion's text. - (delete-region (- (point) (length proxy)) (point)) - (funcall snippet-fn (or insertText label)))) - (when (cl-plusp (length additionalTextEdits)) - (eglot--apply-text-edits additionalTextEdits))) - (eglot--signal-textDocument/didChange))))))))) - -(defun eglot--hover-info (contents &optional _range) - (mapconcat #'eglot--format-markup - (if (vectorp contents) contents (list contents)) "\n")) - -(defun eglot--sig-info (sig &optional sig-active briefp) - (eglot--dbind ((SignatureInformation) - ((:label siglabel)) - ((:documentation sigdoc)) parameters activeParameter) - sig - (with-temp-buffer - (insert siglabel) - ;; Add documentation, indented so we can distinguish multiple signatures - (when-let (doc (and (not briefp) sigdoc (eglot--format-markup sigdoc))) - (goto-char (point-max)) - (insert "\n" (replace-regexp-in-string "^" " " doc))) - ;; Try to highlight function name only - (let (first-parlabel) - (cond ((and (cl-plusp (length parameters)) - (vectorp (setq first-parlabel - (plist-get (aref parameters 0) :label)))) - (save-excursion - (goto-char (elt first-parlabel 0)) - (skip-syntax-backward "^w") - (add-face-text-property (point-min) (point) - 'font-lock-function-name-face))) - ((save-excursion - (goto-char (point-min)) - (looking-at "\\([^(]*\\)([^)]*)")) - (add-face-text-property (match-beginning 1) (match-end 1) - 'font-lock-function-name-face)))) - ;; Now to the parameters - (cl-loop - with active-param = (or sig-active activeParameter) - for i from 0 for parameter across parameters do - (eglot--dbind ((ParameterInformation) - ((:label parlabel)) - ((:documentation pardoc))) - parameter - ;; ...perhaps highlight it in the formals list - (when (eq i active-param) - (save-excursion - (goto-char (point-min)) - (pcase-let - ((`(,beg ,end) - (if (stringp parlabel) - (let ((case-fold-search nil)) - (and (search-forward parlabel (line-end-position) t) - (list (match-beginning 0) (match-end 0)))) - (mapcar #'1+ (append parlabel nil))))) - (if (and beg end) - (add-face-text-property - beg end - 'eldoc-highlight-function-argument))))) - ;; ...and/or maybe add its doc on a line by its own. - (let (fpardoc) - (when (and pardoc (not briefp) - (not (string-empty-p - (setq fpardoc (eglot--format-markup pardoc))))) - (insert "\n " - (propertize - (if (stringp parlabel) parlabel - (apply #'substring siglabel (mapcar #'1+ parlabel))) - 'face (and (eq i active-param) 'eldoc-highlight-function-argument)) - ": " fpardoc))))) - (buffer-string)))) - -(defun eglot-signature-eldoc-function (cb) - "A member of `eldoc-documentation-functions', for signatures." - (when (eglot-server-capable :signatureHelpProvider) - (let ((buf (current-buffer))) - (jsonrpc-async-request - (eglot--current-server-or-lose) - :textDocument/signatureHelp (eglot--TextDocumentPositionParams) - :success-fn - (eglot--lambda ((SignatureHelp) - signatures activeSignature (activeParameter 0)) - (eglot--when-buffer-window buf - (let ((active-sig (and (cl-plusp (length signatures)) - (aref signatures (or activeSignature 0))))) - (if (not active-sig) (funcall cb nil) - (funcall - cb (mapconcat (lambda (s) - (eglot--sig-info s (and (eq s active-sig) - activeParameter) - nil)) - signatures "\n") - :echo (eglot--sig-info active-sig activeParameter t)))))) - :deferred :textDocument/signatureHelp)) - t)) - -(defun eglot-hover-eldoc-function (cb) - "A member of `eldoc-documentation-functions', for hover." - (when (eglot-server-capable :hoverProvider) - (let ((buf (current-buffer))) - (jsonrpc-async-request - (eglot--current-server-or-lose) - :textDocument/hover (eglot--TextDocumentPositionParams) - :success-fn (eglot--lambda ((Hover) contents range) - (eglot--when-buffer-window buf - (let ((info (unless (seq-empty-p contents) - (eglot--hover-info contents range)))) - (funcall cb info - :echo (and info (string-match "\n" info)))))) - :deferred :textDocument/hover)) - (eglot--highlight-piggyback cb) - t)) - -(defvar eglot--highlights nil "Overlays for textDocument/documentHighlight.") - -(defun eglot--highlight-piggyback (_cb) - "Request and handle `:textDocument/documentHighlight'." - ;; FIXME: Obviously, this is just piggy backing on eldoc's calls for - ;; convenience, as shown by the fact that we just ignore cb. - (let ((buf (current-buffer))) - (when (eglot-server-capable :documentHighlightProvider) - (jsonrpc-async-request - (eglot--current-server-or-lose) - :textDocument/documentHighlight (eglot--TextDocumentPositionParams) - :success-fn - (lambda (highlights) - (mapc #'delete-overlay eglot--highlights) - (setq eglot--highlights - (eglot--when-buffer-window buf - (mapcar - (eglot--lambda ((DocumentHighlight) range) - (pcase-let ((`(,beg . ,end) - (eglot-range-region range))) - (let ((ov (make-overlay beg end))) - (overlay-put ov 'face 'eglot-highlight-symbol-face) - (overlay-put ov 'modification-hooks - `(,(lambda (o &rest _) (delete-overlay o)))) - ov))) - highlights)))) - :deferred :textDocument/documentHighlight) - nil))) - -(defun eglot--imenu-SymbolInformation (res) - "Compute `imenu--index-alist' for RES vector of SymbolInformation." - (mapcar - (pcase-lambda (`(,kind . ,objs)) - (cons - (alist-get kind eglot--symbol-kind-names "Unknown") - (mapcan - (pcase-lambda (`(,container . ,objs)) - (let ((elems (mapcar - (eglot--lambda ((SymbolInformation) kind name location) - (let ((reg (eglot-range-region - (plist-get location :range))) - (kind (alist-get kind eglot--symbol-kind-names))) - (cons (propertize name - 'breadcrumb-region reg - 'breadcrumb-kind kind) - (car reg)))) - objs))) - (if container (list (cons container elems)) elems))) - (seq-group-by - (eglot--lambda ((SymbolInformation) containerName) containerName) objs)))) - (seq-group-by (eglot--lambda ((SymbolInformation) kind) kind) res))) - -(defun eglot--imenu-DocumentSymbol (res) - "Compute `imenu--index-alist' for RES vector of DocumentSymbol." - (cl-labels ((dfs (&key name children range kind &allow-other-keys) - (let* ((reg (eglot-range-region range)) - (kind (alist-get kind eglot--symbol-kind-names)) - (name (propertize name - 'breadcrumb-region reg - 'breadcrumb-kind kind))) - (if (seq-empty-p children) - (cons name (car reg)) - (cons name - (mapcar (lambda (c) (apply #'dfs c)) children)))))) - (mapcar (lambda (s) (apply #'dfs s)) res))) - -(cl-defun eglot-imenu () - "Eglot's `imenu-create-index-function'. -Returns a list as described in docstring of `imenu--index-alist'." - (unless (eglot-server-capable :documentSymbolProvider) - (cl-return-from eglot-imenu)) - (let* ((res (eglot--request (eglot--current-server-or-lose) - :textDocument/documentSymbol - `(:textDocument - ,(eglot--TextDocumentIdentifier)) - :cancel-on-input non-essential)) - (head (and (cl-plusp (length res)) (elt res 0)))) - (when head - (eglot--dcase head - (((SymbolInformation)) (eglot--imenu-SymbolInformation res)) - (((DocumentSymbol)) (eglot--imenu-DocumentSymbol res)))))) - -(cl-defun eglot--apply-text-edits (edits &optional version silent) - "Apply EDITS for current buffer if at VERSION, or if it's nil. -If SILENT, don't echo progress in mode-line." - (unless edits (cl-return-from eglot--apply-text-edits)) - (unless (or (not version) (equal version eglot--versioned-identifier)) - (jsonrpc-error "Edits on `%s' require version %d, you have %d" - (current-buffer) version eglot--versioned-identifier)) - (atomic-change-group - (let* ((change-group (prepare-change-group)) - (howmany (length edits)) - (reporter (unless silent - (make-progress-reporter - (format "[eglot] applying %s edits to `%s'..." - howmany (current-buffer)) - 0 howmany))) - (done 0)) - (mapc (pcase-lambda (`(,newText ,beg . ,end)) - (let ((source (current-buffer))) - (with-temp-buffer - (insert newText) - (let ((temp (current-buffer))) - (with-current-buffer source - (save-excursion - (save-restriction - (narrow-to-region beg end) - (replace-buffer-contents temp))) - (when reporter - (eglot--reporter-update reporter (cl-incf done)))))))) - (mapcar (eglot--lambda ((TextEdit) range newText) - (cons newText (eglot-range-region range 'markers))) - (reverse edits))) - (undo-amalgamate-change-group change-group) - (when reporter - (progress-reporter-done reporter))))) - -(defun eglot--confirm-server-edits (origin _prepared) - "Helper for `eglot--apply-workspace-edit. -ORIGIN is a symbol designating a command. Reads the -`eglot-confirm-server-edits' user option and returns a symbol -like `diff', `summary' or nil." - (let (v) - (cond ((symbolp eglot-confirm-server-edits) eglot-confirm-server-edits) - ((setq v (assoc origin eglot-confirm-server-edits)) (cdr v)) - ((setq v (assoc t eglot-confirm-server-edits)) (cdr v))))) - -(defun eglot--propose-changes-as-diff (prepared) - "Helper for `eglot--apply-workspace-edit'. -Goal is to popup a `diff-mode' buffer containing all the changes -of PREPARED, ready to apply with C-c C-a. PREPARED is a -list ((FILENAME EDITS VERSION)...)." - (with-current-buffer (get-buffer-create "*EGLOT proposed server changes*") - (buffer-disable-undo (current-buffer)) - (let ((inhibit-read-only t) - (target (current-buffer))) - (diff-mode) - (erase-buffer) - (pcase-dolist (`(,path ,edits ,_) prepared) - (with-temp-buffer - (let* ((diff (current-buffer)) - (existing-buf (find-buffer-visiting path)) - (existing-buf-label (prin1-to-string existing-buf))) - (with-temp-buffer - (if existing-buf - (insert-buffer-substring existing-buf) - (insert-file-contents path)) - (eglot--apply-text-edits edits nil t) - (diff-no-select (or existing-buf path) (current-buffer) nil t diff) - (when existing-buf - ;; Here we have to pretend the label of the unsaved - ;; buffer is the actual file, just so that we can - ;; diff-apply without troubles. If there's a better - ;; way, it probably involves changes to `diff.el'. - (with-current-buffer diff - (goto-char (point-min)) - (while (search-forward existing-buf-label nil t) - (replace-match (buffer-file-name existing-buf)))))) - (with-current-buffer target - (insert-buffer-substring diff)))))) - (setq-local buffer-read-only t) - (buffer-enable-undo (current-buffer)) - (goto-char (point-min)) - (pop-to-buffer (current-buffer)) - (font-lock-ensure))) - -(defun eglot--apply-workspace-edit (wedit origin) - "Apply (or offer to apply) the workspace edit WEDIT. -ORIGIN is a symbol designating the command that originated this -edit proposed by the server." - (eglot--dbind ((WorkspaceEdit) changes documentChanges) wedit - (let ((prepared - (mapcar (eglot--lambda ((TextDocumentEdit) textDocument edits) - (eglot--dbind ((VersionedTextDocumentIdentifier) uri version) - textDocument - (list (eglot-uri-to-path uri) edits version))) - documentChanges))) - (unless (and changes documentChanges) - ;; We don't want double edits, and some servers send both - ;; changes and documentChanges. This unless ensures that we - ;; prefer documentChanges over changes. - (cl-loop for (uri edits) on changes by #'cddr - do (push (list (eglot-uri-to-path uri) edits) prepared))) - (cl-flet ((notevery-visited-p () - (cl-notevery #'find-buffer-visiting - (mapcar #'car prepared))) - (accept-p () - (y-or-n-p - (format "[eglot] Server wants to edit:\n%sProceed? " - (cl-loop - for (f eds _) in prepared - concat (format - " %s (%d change%s)\n" - f (length eds) - (if (> (length eds) 1) "s" "")))))) - (apply () - (cl-loop for edit in prepared - for (path edits version) = edit - do (with-current-buffer (find-file-noselect path) - (eglot--apply-text-edits edits version)) - finally (eldoc) (eglot--message "Edit successful!")))) - (let ((decision (eglot--confirm-server-edits origin prepared))) - (cond - ((or (eq decision 'diff) - (and (eq decision 'maybe-diff) (notevery-visited-p))) - (eglot--propose-changes-as-diff prepared)) - ((or (memq decision '(t summary)) - (and (eq decision 'maybe-summary) (notevery-visited-p))) - (when (accept-p) (apply))) - (t - (apply)))))))) - -(defun eglot-rename (newname) - "Rename the current symbol to NEWNAME." - (interactive - (list (read-from-minibuffer - (format "Rename `%s' to: " (or (thing-at-point 'symbol t) - "unknown symbol")) - nil nil nil nil - (symbol-name (symbol-at-point))))) - (eglot-server-capable-or-lose :renameProvider) - (eglot--apply-workspace-edit - (eglot--request (eglot--current-server-or-lose) - :textDocument/rename `(,@(eglot--TextDocumentPositionParams) - :newName ,newname)) - this-command)) - -(defun eglot--code-action-bounds () - "Calculate appropriate bounds depending on region and point." - (let (diags boftap) - (cond ((use-region-p) `(,(region-beginning) ,(region-end))) - ((setq diags (flymake-diagnostics (point))) - (cl-loop for d in diags - minimizing (flymake-diagnostic-beg d) into beg - maximizing (flymake-diagnostic-end d) into end - finally (cl-return (list beg end)))) - ((setq boftap (bounds-of-thing-at-point 'sexp)) - (list (car boftap) (cdr boftap))) - (t - (list (point) (point)))))) - -(defun eglot-code-actions (beg &optional end action-kind interactive) - "Find LSP code actions of type ACTION-KIND between BEG and END. -Interactively, offer to execute them. -If ACTION-KIND is nil, consider all kinds of actions. -Interactively, default BEG and END to region's bounds else BEG is -point and END is nil, which results in a request for code actions -at point. With prefix argument, prompt for ACTION-KIND." - (interactive - `(,@(eglot--code-action-bounds) - ,(and current-prefix-arg - (completing-read "[eglot] Action kind: " - '("quickfix" "refactor.extract" "refactor.inline" - "refactor.rewrite" "source.organizeImports"))) - t)) - (eglot-server-capable-or-lose :codeActionProvider) - (let* ((server (eglot--current-server-or-lose)) - (actions - (eglot--request - server - :textDocument/codeAction - (list :textDocument (eglot--TextDocumentIdentifier) - :range (list :start (eglot--pos-to-lsp-position beg) - :end (eglot--pos-to-lsp-position end)) - :context - `(:diagnostics - [,@(cl-loop for diag in (flymake-diagnostics beg end) - when (cdr (assoc 'eglot-lsp-diag - (eglot--diag-data diag))) - collect it)] - ,@(when action-kind `(:only [,action-kind])))))) - ;; Redo filtering, in case the `:only' didn't go through. - (actions (cl-loop for a across actions - when (or (not action-kind) - ;; github#847 - (string-prefix-p action-kind (plist-get a :kind))) - collect a))) - (if interactive - (eglot--read-execute-code-action actions server action-kind) - actions))) - -(defalias 'eglot-code-actions-at-mouse (eglot--mouse-call 'eglot-code-actions) - "Like `eglot-code-actions', but intended for mouse events.") - -(defun eglot--read-execute-code-action (actions server &optional action-kind) - "Helper for interactive calls to `eglot-code-actions'." - (let* ((menu-items - (or (cl-loop for a in actions - collect (cons (plist-get a :title) a)) - (apply #'eglot--error - (if action-kind `("No \"%s\" code actions here" ,action-kind) - `("No code actions here"))))) - (preferred-action (cl-find-if - (lambda (menu-item) - (plist-get (cdr menu-item) :isPreferred)) - menu-items)) - (default-action (car (or preferred-action (car menu-items)))) - (chosen (if (and action-kind (null (cadr menu-items))) - (cdr (car menu-items)) - (if (listp last-nonmenu-event) - (x-popup-menu last-nonmenu-event `("Eglot code actions:" - ("dummy" ,@menu-items))) - (cdr (assoc (completing-read - (format "[eglot] Pick an action (default %s): " - default-action) - menu-items nil t nil nil default-action) - menu-items)))))) - (eglot-execute server chosen))) - -(defmacro eglot--code-action (name kind) - "Define NAME to execute KIND code action." - `(defun ,name (beg &optional end) - ,(format "Execute `%s' code actions between BEG and END." kind) - (interactive (eglot--code-action-bounds)) - (eglot-code-actions beg end ,kind t))) - -(eglot--code-action eglot-code-action-organize-imports "source.organizeImports") -(eglot--code-action eglot-code-action-extract "refactor.extract") -(eglot--code-action eglot-code-action-inline "refactor.inline") -(eglot--code-action eglot-code-action-rewrite "refactor.rewrite") -(eglot--code-action eglot-code-action-quickfix "quickfix") - - -;;; Dynamic registration -;;; -(cl-defmethod eglot-register-capability - (server (method (eql workspace/didChangeWatchedFiles)) id &key watchers) - "Handle dynamic registration of workspace/didChangeWatchedFiles." - (eglot-unregister-capability server method id) - (let* (success - (globs (mapcar - (eglot--lambda ((FileSystemWatcher) globPattern kind) - (cons (eglot--glob-compile globPattern t t) - ;; the default "7" means bitwise OR of - ;; WatchKind.Create (1), WatchKind.Change - ;; (2), WatchKind.Delete (4) - (or kind 7))) - watchers)) - (dirs-to-watch - (delete-dups (mapcar #'file-name-directory - (project-files - (eglot--project server)))))) - (cl-labels - ((handle-event (event) - (pcase-let* ((`(,desc ,action ,file ,file1) event) - (action-type (cl-case action - (created 1) (changed 2) (deleted 3))) - (action-bit (when action-type - (ash 1 (1- action-type))))) - (cond - ((and (memq action '(created changed deleted)) - (cl-loop for (glob . kind-bitmask) in globs - thereis (and (> (logand kind-bitmask action-bit) 0) - (funcall glob file)))) - (jsonrpc-notify - server :workspace/didChangeWatchedFiles - `(:changes ,(vector `(:uri ,(eglot-path-to-uri file) - :type ,action-type)))) - (when (and (eq action 'created) - (file-directory-p file)) - (watch-dir file))) - ((eq action 'renamed) - (handle-event `(,desc 'deleted ,file)) - (handle-event `(,desc 'created ,file1)))))) - (watch-dir (dir) - (when-let ((probe - (and (file-readable-p dir) - (or (gethash dir (eglot--file-watches server)) - (puthash dir (list (file-notify-add-watch - dir '(change) #'handle-event)) - (eglot--file-watches server)))))) - (push id (cdr probe))))) - (unwind-protect - (progn - (mapc #'watch-dir dirs-to-watch) - (setq - success - `(:message ,(format "OK, watching %s directories in %s watchers" - (length dirs-to-watch) (length watchers))))) - (unless success - (eglot-unregister-capability server method id)))))) - -(cl-defmethod eglot-unregister-capability - (server (_method (eql workspace/didChangeWatchedFiles)) id) - "Handle dynamic unregistration of workspace/didChangeWatchedFiles." - (maphash (lambda (dir watch-and-ids) - (setcdr watch-and-ids (delete id (cdr watch-and-ids))) - (when (null (cdr watch-and-ids)) - (file-notify-rm-watch (car watch-and-ids)) - (remhash dir (eglot--file-watches server)))) - (eglot--file-watches server)) - (list t "OK")) - - -;;; Glob heroics -;;; -(defun eglot--glob-parse (glob) - "Compute list of (STATE-SYM EMITTER-FN PATTERN)." - (with-temp-buffer - (save-excursion (insert glob)) - (cl-loop - with grammar = '((:** "\\*\\*/?" eglot--glob-emit-**) - (:* "\\*" eglot--glob-emit-*) - (:? "\\?" eglot--glob-emit-?) - (:{} "{[^][*{}]+}" eglot--glob-emit-{}) - (:range "\\[\\^?[^][/,*{}]+\\]" eglot--glob-emit-range) - (:literal "[^][,*?{}]+" eglot--glob-emit-self)) - until (eobp) - collect (cl-loop - for (_token regexp emitter) in grammar - thereis (and (re-search-forward (concat "\\=" regexp) nil t) - (list (cl-gensym "state-") emitter (match-string 0))) - finally (error "Glob '%s' invalid at %s" (buffer-string) (point)))))) - -(defun eglot--glob-compile (glob &optional byte-compile noerror) - "Convert GLOB into Elisp function. Maybe BYTE-COMPILE it. -If NOERROR, return predicate, else erroring function." - (let* ((states (eglot--glob-parse glob)) - (body `(with-current-buffer (get-buffer-create " *eglot-glob-matcher*") - (erase-buffer) - (save-excursion (insert string)) - (cl-labels ,(cl-loop for (this that) on states - for (self emit text) = this - for next = (or (car that) 'eobp) - collect (funcall emit text self next)) - (or (,(caar states)) - (error "Glob done but more unmatched text: '%s'" - (buffer-substring (point) (point-max))))))) - (form `(lambda (string) ,(if noerror `(ignore-errors ,body) body)))) - (if byte-compile (byte-compile form) form))) - -(defun eglot--glob-emit-self (text self next) - `(,self () (re-search-forward ,(concat "\\=" (regexp-quote text))) (,next))) - -(defun eglot--glob-emit-** (_ self next) - `(,self () (or (ignore-errors (save-excursion (,next))) - (and (re-search-forward "\\=/?[^/]+/?") (,self))))) - -(defun eglot--glob-emit-* (_ self next) - `(,self () (re-search-forward "\\=[^/]") - (or (ignore-errors (save-excursion (,next))) (,self)))) - -(defun eglot--glob-emit-? (_ self next) - `(,self () (re-search-forward "\\=[^/]") (,next))) - -(defun eglot--glob-emit-{} (arg self next) - (let ((alternatives (split-string (substring arg 1 (1- (length arg))) ","))) - `(,self () - (or (re-search-forward ,(concat "\\=" (regexp-opt alternatives)) nil t) - (error "Failed matching any of %s" ',alternatives)) - (,next)))) - -(defun eglot--glob-emit-range (arg self next) - (when (eq ?! (aref arg 1)) (aset arg 1 ?^)) - `(,self () (re-search-forward ,(concat "\\=" arg)) (,next))) - - -;;; List connections mode - -(define-derived-mode eglot-list-connections-mode tabulated-list-mode - "" "Eglot mode for listing server connections -\\{eglot-list-connections-mode-map}" - (setq-local tabulated-list-format - `[("Language server" 16) ("Project name" 16) ("Modes handled" 16)]) - (tabulated-list-init-header)) - -(defun eglot-list-connections () - "List currently active Eglot connections." - (interactive) - (with-current-buffer - (get-buffer-create "*EGLOT connections*") - (let ((inhibit-read-only t)) - (erase-buffer) - (eglot-list-connections-mode) - (setq-local tabulated-list-entries - (mapcar - (lambda (server) - (list server - `[,(or (plist-get (eglot--server-info server) :name) - (jsonrpc-name server)) - ,(eglot-project-nickname server) - ,(mapconcat #'symbol-name - (eglot--major-modes server) - ", ")])) - (cl-reduce #'append - (hash-table-values eglot--servers-by-project)))) - (revert-buffer) - (pop-to-buffer (current-buffer))))) - - -;;; Inlay hints -(defface eglot-inlay-hint-face '((t (:height 0.8 :inherit shadow))) - "Face used for inlay hint overlays.") - -(defface eglot-type-hint-face '((t (:inherit eglot-inlay-hint-face))) - "Face used for type inlay hint overlays.") - -(defface eglot-parameter-hint-face '((t (:inherit eglot-inlay-hint-face))) - "Face used for parameter inlay hint overlays.") - -(defvar-local eglot--outstanding-inlay-hints-region (cons nil nil) - "Jit-lock-calculated (FROM . TO) region with potentially outdated hints") - -(defvar-local eglot--outstanding-inlay-hints-last-region nil) - -(defvar-local eglot--outstanding-inlay-regions-timer nil - "Helper timer for `eglot--update-hints'") - -(defun eglot--update-hints (from to) - "Jit-lock function for Eglot inlay hints." - (cl-symbol-macrolet ((region eglot--outstanding-inlay-hints-region) - (last-region eglot--outstanding-inlay-hints-last-region) - (timer eglot--outstanding-inlay-regions-timer)) - (setcar region (min (or (car region) (point-max)) from)) - (setcdr region (max (or (cdr region) (point-min)) to)) - ;; HACK: We're relying on knowledge of jit-lock internals here. The - ;; condition comparing `jit-lock-context-unfontify-pos' to - ;; `point-max' is a heuristic for telling whether this call to - ;; `jit-lock-functions' happens after `jit-lock-context-timer' has - ;; just run. Only after this delay should we start the smoothing - ;; timer that will eventually call `eglot--update-hints-1' with the - ;; coalesced region. I wish we didn't need the timer, but sometimes - ;; a lot of "non-contextual" calls come in all at once and do verify - ;; the condition. Notice it is a 0 second timer though, so we're - ;; not introducing any more delay over jit-lock's timers. - (when (= jit-lock-context-unfontify-pos (point-max)) - (if timer (cancel-timer timer)) - (let ((buf (current-buffer))) - (setq timer (run-at-time - 0 nil - (lambda () - (eglot--when-live-buffer buf - ;; HACK: In some pathological situations - ;; (Emacs's own coding.c, for example), - ;; jit-lock is calling `eglot--update-hints' - ;; repeatedly with same sequence of - ;; arguments, which leads to - ;; `eglot--update-hints-1' being called with - ;; the same region repeatedly. This happens - ;; even if the hint-painting code does - ;; nothing else other than widen, narrow, - ;; move point then restore these things. - ;; Possible Emacs bug, but this fixes it. - (unless (equal last-region region) - (eglot--update-hints-1 (max (car region) (point-min)) - (min (cdr region) (point-max))) - (setq last-region region)) - (setq region (cons nil nil) - timer nil))))))))) - -(defun eglot--update-hints-1 (from to) - "Do most work for `eglot--update-hints', including LSP request." - (let* ((buf (current-buffer)) - (paint-hint - (eglot--lambda ((InlayHint) position kind label paddingLeft paddingRight) - (goto-char (eglot--lsp-position-to-point position)) - (when (or (> (point) to) (< (point) from)) (cl-return)) - (let* ((left-pad (and paddingLeft - (not (eq paddingLeft :json-false)) - (not (memq (char-before) '(32 9))) " ")) - (right-pad (and paddingRight - (not (eq paddingRight :json-false)) - (not (memq (char-after) '(32 9))) " ")) - (peg-after-p (eql kind 1))) - (cl-labels - ((make-ov () - (if peg-after-p - (make-overlay (point) (1+ (point)) nil t) - (make-overlay (1- (point)) (point) nil nil nil))) - (do-it (label lpad rpad i n) - (let* ((firstp (zerop i)) - (tweak-cursor-p (and firstp peg-after-p)) - (ov (make-ov)) - (text (concat lpad label rpad))) - (when tweak-cursor-p (put-text-property 0 1 'cursor 1 text)) - (overlay-put ov (if peg-after-p 'before-string 'after-string) - (propertize - text - 'face (pcase kind - (1 'eglot-type-hint-face) - (2 'eglot-parameter-hint-face) - (_ 'eglot-inlay-hint-face)))) - (overlay-put ov 'priority (if peg-after-p i (- n i))) - (overlay-put ov 'eglot--inlay-hint t) - (overlay-put ov 'evaporate t) - (overlay-put ov 'eglot--overlay t)))) - (if (stringp label) (do-it label left-pad right-pad 0 1) - (cl-loop - for i from 0 for ldetail across label - do (eglot--dbind ((InlayHintLabelPart) value) ldetail - (do-it value - (and (zerop i) left-pad) - (and (= i (1- (length label))) right-pad) - i (length label)))))))))) - (jsonrpc-async-request - (eglot--current-server-or-lose) - :textDocument/inlayHint - (list :textDocument (eglot--TextDocumentIdentifier) - :range (list :start (eglot--pos-to-lsp-position from) - :end (eglot--pos-to-lsp-position to))) - :success-fn (lambda (hints) - (eglot--when-live-buffer buf - (eglot--widening - ;; Overlays ending right at FROM with an - ;; `after-string' property logically belong to - ;; the (FROM TO) region. Likewise, such - ;; overlays ending at TO don't logically belong - ;; to it. - (dolist (o (overlays-in (1- from) to)) - (when (and (overlay-get o 'eglot--inlay-hint) - (cond ((eq (overlay-end o) from) - (overlay-get o 'after-string)) - ((eq (overlay-end o) to) - (overlay-get o 'before-string)) - (t))) - (delete-overlay o))) - (mapc paint-hint hints)))) - :deferred 'eglot--update-hints-1))) - -(define-minor-mode eglot-inlay-hints-mode - "Minor mode for annotating buffers with LSP server's inlay hints." - :global nil - (cond (eglot-inlay-hints-mode - (if (eglot-server-capable :inlayHintProvider) - (jit-lock-register #'eglot--update-hints 'contextual) - (eglot-inlay-hints-mode -1))) - (t - (jit-lock-unregister #'eglot--update-hints) - (remove-overlays nil nil 'eglot--inlay-hint t)))) - - -;;; Hacks -;;; -;; Emacs bug#56407, the optimal solution is in desktop.el, but that's -;; harder. For now, use `with-eval-after-load'. See also github#1183. -(with-eval-after-load 'desktop - (add-to-list 'desktop-minor-mode-handlers '(eglot--managed-mode . ignore)) - (add-to-list 'desktop-minor-mode-handlers '(eglot-inlay-hints-mode . ignore))) - - -;;; Misc -;;; -;;;###autoload -(progn - (put 'eglot--debbugs-or-github-bug-uri 'bug-reference-url-format t) - (defun eglot--debbugs-or-github-bug-uri () - (format (if (string= (match-string 2) "github") - "https://github.com/joaotavora/eglot/issues/%s" - "https://debbugs.gnu.org/%s") - (match-string 3)))) - -(provide 'eglot) - - -;; Local Variables: -;; bug-reference-bug-regexp: "\\(\\(github\\|bug\\)#\\([0-9]+\\)\\)" -;; bug-reference-url-format: eglot--debbugs-or-github-bug-uri -;; checkdoc-force-docstrings-flag: nil -;; End: - -;;; eglot.el ends here blob - 85fef6be5532e99cb1dd0811682d519c43089bb0 (mode 644) blob + /dev/null --- elpa/eglot-1.17/eglot.texi +++ /dev/null @@ -1,1595 +0,0 @@ -\input texinfo @c -*-texinfo-*- -@c %**start of header -@setfilename ../../eglot.info -@settitle Eglot: The Emacs Client for the Language Server Protocol -@include docstyle.texi -@syncodeindex vr cp -@syncodeindex fn cp -@c %**end of header - -@copying -This manual is for Eglot, the Emacs LSP client. - -Copyright @copyright{} 2022--2024 Free Software Foundation, Inc. - -@quotation -Permission is granted to copy, distribute and/or modify this document -under the terms of the GNU Free Documentation License, Version 1.3 or -any later version published by the Free Software Foundation; with no -Invariant Sections, with the Front-Cover Texts being ``A GNU Manual'', -and with the Back-Cover Texts as in (a) below. A copy of the license -is included in the section entitled ``GNU Free Documentation License''. - -(a) The FSF's Back-Cover Text is: ``You have the freedom to copy and -modify this GNU manual.'' -@end quotation -@end copying - -@dircategory Emacs misc features -@direntry -* Eglot: (eglot). Language Server Protocol client for Emacs. -@end direntry - -@titlepage -@sp 4 -@c The title is printed in a large font. -@center @titlefont{User's Guide} -@sp 1 -@center @titlefont{to} -@sp 1 -@center @titlefont{Eglot: The Emacs LSP Client} -@ignore -@sp 2 -@center release 1.8 -@c -release- -@end ignore -@sp 3 -@center Jo@~ao T@'avora & Eli Zaretskii -@c -date- - -@page -@vskip 0pt plus 1filll -@insertcopying -@end titlepage - -@contents - -@ifnottex -@node Top -@top Eglot - -@cindex LSP -@cindex language server protocol -Eglot is the Emacs client for the @dfn{Language Server Protocol} -(@acronym{LSP}). The name ``Eglot'' is an acronym that stands for -@ifhtml -``@emph{E}macs Poly@emph{glot}''. -@end ifhtml -@ifnothtml -``Emacs polyGLOT''. -@end ifnothtml -@footnote{ -A @dfn{polyglot} is a -person who is able to use several languages. -} Eglot provides infrastructure and a set of commands for enriching -the source code editing capabilities of Emacs via LSP@. LSP is a -standardized communications protocol between source code editors (such -as Emacs) and language servers---programs external to Emacs which -analyze the source code on behalf of Emacs. The protocol allows Emacs -to receive various source code services from the server, such as -description and location of function calls, types of variables, class -definitions, syntactic errors, etc. This way, Emacs doesn't need to -implement the language-specific parsing and analysis capabilities in -its own code, but is still capable of providing sophisticated editing -features that rely on such capabilities, such as automatic code -completion, go-to definition of function/class, documentation of -symbol at-point, refactoring, on-the-fly diagnostics, and more. - -Eglot itself is completely language-agnostic, but it can support any -programming language for which there is a language server and an Emacs -major mode. - -This manual documents how to configure, use, and customize Eglot. - -@insertcopying - -@menu -* Quick Start:: For the impatient. -* Eglot and LSP Servers:: How to work with language servers. -* Using Eglot:: Important Eglot commands and variables. -* Customizing Eglot:: Eglot customization and advanced features. -* Advanced server configuration:: Fine-tune a specific language server -* Extending Eglot:: Writing Eglot extensions in Elisp -* Troubleshooting Eglot:: Troubleshooting and reporting bugs. -* GNU Free Documentation License:: The license for this manual. -* Index:: -@end menu -@end ifnottex - -@node Quick Start -@chapter Quick Start -@cindex quick start - -This chapter provides concise instructions for setting up and using -Eglot with your programming project in common usage scenarios. For -more detailed instructions regarding Eglot setup, @pxref{Eglot and LSP -Servers}. @xref{Using Eglot}, for detailed description of using Eglot, -and see @ref{Customizing Eglot}, for adapting Eglot to less common use -patterns. - -Here's how to start using Eglot with your programming project: - -@enumerate -@item -Select and install a language server. - -Eglot comes pre-configured with many popular language servers, see the -value of @code{eglot-server-programs}. If the server(s) mentioned -there satisfy your needs for the programming language(s) with which -you want to use Eglot, you just need to make sure those servers are -installed on your system. Alternatively, install one or more servers -of your choice and add them to the value of -@code{eglot-server-programs}, as described in @ref{Setting Up LSP -Servers}. - -@item -Turn on Eglot for your project. - -To start using Eglot for a project, type @kbd{M-x eglot @key{RET}} in -a buffer visiting any file that belongs to the project. This starts -the language server configured for the programming language of that -buffer, and causes Eglot to start @dfn{managing} file-visiting buffers -related to that programming language. This includes files that are -already visited at the time the @code{eglot} command is invoked, as -well as any files visited after this invocation. - -The notion of a ``project'' used by Eglot is the same Emacs uses -(@pxref{Projects,,, emacs, GNU Emacs Manual}): in the simplest case, -the ``project'' is the single file you are editing, but it can also be -all the files in a single directory or a directory tree under some -version control system, such as Git. - -There are alternate ways of starting Eglot; see @ref{Starting Eglot} -for details. - -@item -Use Eglot. - -Most Eglot facilities are integrated into Emacs features, such as -ElDoc, Flymake, Xref, and Imenu. However, Eglot also provides -commands of its own, mainly to perform tasks by the language server, -such as @kbd{M-x eglot-rename} (to rename an identifier across the -entire project), @kbd{M-x eglot-format} (to reformat and reindent -code), and some others. @xref{Eglot Commands}, for the detailed list -of Eglot commands. - -@item -That's it! -@end enumerate - -@node Eglot and LSP Servers -@chapter Eglot and LSP Servers - -This chapter describes how to set up Eglot for your needs, and how to -start it. - -@menu -* Setting Up LSP Servers:: How to configure LSP servers for your needs. -* Starting Eglot:: Ways of starting Eglot for your project. -* Shutting Down LSP Servers:: -@end menu - -@node Setting Up LSP Servers -@section Setting Up LSP Servers -@cindex setting up LSP server for Eglot -@cindex LSP server for Eglot, setting up -@cindex language server for Eglot - -For Eglot to be useful, it must first be combined with a suitable -language server. Usually, that means running the server program -locally as a child process of Emacs (@pxref{Processes,,, elisp, GNU -Emacs Lisp Reference Manual}) and communicating with it via the -standard input and output streams. - -The language server program must be installed separately, and is not -further discussed in this manual; refer to the documentation of the -particular server(s) you want to install. - -To use a language server, Eglot must know how to start it and which -programming languages each server supports. This information is -provided by the variable @code{eglot-server-programs}. - -@defvar eglot-server-programs -This variable associates major modes with names and command-line -arguments of the language server programs corresponding to the -programming language of each major mode. It provides all the -information that Eglot needs to know about the programming language of -the source you are editing. - -The value of the variable is an alist, whose elements are of the form -@w{@code{(@var{major-mode} . @var{server})}}. - -The @var{major-mode} of the alist elements can be either a symbol of -an Emacs major mode or a list of the form @w{@code{(@var{mode} -:language-id @var{id})}}, with @var{mode} being a major-mode symbol -and @var{id} a string that identifies the language to the server (if -Eglot cannot by itself convert the major-mode to the language -identifier string required by the server). In addition, -@var{major-mode} can be a list of several major modes specified in one -of the above forms -- this means a running instance of the associated -server is responsible for files of multiple major modes or languages -in the project. - -The @var{server} part of the alist elements can be one of the -following: - -@table @code -@item (@var{program} @var{args}@dots{}) -This says to invoke @var{program} with zero or more arguments -@var{args}; the program is expected to communicate with Emacs via the -standard input and standard output streams. - -@item (@var{program} @var{args}@dots{} :initializationOptions @var{options}@dots{}) -@var{program} is invoked with @var{args} but @var{options} specifies -how to construct the @samp{:initializationOptions} JSON object to pass -the server on during the LSP handshake (@pxref{Advanced server -configuration}). - -@item (@var{host} @var{port} @var{args}@dots{}) -Here @var{host} is a string and @var{port} is a positive integer -specifying a TCP connection to a remote server. The @var{args} are -passed to @code{open-network-stream}, e.g.@: if the connection needs -to use encryption or other non-default parameters (@pxref{Network,,, -elisp, GNU Emacs Lisp Reference Manual}). - -@item (@var{program} @var{args}@dots{} :autoport @var{moreargs}@dots{}) -@var{program} is started with a command line constructed from -@var{args} followed by an available server port and the rest of -arguments in @var{moreargs}; Eglot then establishes a TCP connection -with the server via that port on the local host. - -@item @var{function} -This should be a function of a single argument: non-@code{nil} if the -connection was requested interactively (e.g., by the @code{eglot} -command), otherwise @code{nil}. The function should return a value of -any of the forms described above. This allows interaction with the -user for determining the program to start and its command-line -arguments. -@end table - -@end defvar - -Eglot comes with a fairly complete set of associations of major-modes -to popular language servers predefined. If you need to add server -associations to the default list, use @code{add-to-list}. For -example, if there is a hypothetical language server program -@command{fools} for the language @code{Foo} which is supported by an -Emacs major-mode @code{foo-mode}, you can add it to the alist like -this: - -@lisp -(with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(foo-mode . ("fools" "--stdio")))) -@end lisp - -This will invoke the program @command{fools} with the command-line -argument @option{--stdio} in support of editing source files for which -Emacs turns on @code{foo-mode}, and will communicate with the program -via the standard streams. As usual with invoking programs, the -executable file @file{fools} should be in one of the directories -mentioned by the @code{exec-path} variable (@pxref{Subprocess -Creation,,, elisp, GNU Emacs Lisp Reference Manual}), for Eglot to be -able to find it. - -Sometimes, multiple servers are acceptable alternatives for handling a -given major-mode. In those cases, you may combine the helper function -@code{eglot-alternatives} with the functional form of -@code{eglot-server-programs}. - -@lisp -(with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - `(foo-mode . ,(eglot-alternatives - '(("fools" "--stdio") - ("phewls" "--fast")))))) -@end lisp - -If you have @command{fools} and @command{phewls} installed, the -function produced by @code{eglot-alternatives} will prompt for the -server to use in @code{foo-mode} buffers. Else it will use whichever -is available. - -@node Starting Eglot -@section Starting Eglot -@cindex starting Eglot -@cindex activating Eglot for a project - -@findex eglot -The most common way to start Eglot is to simply visit a source file of -a given language and use the command @kbd{M-x eglot}. This starts the -language server suitable for the visited file's major-mode, and -attempts to connect to it. If the connection to the language server -is successful, you will see the @code{[eglot:@var{project}]} indicator -on the mode line which reflects the server that was started. If the -server program couldn't be started or connection to it failed, you -will see an error message; in that case, try to troubleshoot the -problem as described in @ref{Troubleshooting Eglot}. Once a language -server was successfully started and Eglot connected to it, you can -immediately start using the Emacs features supported by Eglot, as -described in @ref{Eglot Features}. - -A single Eglot session for a certain major-mode usually serves all the -buffers under that mode which visit files from the same project, so -you don't need to invoke @kbd{M-x eglot} again when you visit another -file from the same project which is edited using the same major-mode. -This is because Eglot uses the Emacs project infrastructure, as -described in @ref{Eglot and Buffers}, and this knows about files that -belong to the same project. Thus, after starting an Eglot session for -some buffer, that session is automatically reused when visiting files -in the same project with the same major-mode. - -@findex eglot-ensure -Alternatively, you could configure Eglot to start automatically for -one or more major-modes from the respective mode hooks. Here's an -example for a hypothetical @code{foo-mode}: - -@lisp - (add-hook 'foo-mode-hook 'eglot-ensure) -@end lisp - -@noindent -The function @code{eglot-ensure} will start an Eglot session for each -buffer in which @code{foo-mode} is turned on, if there isn't already -an Eglot session that handles the buffer. Note that this variant of -starting an Eglot session is non-interactive, so it should be used -only when you are confident that Eglot can be started reliably for any -file which may be visited with the major-mode in question. - -Note that it's often difficult to establish this confidence fully, so -it may be wise to use the interactive command @code{eglot} instead. -You only need to invoke it once per project, as all other files -visited within the same project will automatically be managed with no -further user intervention needed. - -When Eglot connects to a language server for the first time in an -Emacs session, it runs the hook @code{eglot-connect-hook} -(@pxref{Eglot Variables}). - -@node Shutting Down LSP Servers -@section Shutting Down LSP Servers -@cindex shutting down LSP server - -When Eglot is turned on, it arranges for turning itself off -automatically if the language server process terminates. Turning off -Eglot means that it shuts down the server connection, ceases its -management of all the buffers that use the server connection which was -terminated, deactivates its minor mode, and restores the original -values of the Emacs variables that Eglot changed when it was turned -on. @xref{Eglot and Buffers}, for more details of what Eglot -management of a buffer entails. - -@findex eglot-shutdown -You can also shut down a language server manually, by using the -command @kbd{M-x eglot-shutdown}. This prompts for the server (unless -there's only one connection and it's used in the current buffer), and -then shuts it down. By default, it also kills the server's events -buffer (@pxref{Troubleshooting Eglot}), but a prefix argument prevents -that. - -Alternatively, you can customize the variable -@code{eglot-autoshutdown} to a non-@code{nil} value, in which case -Eglot will automatically shut down the language server process when -the last buffer served by that language server is killed. The default -of this variable is @code{nil}, so that visiting another file would -automatically activate Eglot even when the project which started Eglot -with the server no longer has any buffer associated with it. This -default allows you to start a server only once in each Emacs session. - -@node Using Eglot -@chapter Using Eglot - -This chapter describes in detail the features that Eglot provides and -how it does that. It also provides reference sections for Eglot -commands and variables. - -@menu -* Eglot Features:: -* Eglot and Buffers:: -* Eglot Commands:: -* Eglot Variables:: -@end menu - -@node Eglot Features -@section Eglot Features -@cindex features in buffers supported by Eglot - -While Eglot is enabled in a buffer, it is said to be @dfn{managing} -it, using LSP and the specific capabilities of the language server to -activate and enhance modern IDE features in Emacs. Some of these -features are provided via other Emacs packages, and some via Eglot -directly (@pxref{Eglot Commands}). - -Here's an overview of the main features that Eglot provides: - -@itemize @bullet -@item -At-point documentation: when point is at or near a symbol or an -identifier, the information about the symbol/identifier, such as the -signature of a function or class method and server-generated -diagnostics, is made available via the ElDoc package -(@pxref{Programming Language Doc,,, emacs, GNU Emacs Manual}). This -allows major modes to provide extensive help and documentation about -the program identifiers. - -@item -On-the-fly diagnostic annotations, via the Flymake package -(@pxref{Top,,, flymake, GNU Flymake manual}). Eglot's Flymake backend -replaces other Flymake backends while it is managing a buffer, and -enhances diagnostics with interactive server-suggested fixes -(so-called @dfn{code actions}, @pxref{Eglot Commands}) - -@item -Finding definitions and uses of identifiers, via Xref (@pxref{Xref,,, -emacs, GNU Emacs Manual}). Eglot provides a backend for the Xref -capabilities which uses the language-server understanding of the -program source. In particular, it eliminates the need to generate -tags tables (@pxref{Tags tables,,, emacs, GNU Emacs Manual}) for -languages which are only supported by the @code{etags} backend. - -@item -Buffer navigation by name of function, class, method, etc., via Imenu -(@pxref{Imenu,,, emacs, GNU Emacs Manual}). Eglot provides its own -variant of @code{imenu-create-index-function}, which generates the -index for the buffer based on language-server program source analysis. - -@item -Enhanced completion of symbol at point by the -@code{completion-at-point} command (@pxref{Symbol Completion,,, emacs, -GNU Emacs Manual}). This uses the language-server's parser data for -the completion candidates. - -@item -Automatic reformatting of source code as you type it. This is similar -to what the @code{eglot-format} command does (see below), but is -activated automatically as you type. - -@item -If a completion package such as @code{company-mode}, a popular -third-party completion package (or any other completion package), is -installed, Eglot enhances it by providing completion candidates based -on the language-server analysis of the source code. -(@code{company-mode} can be installed from GNU ELPA.) - -@item -If @code{yasnippet}, a popular third-party package for automatic -insertion of code templates (snippets), is installed, and the language -server supports snippet completion candidates, Eglot arranges for the -completion package to instantiate these snippets using -@code{yasnippet}. (@code{yasnippet} can be installed from GNU ELPA.) - -@item -If the popular third-party package @code{markdown-mode} is installed, -and the server provides at-point documentation formatted as Markdown -in addition to plain text, Eglot arranges for the ElDoc package to -enrich this text with fontifications and other nice formatting before -displaying it to the user. This makes the documentation shown by -ElDoc look nicer on display. - -@item -In addition to enabling and enhancing other features and packages, -Eglot also provides a number of user commands based on the -capabilities of language servers. Examples include renaming symbols -with @kbd{eglot-rename} and asking to automatically correct problems -with @kbd{eglot-code-actions}. @xref{Eglot Commands}. -@end itemize - -Not all servers support the full set of LSP capabilities, but most of -them support enough to enable the basic set of features mentioned -above. - -Conversely, some servers offer capabilities for which no equivalent -Emacs package exists yet, and so Eglot cannot (yet) expose these -capabilities to Emacs users. However, @xref{Extending Eglot}. - -Finally, it's worth noting that, by default, Eglot generally turns on -all features that it @emph{can} turn on. It's possible to opt out of -some features via user options (@pxref{Customizing Eglot}) and a hook -that runs after Eglot starts managing a buffer (@pxref{Eglot and -Buffers}). - -@node Eglot and Buffers -@section Buffers, Projects, and Eglot -@cindex buffers managed by Eglot -@cindex projects and Eglot - -@cindex workspace -One of the main strong points of using a language server is that a -language server has a broad view of the program: it considers more -than just the single source file you are editing. Ideally, the -language server should know about all the source files of your program -which are written in the language supported by the server. In the -language-server parlance, the set of the source files of a program is -known as a @dfn{workspace}. The Emacs equivalent of a workspace is a -@dfn{project} (@pxref{Projects,,, emacs, GNU Emacs Manual}). Eglot -fully supports Emacs projects, and considers the file in whose buffer -Eglot is turned on as belonging to a project. In the simplest case, -that file is the entire project, i.e.@: your project consists of a -single file. But there are other more complex projects: - -@itemize @bullet -@item -A single-directory project: several source files in a single common -directory. - -@item -A VC project: source files in a directory hierarchy under some VCS, -e.g.@: a VCS repository (@pxref{Version Control,,, emacs, GNU Emacs -Manual}). - -@item -An EDE project: source files in a directory hierarchy managed via the -Emacs Development Environment (@pxref{EDE,,, emacs, GNU Emacs -Manual}). -@end itemize - -Eglot uses Emacs's project management infrastructure to figure out -which files and buffers belong to what project, so any kind of project -supported by that infrastructure is automatically supported by Eglot. - -When Eglot starts a server program, it does so in the project's root -directory, which is usually the top-level directory of the project's -directory hierarchy. This ensures the language server has the same -comprehensive view of the project's files as you do. - -For example, if you visit the file @file{~/projects/fooey/lib/x.foo} -and @file{x.foo} belongs to a project rooted at -@file{~/projects/fooey} (perhaps because a @file{.git} directory -exists there), then @kbd{M-x eglot} causes the server program to start -with that root as the current working directory. The server then will -analyze not only the file @file{lib/x.foo} you visited, but likely -also all the other @file{*.foo} files under the -@file{~/projects/fooey} directory. - -In some cases, additional information specific to a given project will -need to be provided to the language server when starting it. The -variable @code{eglot-workspace-configuration} (@pxref{Customizing -Eglot}) exists for that purpose. It specifies the parameters and -their values to communicate to each language server which needs that. - -When Eglot is active for a project, it performs several background -activities on behalf of the project and its buffers: - -@itemize @bullet -@cindex mode-line indication of language server -@cindex mouse clicks on mode-line, and Eglot -@vindex eglot-menu -@item -All of the project's file-visiting buffers under the same major-mode -are served by a single language-server connection. (If the project -uses several programming languages, there will usually be a separate -server connection for each group of files written in the same language -and using the same Emacs major-mode.) Eglot adds the -@samp{[eglot:@var{project}]} indication to the mode line of -each such buffer, where @var{server} is the name of the server and -@var{project} identifies the project by its root directory. Clicking -the mouse on the Eglot mode-line indication activates a menu with -server-specific items. - -@item -For each buffer in which Eglot is active, it notifies the language -server that Eglot is @dfn{managing} the file visited by that buffer. -This tells the language server that the file's contents on disk may no -longer be up-to-date due to unsaved edits. Eglot reports to the -server any changes in the text of each managed buffer, to make the -server aware of unsaved changes. This includes your editing of the -buffer and also changes done automatically by other Emacs features and -commands. Killing a buffer relinquishes its management by Eglot and -notifies the server that the file on disk is up-to-date. - -@vindex eglot-managed-mode-hook -@vindex eglot-managed-p -@item -Eglot turns on a special minor mode in each buffer it manages. This -minor mode ensures the server is notified about files Eglot manages, -and also arranges for other Emacs features supported by Eglot -(@pxref{Eglot Features}) to receive information from the language -server, by changing the settings of these features. Unlike other -minor-modes, this special minor mode is not activated manually by the -user, but automatically, as the result of starting an Eglot session -for the buffer. However, this minor mode provides a hook variable -@code{eglot-managed-mode-hook} that can be used to customize the Eglot -management of the buffer. This hook is run both when the minor mode -is turned on and when it's turned off; use the variable -@code{eglot-managed-p} to tell if current buffer is still being -managed or not. When Eglot stops managing the buffer, this minor mode -is turned off, and all the settings that Eglot changed are restored to -their original values. - -@item -When you visit a file under the same project, whether an existing or a -new file, its buffer is automatically added to the set of buffers -managed by Eglot, and the server which supports the buffer's -major-mode is notified about that. Thus, visiting a non-existent file -@file{/home/joe/projects/fooey/lib/y.foo} in the above example will -notify the server of the @file{*.foo} files' language that a new file -was added to the project, even before the file appears on disk. The -special Eglot minor mode is also turned on automatically in the buffer -visiting the file. -@end itemize - -@node Eglot Commands -@section Eglot Commands -@cindex commands, Eglot - -This section provides a reference for the most commonly used Eglot -commands: - -@ftable @code -@item M-x eglot -This command adds the current buffer and the file it visits to the -group of buffers and files managed by Eglot on behalf of a suitable -language server. If a language server for the buffer's -@code{major-mode} (@pxref{Major Modes,,, emacs, GNU Emacs Manual}) is -not yet running, it will be started; otherwise the buffer and its file -will be added to those managed by an existing server session. - -The command attempts to figure out the buffer's major mode and the -suitable language server; in case it fails, it might prompt for the -major mode to use and for the server program to start. If invoked -with @kbd{C-u}, it always prompts for the server program, and if -invoked with @kbd{C-u C-u}, it also prompts for the major mode. - -If the language server is successfully started and contacted, this -command arranges for any other buffers belonging to the same project -and using the same major mode to use the same language-server session. -That includes any buffers created by visiting files after this command -succeeds to connect to a language server. - -All the Emacs features that are capable of using Eglot services -(@pxref{Eglot Features}) are automatically configured by this command -to start using the language server via Eglot. To customize which -Emacs features will be configured to use Eglot, use the -@code{eglot-stay-out-of} option (@pxref{Customizing Eglot}). - -@item M-x eglot-reconnect -This command shuts down the current connection to the language -server and immediately restarts it using the same options used -originally. This can sometimes be useful to unclog a partially -malfunctioning server connection. - -@item M-x eglot-shutdown -This command shuts down a language server. It prompts for a language -server to shut down (unless there's only one server session, and it -manages the current buffer). Then the command shuts down the server -and stops managing the buffers the server was used for. Emacs -features (@pxref{Eglot Features}) that Eglot configured to work with -the language server are restored back to their original configuration. - -Normally, this command kills the buffers used for communicating with -the language server, but if invoked with a prefix argument @kbd{C-u}, -the command doesn't kill those buffers, allowing them to be used for -diagnostics and problem reporting (@pxref{Troubleshooting Eglot}). - -@item M-x eglot-shutdown-all -This command shuts down all the language servers active in the current -Emacs session. As with @code{eglot-shutdown}, invoking this command -with a prefix argument avoids killing the buffers used for -communications with the language servers. - -@item M-x eglot-rename -This command renames the program symbol (a.k.a.@: @dfn{identifier}) at -point to another name. It prompts for the new name of the symbol, and -then modifies all the files in the project which are managed by the -language server of the current buffer to implement the renaming. - -@item M-x eglot-format -This command reformats the active region according to the -language-server rules. If no region is active, it reformats the -entire current buffer. - -@item M-x eglot-format-buffer -This command reformats the current buffer, in the same manner as -@code{eglot-format} does. - -@cindex code actions -@item M-x eglot-code-actions -@itemx M-x eglot-code-action-organize-imports -@itemx M-x eglot-code-action-quickfix -@itemx M-x eglot-code-action-extract -@itemx M-x eglot-code-action-inline -@itemx M-x eglot-code-action-rewrite -These commands allow you to invoke the so-called @dfn{code actions}: -requests for the language server to provide editing commands for -correcting, refactoring or beautifying your code. These commands may -affect more than one visited file belonging to the project. - -The command @code{eglot-code-actions} asks the server if there are any -code actions for any point in the buffer or contained in the active -region. If there are, you have the choice to execute one of them via -the minibuffer. - -A common use of code actions is fixing the Flymake error diagnostics -issued by Eglot (@pxref{Top,,, flymake, GNU Flymake manual}). -Clicking on a diagnostic with @kbd{mouse-2} invokes -@code{eglot-code-actions-at-mouse} which pops up a menu of available -code actions. The variable @code{eglot-diagnostics-map} can be used -to control the mouse binding. - -Other commands execute a specific code action. For example, -@code{eglot-code-action-organize-imports} rearranges the program's -@dfn{imports}---declarations of modules whose capabilities the program -uses. - -@cindex inlay hints -@item M-x eglot-inlay-hints-mode -This command toggles LSP @dfn{inlay hints} on and off for the current -buffer. Inlay hints are small text annotations to specific parts of -the whole buffer, not unlike diagnostics, but designed to help -readability instead of indicating problems. For example, a C++ -language server can serve hints about positional parameter names in -function calls and a variable's automatically deduced type. Inlay -hints help the user not have to remember these things by heart. -@end ftable - -The following Eglot commands are used less commonly, mostly for -diagnostic and troubleshooting purposes: - -@ftable @code -@item M-x eglot-events-buffer -This command pops up the events buffer used for communication with the -language server of the current buffer. - -@item M-x eglot-stderr-buffer -This command pops up the buffer with the debug info printed by the -language server to its standard error stream. - -@item M-x eglot-forget-pending-continuations -Forget pending requests for the server of the current buffer. -@c FIXME: Better description of the need. - -@item M-x eglot-signal-didChangeConfiguration -This command updates the language server configuration according to -the current value of the variable @code{eglot-workspace-configuration} -(@pxref{Customizing Eglot}). - -@item M-x eglot-clear-status -Clear the last JSONRPC error for the server of the current buffer. -Eglot keeps track of erroneous situations encountered by the server in -its mode-line indication so that the user may inspect the -communication leading up to it (@pxref{Troubleshooting Eglot}). If -the situation is deemed uninteresting or temporary, this command can -be used to ``forget'' the error. Note that the command @code{M-x -eglot-reconnect} can sometimes be used to unclog a temporarily -malfunctioning server. -@end ftable - -As described in @ref{Eglot Features} most features associated with -Eglot are actually provided by other Emacs packages and features, and -Eglot only enhances them by allowing them to use the information -coming from the language servers. For completeness, here's the list -of commands of those other packages that are very commonly used in -Eglot-managed buffers: - -@c Not @ftable, because the index entries should mention Eglot -@table @code -@cindex eldoc, and Eglot -@cindex documentation using Eglot -@item M-x eldoc -Ask the ElDoc system for help at point. - -@cindex flymake, and Eglot -@cindex on-the-fly diagnostics using Eglot -@item M-x flymake-show-buffer-diagnostics -Ask Flymake system to display diagnostics for the current buffer. - -@item M-x flymake-show-project-diagnostics -Ask Flymake to list diagnostics for all the files in the current -project. - -@cindex xref, and Eglot -@cindex finding definitions of identifiers using Eglot -@item M-x xref-find-definitions -Ask Xref to go the definition of the identifier at point. - -@cindex imenu navigation using Eglot -@item M-x imenu -Let the user navigate the program source code using buffer index, -categorizing program elements by syntactic class (class, method, -variable, etc.) and offering completion. - -@cindex symbol completion using Eglot -@item M-x completion-at-point -Request completion of the symbol at point. -@end table - -@node Eglot Variables -@section Eglot Variables -@cindex variables, Eglot - -This section provides a reference for the Eglot user options. - -@vtable @code -@item eglot-autoreconnect -This option controls the ability to reconnect automatically to the -language server when Eglot detects that the server process terminated -unexpectedly. The default value @code{3} means to attempt reconnection only -if the previous successful connection lasted for more than that number -of seconds; a different positive value changes the minimal length of -the connection to trigger reconnection. A value of @code{t} means -always reconnect automatically, and @code{nil} means never reconnect -(in which case you will need to reconnect manually using @kbd{M-x -eglot}). - -@item eglot-connect-timeout -This specifies the number of seconds before connection attempt to a -language server times out. The value of @code{nil} means never time -out. The default is 30 seconds. - -@item eglot-sync-connect -This setting is mainly important for connections which are slow to -establish. Whereas the variable @code{eglot-connect-timeout} controls -how long to wait for, this variable controls whether to block Emacs's -user interface while waiting. The default value is @code{3}; a positive -value means block for that many seconds, then wait for the connection -in the background. The value of @code{t} means block during the whole -waiting period. The value of @code{nil} or @code{0} means don't block at -all during the waiting period. - -@item eglot-events-buffer-config -This configures the size and format of the Eglot events buffer. -@xref{Eglot Commands, eglot-events-buffer}, for how to access that -buffer. If the value is changed, the connection should be restarted -using @kbd{M-x eglot-reconnect} for the new value to take effect. -@c FIXME: Shouldn't the defcustom do this by itself using the :set -@c attribute? Maybe not because reconnecting is a complex task. -@xref{Troubleshooting Eglot}, for when this could be useful. - -@item eglot-autoshutdown -If this is non-@code{nil}, Eglot shuts down a language server when the -last buffer managed by it is killed. @xref{Shutting Down LSP Servers}. -The default is @code{nil}; if you want to shut down a server, use -@kbd{M-x eglot-shutdown} (@pxref{Eglot Commands}). - -@item eglot-confirm-server-edits -Various Eglot commands and code actions result in the language server -sending editing commands to Emacs. If this option's value is -non-@code{nil}, Eglot will ask for confirmation before performing -edits proposed by the language server. This option's value can be -crafted to require this confirmation for specific commands or only -when the edit affects files not yet visited by the user. Consult this -option's docstring for more information. - -@item eglot-ignored-server-capabilities -This variable's value is a list of language server capabilities that -Eglot should not use. The default is @code{nil}: Eglot uses all of -the capabilities supported by each server. - -@item eglot-extend-to-xref -If this is non-@code{nil}, and @kbd{M-.} -(@code{xref-find-definitions}) lands you in a file outside of your -project, such as a system-installed library or header file, -transiently consider that file as managed by the same language server. -That file is still outside your project (i.e. @code{project-find-file} -won't find it), but Eglot and the server will consider it to be part -of the workspace. The default is @code{nil}. - -@item eglot-mode-map -This variable is the keymap for binding Eglot-related command. It is -in effect only as long as the buffer is managed by Eglot. By default, -it is empty, with the single exception: @kbd{C-h .} is remapped to -invoke @code{eldoc-doc-buffer}. You can bind additional commands in -this map. For example: - -@lisp - (define-key eglot-mode-map (kbd "C-c r") 'eglot-rename) - (define-key eglot-mode-map (kbd "C-c o") 'eglot-code-action-organize-imports) - (define-key eglot-mode-map (kbd "C-c h") 'eldoc) - (define-key eglot-mode-map (kbd "") 'xref-find-definitions) -@end lisp - -@end vtable - -Additional variables, which are relevant for customizing the server -connections, are documented in @ref{Customizing Eglot}. - -@node Customizing Eglot -@chapter Customizing Eglot -@cindex customizing Eglot - -Eglot itself has a relatively small number of customization options. -A large part of customizing Eglot to your needs and preferences should -actually be done via options of the Emacs packages and features which -Eglot supports and enhances (@pxref{Eglot Features}). For example: - -@itemize @bullet -@item -To configure the face used for server-derived errors and warnings, -customize the Flymake faces @code{flymake-error} and -@code{flymake-warning}. - -@item -To configure the amount of space taken up by documentation in the -echo area, customize the ElDoc variable -@code{eldoc-echo-area-use-multiline-p}. - -@item -To completely change how ElDoc displays the at-point documentation -destination, customize the ElDoc variable -@code{eldoc-display-functions}. -@end itemize - -For this reason, this manual describes only how to customize -Eglot's own operation, which mainly has to do with the server -connections and the server features to be used by Eglot. - -@c @table, not @vtable, because some of the variables are indexed -@c elsewhere -@table @code -@item eglot-server-programs -This variable determines which language server to start for each -supported major mode, and how to invoke that server's program. -@xref{Setting Up LSP Servers}, for the details. - -@vindex eglot-strict-mode -@item eglot-strict-mode -This is @code{nil} by default, meaning that Eglot is generally lenient -about non-conforming servers. If you need to debug a server, set this -to @w{@code{(disallow-non-standard-keys enforce-required-keys)}}. - -@vindex eglot-server-initialized-hook -@item eglot-server-initialized-hook -A hook run after the server object is successfully initialized. - -@vindex eglot-connect-hook -@item eglot-connect-hook -A hook run after connection to the server is successfully -established. @xref{Starting Eglot}. - -@item eglot-managed-mode-hook -A hook run after Eglot started or stopped managing a buffer. -@xref{Eglot and Buffers}, for details of its usage. - -@vindex eglot-stay-out-of -@item eglot-stay-out-of -This variable's value lists Emacs features that Eglot shouldn't -automatically try to manage on the user's behalf. It is useful, for -example, when you need to use non-LSP Flymake or Company back-ends. -To have Eglot stay away from some Emacs feature, add that feature's -symbol or a regexp that will match a symbol's name to the list: for -example, the symbol @code{xref} to leave Xref alone, or the string -@samp{company} to stay away from your Company customizations. Here's an -example: - -@lisp -(add-to-list 'eglot-stay-out-of 'flymake) -@end lisp - -Note that you can still configure the excluded Emacs features manually -to use Eglot in your @code{eglot-managed-mode-hook} or via some other -mechanism. - -@vindex eglot-report-progress -@cindex progress -@item eglot-report-progress -Set this variable to true if you'd like progress notifications coming -from the language server to be handled as Emacs's progress reporting -facilities. -@end table - -@node Advanced server configuration -@chapter Advanced server configuration - -Though many language servers work well out-of-the-box, most allow -fine-grained control of their operation via specific configuration -options that are transmitted over the LSP protocol and vary from -server to server. A small number of servers require such special -configuration to work acceptably, or even to work at all. - -After having setup a server executable program in -@code{eglot-server-programs} (@pxref{Setting Up LSP Servers}) and -ensuring Eglot can invoke it, you may want to take advantage of some -of these options. You should first distinguish two main kinds of -server configuration: - -@itemize @bullet -@item -Project-specific, applying to a specific project; - -@item -User-specific, applying to all projects the server is used for. -@end itemize - -When you have decided which kind you need, the following sections -teach how Eglot's user variables can be used to achieve it: - -@menu -* Project-specific configuration:: -* User-specific configuration:: -* JSONRPC objects in Elisp:: -@end menu - -It's important to note that not all servers allow both kinds of -configuration, nor is it guaranteed that user options can be copied -over to project options, and vice-versa. When in doubt, consult your -language server's documentation. - -It's also worth noting that some language servers can read these -settings from configuration files in the user's @code{HOME} directory -or in a project's directory. For example, the @command{pylsp} Python -server reads the file @file{~/.config/pycodestyle} for user -configuration. The @command{clangd} C/C++ server reads both -@file{~/.config/clangd/config.yaml} for user configuration and -@file{.clangd} for project configuration. It may be advantageous to -use these mechanisms instead of Eglot's, as this will probably work -with other LSP clients and may be easier to debug than options riding -on the LSP wire. - -@node Project-specific configuration -@section Project-specific configuration -@vindex eglot-workspace-configuration -@cindex workspace configuration - -To set project-specific settings, which the LSP specification calls -@dfn{workspace configuration}, the variable -@code{eglot-workspace-configuration} may be used. - -This variable is a directory-local variable (@pxref{Directory -Variables, , Per-directory Local Variables, emacs, The GNU Emacs -Manual}). It's important to recognize that this variable really only -makes sense when set directory-locally. It usually does not make -sense to set it file-locally or in a major-mode hook. - -The most common way to set @code{eglot-workspace-configuration } is -using a @file{.dir-locals.el} file in the root of your project. If -you can't do that, you may also set it from Elisp code via the -@code{dir-locals-set-class-variables} function. (@pxref{Directory -Local Variables,,, elisp, GNU Emacs Lisp Reference Manual}). - -However you choose to set it, the variable's value is a plist -(@pxref{Property Lists,,, elisp, GNU Emacs Lisp Reference Manual}) with -the following format: - -@lisp - (@var{:server1} @var{plist1} @var{:server2} @var{plist2} @dots{}) -@end lisp - -@noindent -Here, @var{:server1} and @var{:server2} are keywords whose names -identify the LSP language servers to target. Consult server -documentation to find out what name to use. @var{plist1} and -@var{plist2} are plists of options, possibly nesting other plists. - -@findex eglot-show-workspace-configuration -When experimenting with workspace settings, you can use the command -@kbd{M-x eglot-show-workspace-configuration} to inspect and debug the -value of this variable in its final JSON form, ready to be sent to the -server (@pxref{JSONRPC objects in Elisp}). This helper command works -even before actually connecting to the server. - -These variable's value doesn't take effect immediately. That happens -upon establishing the connection, in response to an explicit query -from the server, or when issuing the command @kbd{M-x -eglot-signal-didChangeConfiguration} which notifies the server during -an ongoing Eglot session. - -@subsection Examples - -For some users, setting @code{eglot-workspace-configuration} is a -somewhat daunting task. One of the reasons is having to manage the -general Elisp syntax of per-mode directory-local variables, which uses -alists (@pxref{Association Lists,,, elisp, GNU Emacs Lisp Reference -Manual}), and the specific syntax of Eglot's variable, which uses -plists. Some examples are useful. - -Let's say you want to configure two language servers to be used in a -project written in a combination of the Python and Go languages. You -want to use the @command{pylsp} and @command{gopls} languages -servers. In the documentation of the servers in question (or in some -other editor's configuration file, or in some blog article), you find -the following configuration options in informal dotted-notation -syntax: - -@example -pylsp.plugins.jedi_completion.include_params: true -pylsp.plugins.jedi_completion.fuzzy: true -pylsp.pylint.enabled: false -gopls.usePlaceholders: true -@end example - -To apply this to Eglot, and assuming you chose the -@file{.dir-locals.el} file method, the contents of that file could be: - -@lisp -((nil - . ((eglot-workspace-configuration - . (:pylsp (:plugins (:jedi_completion (:include_params t - :fuzzy t) - :pylint (:enabled :json-false))) - :gopls (:usePlaceholders t))))) - (python-base-mode . ((indent-tabs-mode . nil))) - (go-mode . ((indent-tabs-mode . t)))) -@end lisp - -@noindent -This sets the value of @code{eglot-workspace-configuration} in all the -buffers inside the project; each server will use only the section of -the parameters intended for that server, and ignore the rest. Note -how alists are used for associating Emacs mode names with alists -associating variable names with variable values. Then notice how -plists are used inside the value of -@code{eglot-workspace-configuration}. - -This following form may also be used: - -@lisp -((python-base-mode - . ((eglot-workspace-configuration - . (:pylsp (:plugins (:jedi_completion (:include_params t - :fuzzy t) - :pylint (:enabled :json-false))))) - (indent-tabs-mode . nil))) - (go-mode - . ((eglot-workspace-configuration - . (:gopls (:usePlaceholders t))) - (indent-tabs-mode . t)))) -@end lisp - -@noindent -This sets up the value of @code{eglot-workspace-configuration} -separately depending on the major mode of each of that project's -buffers. @code{python-base-mode} buffers will have the variable set to -@code{(:pylsp (:plugins ...))}. @code{go-mode} buffers will have the -variable set to @code{(:gopls (:usePlaceholders t))}. - -Some servers will issue workspace configuration for specific files -inside your project. For example, if you know @code{gopls} is asking -about specific files in the @code{src/imported} subdirectory and you -want to set a different option for @code{gopls.usePlaceholders} , you -may use something like: - -@lisp -((python-base-mode - . ((eglot-workspace-configuration - . (:pylsp (:plugins (:jedi_completion (:include_params t - :fuzzy t) - :pylint (:enabled :json-false))))) - (indent-tabs-mode nil))) - (go-mode - . ((eglot-workspace-configuration - . (:gopls (:usePlaceholders t))) - (indent-tabs-mode t))) - ("src/imported" - . ((eglot-workspace-configuration - . (:gopls (:usePlaceholders nil)))))) -@end lisp - -Finally, if one needs to determine the workspace configuration based -on some dynamic context, @code{eglot-workspace-configuration} can be -set to a function. The function is called with the -@code{eglot-lsp-server} instance of the connected server (if any) and -with @code{default-directory} set to the root of the project. The -function should return a plist suitable for use as the variable's -value. - -@node User-specific configuration -@section User-specific configuration -@cindex initializationOptions -@cindex command-line arguments - -This kind of configuration applies to all projects the server is used -for. Here, there are a number of ways to do this inside Eglot. - -A common way is to pass command-line options to the server invocation -via @code{eglot-server-programs}. Let's say we want to configure -where the @command{clangd} server reads its -@code{compile_commands.json} from. This can be done like so: - -@lisp -(with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - `(c++-mode . ("clangd" "--compile-commands-dir=/tmp")))) - -@end lisp - -@noindent -Another way is to have Eglot pass a JSON object to the server during -the LSP handshake. This is done using the -@code{:initializationOptions} syntax of @code{eglot-server-programs}: - -@lisp -(with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - `(c++-mode . ("clangd" :initializationOptions - (:compilationDatabasePath "/tmp"))))) -@end lisp - -@noindent -The argument @code{(:compilationDatabasePath "/tmp")} is Emacs's -representation in plist format of a simple JSON object -@code{@{"compilationDatabasePath": "/tmp"@}}. To learn how to -represent more deeply nested options in this format, @pxref{JSONRPC -objects in Elisp}. - -In this case, the two examples achieve exactly the same, but notice -how the option's name has changed between them. - -@vindex eglot-workspace-configuration -Finally there is another way to do user-specific configuration of -language servers, which may be used if the methods above are not -supported. It consists of @emph{globally} setting -@code{eglot-workspace-configuration}, a variable originally intended -for project-specific configuration. This has the same effect as -giving all your projects a certain default configuration, as described -in @ref{Project-specific configuration}. Here is an example: - -@lisp -(setq-default eglot-workspace-configuration - '(:pylsp (:plugins (:jedi_completion (:include_params t - :fuzzy t) - :pylint (:enabled :json-false))) - :gopls (:usePlaceholders t))) -@end lisp - -Note that the global value of @code{eglot-workspace-configuration} is -always overridden if a directory-local value is detected. - -@node JSONRPC objects in Elisp -@section JSONRPC objects in Elisp - -Emacs's preferred way of representing JSON is via Lisp lists. In -Eglot, the syntax of this list is the simplest possible (the one with -fewer parenthesis), a plist (@pxref{Property Lists,,, elisp, GNU Emacs -Lisp Reference Manual}). - -The plist may be arbitrarily complex, and generally containing other -keyword-value property sub-plists corresponding to JSON sub-objects. - -For representing the JSON leaf values @code{true}, @code{false}, -@code{null} and @code{@{@}}, you can use the Lisp values @code{t}, -@code{:json-false}, @code{nil}, and @code{eglot-@{@}}, respectively. -JSON arrays are represented as Elisp vectors surrounded by square brackets -(@pxref{Vectors,,,elisp,GNU Emacs Lisp Reference Manual}). - -For example, the plist - -@lisp -(:pylsp (:plugins (:jedi_completion (:include_params t - :fuzzy t - :cache_for ["pandas" "numpy"]) - :pylint (:enabled :json-false))) - :gopls (:usePlaceholders t)) -@end lisp - -@noindent -is serialized by Eglot to the following JSON text: - -@example -@{ - "pylsp": @{ - "plugins": @{ - "jedi_completion": @{ - "include_params": true, - "fuzzy": true, - "cache_for": [ "pandas", "numpy" ] - @}, - "pylint": @{ - "enabled": false - @} - @} - @}, - "gopls": @{ - "usePlaceholders": true - @} -@} -@end example - -@node Extending Eglot -@chapter Extending Eglot - -Sometimes it may be useful to extend existing Eglot functionality -using Elisp its public methods. A good example of when this need may -arise is adding support for a custom LSP protocol extension only -implemented by a specific server. - -The best source of documentation for this is probably Eglot source -code itself, particularly the section marked ``API''. - -Most of the functionality is implemented with Common-Lisp style -generic functions (@pxref{Generics,,,eieio,EIEIO}) that can be easily -extended or overridden. The Eglot code itself is an example on how to -do this. - -The following is a relatively simple example that adds support for the -@code{inactiveRegions} experimental feature introduced in version 17 -of the @command{clangd} C/C++ language server++. - -Summarily, the feature works by first having the server detect the -Eglot's advertisement of the @code{inactiveRegions} client capability -during startup, whereupon the language server will report a list of -regions of inactive code for each buffer. This is usually code -surrounded by C/C++ @code{#ifdef} macros that the preprocessor removes -based on compile-time information. - -The language server reports the regions by periodically sending a -@code{textDocument/inactiveRegions} notification for each managed -buffer (@pxref{Eglot and Buffers}). Normally, unknown server -notifications are ignored by Eglot, but we're going change that. - -Both the announcement of the client capability and the handling of the -new notification is done by adding methods to generic functions. - -@itemize @bullet -@item -The first method extends @code{eglot-client-capabilities} using a -simple heuristic to detect if current server is @command{clangd} and -enables the @code{inactiveRegion} capability. - -@lisp -(cl-defmethod eglot-client-capabilities :around (server) - (let ((base (cl-call-next-method))) - (when (cl-find "clangd" (process-command - (jsonrpc--process server)) - :test #'string-match) - (setf (cl-getf (cl-getf base :textDocument) - :inactiveRegionsCapabilities) - '(:inactiveRegions t))) - base)) -@end lisp - -Notice we use an internal function of the @code{jsonrpc.el} library, -and a regexp search to detect @command{clangd}. An alternative would -be to define a new EIEIO subclass of @code{eglot-lsp-server}, maybe -called @code{eglot-clangd}, so that the method would be simplified: - -@lisp -(cl-defmethod eglot-client-capabilities :around ((_s eglot-clangd)) - (let ((base (cl-call-next-method))) - (setf (cl-getf (cl-getf base :textDocument) - :inactiveRegionsCapabilities) - '(:inactiveRegions t)))) -@end lisp - -However, this would require that users tweak -@code{eglot-server-program} to tell Eglot instantiate such sub-classes -instead of the generic @code{eglot-lsp-server} (@pxref{Setting Up LSP -Servers}). For the purposes of this particular demonstration, we're -going to use the more hacky regexp route which doesn't require that. - -Note, however, that detecting server versions before announcing new -capabilities is generally not needed, as both server and client are -required by LSP to ignore unknown capabilities advertised by their -counterparts. - -@item -The second method implements @code{eglot-handle-notification} to -process the server notification for the LSP method -@code{textDocument/inactiveRegions}. For each region received it -creates an overlay applying the @code{shadow} face to the region. -Overlays are recreated every time a new notification of this kind is -received. - -To learn about how @command{clangd}'s special JSONRPC notification -message is structured in detail you could consult that server's -documentation. Another possibility is to evaluate the first -capability-announcing method, reconnect to the server and peek in the -events buffer (@pxref{Eglot Commands, eglot-events-buffer}). You -could find something like: - -@lisp -[server-notification] Mon Sep 4 01:10:04 2023: -(:jsonrpc "2.0" :method "textDocument/inactiveRegions" :params - (:textDocument - (:uri "file:///path/to/file.cpp") - :regions - [(:start (:character 0 :line 18) - :end (:character 58 :line 19)) - (:start (:character 0 :line 36) - :end (:character 1 :line 38))])) -@end lisp - -This reveals that the @code{textDocument/inactiveRegions} notification -contains a @code{:textDocument} property to designate the managed -buffer and an array of LSP regions under the @code{:regions} property. -Notice how the message (originally in JSON format), is represented as -Elisp plists (@pxref{JSONRPC objects in Elisp}). - -The Eglot generic function machinery will automatically destructure -the incoming message, so these two properties can simply be added to -the new method's lambda list as @code{&key} arguments. Also, the -@code{eglot-uri-to-path} and @code{eglot-range-region} may be used to -easily parse the LSP @code{:uri} and @code{:start ... :end ...} -objects to obtain Emacs objects for file names and positions. - -The remainder of the implementation consists of standard Elisp -techniques to loop over arrays, manage buffers and overlays. - -@lisp -(cl-defmethod eglot-handle-notification - (_server (_method (eql textDocument/inactiveRegions)) - &key regions textDocument &allow-other-keys) - (if-let* ((path (expand-file-name (eglot-uri-to-path - (cl-getf textDocument :uri)))) - (buffer (find-buffer-visiting path))) - (with-current-buffer buffer - (remove-overlays nil nil 'inactive-code t) - (cl-loop - for r across regions - for (beg . end) = (eglot-range-region r) - for ov = (make-overlay beg end) - do - (overlay-put ov 'face 'shadow) - (overlay-put ov 'inactive-code t))))) -@end lisp - -@end itemize - -After evaluating these two additions and reconnecting to the -@command{clangd} language server (version 17), the result will be that -all the inactive code in the buffer will be nicely grayed out using -the LSP server knowledge about current compile time preprocessor -defines. - -@node Troubleshooting Eglot -@chapter Troubleshooting Eglot -@cindex troubleshooting Eglot - -This chapter documents commands and variables that can be used to -troubleshoot Eglot problems. It also provides guidelines for -reporting Eglot bugs in a way that facilitates their resolution. - -When you encounter problems with Eglot, try first using the commands -@kbd{M-x eglot-events-buffer} and @kbd{M-x eglot-stderr-buffer}. They -pop up special buffers that can be used to inspect the communications -between the Eglot and language server. In many cases, this will -indicate the problems or at least provide a hint. - -@menu -* Performance:: -* Getting the latest version:: -* Reporting bugs:: -@end menu - -@node Performance -@section Performance -@cindex performance problems, with Eglot -A common and easy-to-fix cause of performance problems in Eglot -(especially in older versions) is its events buffer, since it -represents additional work that Eglot must do (@pxref{Eglot Commands, -eglot-events-buffer}). If you find Eglot is operating correctly but -slowly, try to customize the variable -@code{eglot-events-buffer-config} (@pxref{Eglot Variables}) and set -its @code{:size} property to 0. This will disable recording any -events and may speed things up. - -In other situations, the cause of poor performance lies in the -language server itself. Servers use aggressive caching and other -techniques to improve their performance. Often, this can be tweaked -by changing the server configuration (@pxref{Advanced server -configuration}). - -@node Getting the latest version -@section Getting the latest version -@cindex upgrading Eglot - -To install the latest Eglot in an Emacs version that does not bundle -Eglot, use @kbd{M-x package-install}. - -Often, a newer Eglot version exists that has fixed a longstanding bug, -has more LSP features, or just better supports a particular language -server. Recent Eglot versions can self-update via the command -@kbd{M-x eglot-upgrade-eglot}. This will replace any currently -installed version with the newest one available from the ELPA archives -configured in @code{package-archives}. - -You can also update Eglot through other methods, such as -@code{use-package} (@pxref{Installing packages,,, use-package, -use-package User Manual}), @code{package-install}, -@code{list-packages} or the newer @code{package-upgrade} -(@pxref{Packages,,, emacs, GNU Emacs Manual}). However, do read the -docstrings of the command you intend to use before you use it, as some -of them may not work in exactly the same way across Emacs versions, -meaning your configuration may be not portable. - -@node Reporting bugs -@section Reporting bugs -@cindex bug reports - -If you think you have found a bug, we want to hear about it. Before -reporting a bug, keep in mind that interaction with language servers -represents a large quantity of unknown variables. Therefore, it is -generally both @emph{difficult} and @emph{absolutely essential} that -the maintainers reproduce bugs exactly as they happened to you, the -user. - -To report an Eglot bug, send e-mail to @email{bug-gnu-emacs@@gnu.org}. - -To understand how to write this email, get acquainted with Emacs's bug -reporting guidelines (@pxref{Bugs,,, emacs, GNU Emacs Manual}). Then, -follow this Eglot-specific checklist: - -@enumerate -@item -Include the transcript of JSONRPC events obtained from the buffer -popped up by @kbd{M-x eglot-events-buffer}. You may narrow down the -transcript if you are sure of where the problematic exchange is, but -it's safer to include the whole transcript, either attached or inline. - -@item -If Emacs signaled an error (an error message was seen or heard), make -sure to repeat the process after turning on @code{debug-on-error} via -@kbd{M-x toggle-debug-on-error}. This normally produces a backtrace -of the error that should also be attached to the bug report. - -@item -Include a description of how the maintainer should obtain, install, -and configure the language server you used. Maintainers usually have -access to GNU/Linux systems, though not necessarily the distribution -that you may be using. If possible, try to replicate the problem with -the C/C@t{++} or Python servers, as these are very easy to install. - -@item -Describe how to setup a @emph{minimal} project directory where Eglot -should be started for the problem to happen. Describe each file's -name and its contents. Alternatively, you can supply the address of a -public Git repository. - -@item -Include versions of the software used. The Emacs version can be -obtained with @kbd{M-x emacs-version}. - -We welcome bug reports about all Eglot versions, but it is helpful to -first check if the problem isn't already fixed in the latest version -(@pxref{Getting the latest version}). - -It's also essential to include the version of ELPA packages that are -explicitly or implicitly loaded. The optional but popular Company or -Markdown packages are distributed as GNU ELPA packages, not to mention -Eglot itself in some situations. Some major modes (Go, Rust, etc.) -are provided by ELPA packages. It's sometimes easy to miss these, -since they are usually implicitly loaded when visiting a file in that -language. - -ELPA packages usually live in @code{~/.emacs.d/elpa} (or what is in -@code{package-user-dir}). Including a listing of files in that -directory is a way to tell the maintainers about ELPA package -versions. - -@item -Include a recipe to replicate the problem with @emph{a clean Emacs -run}. The invocation @code{emacs -Q -f package-initialize} starts -Emacs with no configuration and initializes the ELPA packages. A very -minimal @file{.emacs} initialization file (10 lines or less) is also -acceptable and good means to describe changes to variables. - -There is usually no need to include @code{require} statements in the -recipe, as Eglot's functionality uses autoloads. - -Likewise, there is rarely the need to use things like -@code{use-package} or @code{eglot-ensure}. This just makes the recipe -harder to follow. Prefer setting variables with @code{setq} and -adding to hooks with @code{add-hook}. Prefer starting Eglot with -@code{M-x eglot}. - -@item -Make sure to double check all the above elements and re-run the recipe -to see that the problem is reproducible. Following the recipe should -produce event transcript and error backtraces that are very similar to -the ones you included. If the problem only happens sometimes, mention -this in your report. -@end enumerate - -Please keep in mind that some problems reported against Eglot may -actually be bugs in the language server or the Emacs feature/package -that used Eglot to communicate with the language server. Eglot is, in -many cases, just a frontend to that functionality. - -@node GNU Free Documentation License -@appendix GNU Free Documentation License -@include doclicense.texi - -@node Index -@unnumbered Index -@printindex cp - -@bye blob - 652fcad0b63099cdbf6a7f112314d6f1c420d848 (mode 644) blob + /dev/null --- elpa/eglot-1.17.signed +++ /dev/null @@ -1,2 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2024-01-25T23:05:02+0100 using RSA -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2024-01-25T23:05:02+0100 using EDDSA \ No newline at end of file blob - 30e4c4f7b46f8c299745be79f681714cb482e660 (mode 644) blob + /dev/null --- elpa/eldoc-1.15.0/eldoc-autoloads.el +++ /dev/null @@ -1,83 +0,0 @@ -;;; eldoc-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from eldoc.el - -(defvar eldoc-minor-mode-string (purecopy " ElDoc") "\ -String to display in mode line when ElDoc Mode is enabled; nil for none.") -(custom-autoload 'eldoc-minor-mode-string "eldoc" t) -(autoload 'eldoc-mode "eldoc" "\ -Toggle echo area display of Lisp objects at point (ElDoc mode). - -ElDoc mode is a buffer-local minor mode. When enabled, the echo -area displays information about a function or variable in the -text where point is. If point is on a documented variable, it -displays the first line of that variable's doc string. Otherwise -it displays the argument list of the function called in the -expression point is on. - -This is a minor mode. If called interactively, toggle the `Eldoc - mode' mode. If the prefix argument is positive, enable the - mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable - the mode if ARG is nil, omitted, or is a positive number. - Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, - evaluate `eldoc-mode'. - -The mode's hook is called both when the mode is enabled and when - it is disabled. - -(fn &optional ARG)" t) -(put 'global-eldoc-mode 'globalized-minor-mode t) -(defcustom global-eldoc-mode t "\ -Non-nil if Global Eldoc mode is enabled. -See the `global-eldoc-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `global-eldoc-mode'." :set #'custom-set-minor-mode :initialize 'custom-initialize-delay :type 'boolean) -(custom-autoload 'global-eldoc-mode "eldoc" nil) -(autoload 'global-eldoc-mode "eldoc" "\ -Toggle Eldoc mode in all buffers. -With prefix ARG, enable Global Eldoc mode if ARG is positive; -otherwise, disable it. - -If called from Lisp, toggle the mode if ARG is `toggle'. -Enable the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -Eldoc mode is enabled in all buffers where `turn-on-eldoc-mode' would -do it. - -See `eldoc-mode' for more information on Eldoc mode. - -(fn &optional ARG)" t) -(autoload 'turn-on-eldoc-mode "eldoc" "\ -Turn on `eldoc-mode' if the buffer has ElDoc support enabled. -See `eldoc-documentation-strategy' for more detail.") -(register-definition-prefixes "eldoc" '("eldoc")) - -;;; End of scraped data - -(provide 'eldoc-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; eldoc-autoloads.el ends here blob - 67d0eab9ae284e2b3f804d0c8d7d0b3a3dcce9cf (mode 644) blob + /dev/null --- elpa/eldoc-1.15.0/eldoc-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from eldoc.el -*- no-byte-compile: t -*- -(define-package "eldoc" "1.15.0" "Show function arglist or variable docstring in echo area" '((emacs "26.3")) :commit "dc744fe6f3cd185bd9d29f61b08cd4c524e3969e" :url "https://elpa.gnu.org/packages/eldoc.html" :authors '(("Noah Friedman" . "friedman@splode.com")) :maintainer '("Noah Friedman" . "friedman@splode.com") :keywords '("extensions")) blob - e28d73c3555c986c67797036db243fc5c19a90fe (mode 644) blob + /dev/null --- elpa/eldoc-1.15.0/eldoc.el +++ /dev/null @@ -1,1007 +0,0 @@ -;;; eldoc.el --- Show function arglist or variable docstring in echo area -*- lexical-binding:t; -*- - -;; Copyright (C) 1996-2023 Free Software Foundation, Inc. - -;; Author: Noah Friedman -;; Keywords: extensions -;; Created: 1995-10-06 -;; Version: 1.15.0 -;; Package-Requires: ((emacs "26.3")) - -;; This is a GNU ELPA :core package. Avoid functionality that is not -;; compatible with the version of Emacs recorded above. - -;; This file is part of GNU Emacs. - -;; GNU Emacs 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 of the License, or -;; (at your option) any later version. - -;; GNU Emacs 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. If not, see . - -;;; Commentary: - -;; This program was inspired by the behavior of the "mouse documentation -;; window" on many Lisp Machine systems; as you type a function's symbol -;; name as part of a sexp, it will print the argument list for that -;; function. Behavior is not identical; for example, you need not actually -;; type the function name, you need only move point around in a sexp that -;; calls it. Also, if point is over a documented variable, it will print -;; the one-line documentation for that variable instead, to remind you of -;; that variable's meaning. - -;; This mode is now enabled by default in all major modes that provide -;; support for it, such as `emacs-lisp-mode'. -;; This is controlled by `global-eldoc-mode'. - -;; Major modes for other languages may use ElDoc by adding an -;; appropriate function to the buffer-local value of -;; `eldoc-documentation-functions'. - -;;; Code: - -(eval-when-compile (require 'cl-lib)) - -(defgroup eldoc nil - "Show function arglist or variable docstring in echo area." - :group 'lisp - :group 'extensions) - -(defcustom eldoc-idle-delay 0.50 - "Number of seconds of idle time to wait before displaying documentation. -If user input arrives before this interval of time has elapsed after the -last input event, no documentation will be displayed. - -If this variable is set to 0, display the documentation without any delay." - :type 'number) - -(defcustom eldoc-print-after-edit nil - "If non-nil, eldoc info is only shown after editing commands. -Changing the value requires toggling `eldoc-mode'." - :type 'boolean - :version "24.4") - -(defcustom eldoc-echo-area-display-truncation-message t - "If non-nil, provide verbose help when a message has been truncated. -When this is non-nil, and the documentation string was truncated to -fit in the echo-area, the documentation will be followed by an -explanation of how to display the full documentation text. -If nil, truncated messages will just have \"...\" to indicate truncation." - :type 'boolean - :version "28.1") - -;;;###autoload -(defcustom eldoc-minor-mode-string (purecopy " ElDoc") - "String to display in mode line when ElDoc Mode is enabled; nil for none." - :type '(choice string (const :tag "None" nil))) - -(defcustom eldoc-argument-case #'identity - "Case to display argument names of functions, as a symbol. -This has two preferred values: `upcase' or `downcase'. -Actually, any name of a function which takes a string as an argument and -returns another string is acceptable. - -Note that this variable has no effect, unless -`eldoc-documentation-strategy' handles it explicitly." - :type '(radio (function-item upcase) - (function-item downcase) - function)) -(make-obsolete-variable 'eldoc-argument-case nil "25.1") - -(defcustom eldoc-echo-area-use-multiline-p 'truncate-sym-name-if-fit - "Allow long ElDoc doc strings to resize echo area display. -If the value is t, never attempt to truncate messages, even if the -echo area must be resized to fit. In that case, Emacs will resize -the mini-window up to the limit set by `max-mini-window-height'. - -If the value is a positive number, it is used to calculate a -number of screen lines of documentation that ElDoc is allowed to -put in the echo area. A positive integer specifies the maximum -number of lines directly, while a floating-point number specifies -the number of screen lines as a fraction of the echo area frame's -height. - -If the value is the symbol `truncate-sym-name-if-fit', the part of -the doc string that represents a symbol's name may be truncated -if it will enable the rest of the doc string to fit on a single -line, without resizing the echo area. - -If the value is nil, a doc string is always truncated to fit in a -single screen line of echo-area display. - -Any resizing of the echo area additionally respects -`max-mini-window-height'." - :type '(radio (const :tag "Always" t) - (float :tag "Fraction of frame height" 0.25) - (integer :tag "Number of lines" 5) - (const :tag "Never" nil) - (const :tag "Yes, but ask major-mode to truncate - symbol names if it will\ enable argument list to fit on one - line" truncate-sym-name-if-fit))) - -(defcustom eldoc-echo-area-prefer-doc-buffer nil - "Prefer ElDoc's documentation buffer if it is displayed in some window. -If this variable's value is t, ElDoc will skip showing -documentation in the echo area if the dedicated documentation -buffer (displayed by `eldoc-doc-buffer') is already displayed in -some window. If the value is the symbol `maybe', then the echo area -is only skipped if the documentation needs to be truncated there." - :type '(choice (const :tag "Prefer ElDoc's documentation buffer" t) - (const :tag "Prefer echo area" nil) - (const :tag "Skip echo area if truncating" maybe)) - :version "28.1") - -(defface eldoc-highlight-function-argument - '((t (:inherit bold))) - "Face used for the argument at point in a function's argument list. -Note that this face has no effect unless the `eldoc-documentation-strategy' -handles it explicitly.") - -;;; No user options below here. - -(defvar eldoc-message-commands-table-size 31 - "Used by `eldoc-add-command' to initialize `eldoc-message-commands' obarray. -It should probably never be necessary to do so, but if you -choose to increase the number of buckets, you must do so before loading -this file since the obarray is initialized at load time. -Remember to keep it a prime number to improve hash performance.") - -(defvar eldoc-message-commands - ;; Don't define as `defconst' since it would then go to (read-only) purespace. - (make-vector eldoc-message-commands-table-size 0) - "Commands after which it is appropriate to print in the echo area. -ElDoc does not try to print function arglists, etc., after just any command, -because some commands print their own messages in the echo area and these -functions would instantly overwrite them. But `self-insert-command' as well -as most motion commands are good candidates. -This variable contains an obarray of symbols; do not manipulate it -directly. Instead, use `eldoc-add-command' and `eldoc-remove-command'.") - -;; Not a constant. -(defvar eldoc-last-data (make-vector 3 nil) - ;; Don't define as `defconst' since it would then go to (read-only) purespace. - "Bookkeeping; elements are as follows: - 0 - contains the last symbol read from the buffer. - 1 - contains the string last displayed in the echo area for variables, - or argument string for functions. - 2 - `function' if function args, `variable' if variable documentation.") -(make-obsolete-variable 'eldoc-last-data "use your own instead" "25.1") - -(defvar eldoc-last-message nil) - -(defvar eldoc-timer nil "ElDoc's timer object.") - -(defvar eldoc-current-idle-delay eldoc-idle-delay - "Idle time delay currently in use by timer. -This is used to determine if `eldoc-idle-delay' is changed by the user.") - -(defvar eldoc-message-function #'eldoc-minibuffer-message - "The function used by `eldoc--message' to display messages. -It should receive the same arguments as `message'.") - -(defun eldoc-edit-message-commands () - "Return an obarray containing common editing commands. - -When `eldoc-print-after-edit' is non-nil, ElDoc messages are only -printed after commands contained in this obarray." - (let ((cmds (make-vector 31 0)) - (re (regexp-opt '("delete" "insert" "edit" "electric" "newline")))) - (mapatoms (lambda (s) - (and (commandp s) - (string-match-p re (symbol-name s)) - (intern (symbol-name s) cmds))) - obarray) - cmds)) - - -;;;###autoload -(define-minor-mode eldoc-mode - "Toggle echo area display of Lisp objects at point (ElDoc mode). - -ElDoc mode is a buffer-local minor mode. When enabled, the echo -area displays information about a function or variable in the -text where point is. If point is on a documented variable, it -displays the first line of that variable's doc string. Otherwise -it displays the argument list of the function called in the -expression point is on." :lighter eldoc-minor-mode-string - (setq eldoc-last-message nil) - (cond - ((not (eldoc--supported-p)) - (when (called-interactively-p 'any) - (message "There is no ElDoc support in this buffer")) - (setq eldoc-mode nil)) - (eldoc-mode - (when eldoc-print-after-edit - (setq-local eldoc-message-commands (eldoc-edit-message-commands))) - (add-hook 'post-command-hook #'eldoc-schedule-timer nil t) - (add-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area nil t)) - (t - (kill-local-variable 'eldoc-message-commands) - (remove-hook 'post-command-hook #'eldoc-schedule-timer t) - (remove-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area t) - (when eldoc-timer - (cancel-timer eldoc-timer) - (setq eldoc-timer nil))))) - -;;;###autoload -(define-globalized-minor-mode global-eldoc-mode eldoc-mode turn-on-eldoc-mode - :initialize 'custom-initialize-delay - :init-value t - ;; For `read--expression', the usual global mode mechanism of - ;; `change-major-mode-hook' runs in the minibuffer before - ;; `eldoc-documentation-strategy' is set, so `turn-on-eldoc-mode' - ;; does nothing. Configure and enable eldoc from - ;; `eval-expression-minibuffer-setup-hook' instead. - (if global-eldoc-mode - (add-hook 'eval-expression-minibuffer-setup-hook - #'eldoc--eval-expression-setup) - (remove-hook 'eval-expression-minibuffer-setup-hook - #'eldoc--eval-expression-setup))) - -(defun eldoc--eval-expression-setup () - ;; Setup `eldoc', similar to `emacs-lisp-mode'. FIXME: Call - ;; `emacs-lisp-mode' itself? - (cond ((<= emacs-major-version 27) - (declare-function elisp-eldoc-documentation-function "elisp-mode") - (with-no-warnings - (add-function :before-until (local 'eldoc-documentation-function) - #'elisp-eldoc-documentation-function))) - (t (add-hook 'eldoc-documentation-functions - #'elisp-eldoc-var-docstring nil t) - (add-hook 'eldoc-documentation-functions - #'elisp-eldoc-funcall nil t) - (setq-local eldoc-documentation-strategy - 'eldoc-documentation-default))) - (eldoc-mode +1)) - -;;;###autoload -(defun turn-on-eldoc-mode () - "Turn on `eldoc-mode' if the buffer has ElDoc support enabled. -See `eldoc-documentation-strategy' for more detail." - (when (eldoc--supported-p) - (eldoc-mode 1))) - - -(defun eldoc-schedule-timer () - "Ensure `eldoc-timer' is running. - -If the user has changed `eldoc-idle-delay', update the timer to -reflect the change." - (or (and eldoc-timer - (memq eldoc-timer timer-idle-list)) ;FIXME: Why? - (setq eldoc-timer - (run-with-idle-timer - eldoc-idle-delay nil - (lambda () - (when (or eldoc-mode - (and global-eldoc-mode - (eldoc--supported-p))) - ;; Don't ignore, but also don't full-on signal errors - (with-demoted-errors "eldoc error: %s" - (eldoc-print-current-symbol-info)) ))))) - - ;; If user has changed the idle delay, update the timer. - (cond ((not (= eldoc-idle-delay eldoc-current-idle-delay)) - (setq eldoc-current-idle-delay eldoc-idle-delay) - (timer-set-idle-time eldoc-timer eldoc-idle-delay t)))) - -(defvar eldoc-mode-line-string nil) -(put 'eldoc-mode-line-string 'risky-local-variable t) - -(defun eldoc-minibuffer-message (format-string &rest args) - "Display message specified by FORMAT-STRING and ARGS on the mode-line as needed. -This function displays the message produced by formatting ARGS -with FORMAT-STRING on the mode line when the current buffer is a minibuffer. -Otherwise, it displays the message like `message' would." - (if (or (bound-and-true-p edebug-mode) (minibufferp)) - (progn - (add-hook 'post-command-hook #'eldoc-minibuffer--cleanup) - (with-current-buffer - (window-buffer - (or (window-in-direction 'above (minibuffer-window)) - (minibuffer-selected-window) - (get-largest-window))) - (when (and mode-line-format - (not (and (listp mode-line-format) - (assq 'eldoc-mode-line-string mode-line-format)))) - (setq mode-line-format - (list "" '(eldoc-mode-line-string - (" " eldoc-mode-line-string " ")) - mode-line-format))) - (setq eldoc-mode-line-string - (when (stringp format-string) - (apply #'format-message format-string args))) - (force-mode-line-update))) - (apply #'message format-string args))) - -(defun eldoc-minibuffer--cleanup () - (unless (or (bound-and-true-p edebug-mode) (minibufferp)) - (setq eldoc-mode-line-string nil - ;; https://debbugs.gnu.org/16920 - eldoc-last-message nil) - (remove-hook 'post-command-hook #'eldoc-minibuffer--cleanup))) - -(make-obsolete - 'eldoc-message "use `eldoc-documentation-functions' instead." "eldoc-1.1.0") -(defun eldoc-message (&optional string) (eldoc--message string)) -(defun eldoc--message (&optional string) - "Display STRING as an ElDoc message if it's non-nil. - -Also store it in `eldoc-last-message' and return that value." - (let ((omessage eldoc-last-message)) - (setq eldoc-last-message string) - ;; Do not put eldoc messages in the log since they are Legion. - ;; Emacs way of preventing log messages. - (let ((message-log-max nil)) - (cond (eldoc-last-message - (funcall eldoc-message-function "%s" eldoc-last-message)) - (omessage (funcall eldoc-message-function nil))))) - eldoc-last-message) - -(defun eldoc--message-command-p (command) - "Return non-nil if COMMAND is in `eldoc-message-commands'." - (and (symbolp command) - (intern-soft (symbol-name command) eldoc-message-commands))) - -;; This function goes on pre-command-hook. -;; Motion commands clear the echo area for some reason, -;; which make eldoc messages flicker or disappear just before motion -;; begins. This function reprints the last eldoc message immediately -;; before the next command executes, which does away with the flicker. -;; This doesn't seem to be required for Emacs 19.28 and earlier. -;; FIXME: The above comment suggests we don't really understand why -;; this is needed. Maybe it's not needed any more, but if it is -;; we should figure out why. -(defun eldoc-pre-command-refresh-echo-area () - "Reprint `eldoc-last-message' in the echo area." - (and eldoc-last-message - (not (minibufferp)) ;We don't use the echo area when in minibuffer. - (if (and (eldoc-display-message-no-interference-p) - (eldoc--message-command-p this-command)) - (eldoc--message eldoc-last-message) - ;; No need to call eldoc--message since the echo area will be cleared - ;; for us, but do note that the last-message will be gone. - (setq eldoc-last-message nil)))) - -;; The point of `eldoc--request-state' is not to over-request, which -;; can happen if the idle timer is restarted on execution of command -;; which is guaranteed not to change the conditions that warrant a new -;; request for documentation. -(defvar eldoc--last-request-state nil - "Tuple containing information about last ElDoc request.") -(defun eldoc--request-state () - "Compute information to store in `eldoc--last-request-state'." - (list (current-buffer) (buffer-modified-tick) (point))) - -(defun eldoc-display-message-p () - "Tell if ElDoc can use the echo area." - (and (eldoc-display-message-no-interference-p) - (not this-command) - (eldoc--message-command-p last-command))) - -(make-obsolete 'eldoc-display-message-p - "Use `eldoc-documentation-functions' instead." - "eldoc-1.6.0") - -;; Check various conditions about the current environment that might make -;; it undesirable to print eldoc messages right this instant. -(defun eldoc-display-message-no-interference-p () - "Return nil if displaying a message would cause interference." - (not (or executing-kbd-macro - ;; The following configuration shows "Matches..." in the - ;; echo area when point is after a closing bracket, which - ;; conflicts with eldoc. - (and (boundp 'show-paren-context-when-offscreen) - show-paren-context-when-offscreen - ;; There's no conflict with the child-frame and - ;; overlay versions. - (not (memq show-paren-context-when-offscreen - '(child-frame overlay))) - (not (pos-visible-in-window-p - (overlay-end show-paren--overlay))))))) - - -(defvar eldoc-documentation-functions nil - "Hook of functions that produce doc strings. - -A doc string is typically relevant if point is on a function-like -name, inside its arg list, or on any object with some associated -information. - -Each hook function is called with at least one argument CALLBACK, -a function, and decides whether to display a short doc string -about the context around point. - -- If that decision can be taken quickly, the hook function may - call CALLBACK immediately, following the protocol described - below. Alternatively, it may ignore CALLBACK entirely and - return either the doc string, or nil if there's no doc - appropriate for the context. - -- If the computation of said doc string (or the decision whether - there is one at all) is expensive or can't be performed - directly, the hook function should return a non-nil, non-string - value and arrange for CALLBACK to be called at a later time, - using asynchronous processes or other asynchronous mechanisms. - -To call the CALLBACK function, the hook function must pass it an -obligatory argument DOCSTRING, a string containing the -documentation, followed by an optional list of arbitrary -keyword-value pairs of the form (:KEY VALUE :KEY2 VALUE2...). -The information contained in these pairs is understood by members -of `eldoc-display-functions', allowing the -documentation-producing backend to cooperate with specific -documentation-displaying frontends. For example, KEY can be: - -* `:thing', VALUE being a short string or symbol designating what - DOCSTRING reports on. It can, for example be the name of the - function whose signature is being documented, or the name of - the variable whose docstring is being documented. - `eldoc-display-in-echo-area', a member of - `eldoc-display-functions', sometimes omits this information - depending on space constraints; - -* `:face', VALUE being a symbol designating a face which both - `eldoc-display-in-echo-area' and `eldoc-display-in-buffer' will - use when displaying `:thing''s value. - -* `:echo', controlling how `eldoc-display-in-echo-area' should - present this documentation item in the echo area, to save - space. If VALUE is a string, echo it instead of DOCSTRING. If - a number, only echo DOCSTRING up to that character position. - If `skip', don't echo DOCSTRING at all. - -The additional KEY `:origin' is always added by ElDoc, its VALUE -being the member of `eldoc-documentation-functions' where -DOCSTRING originated. `eldoc-display-functions' may use this -information to organize display of multiple docstrings. - -Finally, major modes should modify this hook locally, for -example: - (add-hook \\='eldoc-documentation-functions #\\='foo-mode-eldoc nil t) -so that the global value (i.e. the default value of the hook) is -taken into account if the major mode specific function does not -return any documentation.") - -(defvar eldoc-display-functions - '(eldoc-display-in-echo-area eldoc-display-in-buffer) - "Hook of functions tasked with displaying ElDoc results. -Each function is passed two arguments: DOCS and INTERACTIVE. DOCS -is a list (DOC ...) where DOC looks like (STRING :KEY VALUE :KEY2 -VALUE2 ...). STRING is a string containing the documentation's -text and the remainder of DOC is an optional list of -keyword-value pairs denoting additional properties of that -documentation. For commonly recognized properties, see -`eldoc-documentation-functions'. - -INTERACTIVE says if the request to display doc strings came -directly from the user or from ElDoc's automatic mechanisms'.") - -(defvar eldoc--doc-buffer nil "Buffer displaying latest ElDoc-produced docs.") - -(defun eldoc-doc-buffer (&optional interactive) - "Get or display ElDoc documentation buffer. - -The buffer holds the results of the last documentation request. -If INTERACTIVE, display it. Else, return said buffer." - (interactive (list t)) - (unless (buffer-live-p eldoc--doc-buffer) - (user-error (format - "ElDoc buffer doesn't exist, maybe `%s' to produce one." - (substitute-command-keys "\\[eldoc]")))) - (with-current-buffer eldoc--doc-buffer - (cond (interactive - (rename-buffer (replace-regexp-in-string "^ *" "" - (buffer-name))) - (display-buffer (current-buffer))) - (t (current-buffer))))) - -(defvar eldoc-doc-buffer-separator - (concat "\n" (propertize "\n" 'face '(:inherit separator-line :extend t)) "\n") - "String used to separate items in Eldoc documentation buffer.") - -(defun eldoc--format-doc-buffer (docs) - "Ensure DOCS are displayed in an *eldoc* buffer." - (with-current-buffer (if (buffer-live-p eldoc--doc-buffer) - eldoc--doc-buffer - (setq eldoc--doc-buffer - (get-buffer-create " *eldoc*"))) - (let ((inhibit-read-only t) - (things-reported-on)) - (special-mode) - (erase-buffer) - (setq-local nobreak-char-display nil) - (cl-loop for (docs . rest) on docs - for (this-doc . plist) = docs - for thing = (plist-get plist :thing) - when thing do - (cl-pushnew thing things-reported-on) - (setq this-doc - (concat - (propertize (format "%s" thing) - 'face (plist-get plist :face)) - ": " - this-doc)) - do (insert this-doc) - when rest do - (insert eldoc-doc-buffer-separator) - finally (goto-char (point-min))) - ;; Rename the buffer, taking into account whether it was - ;; hidden or not - (rename-buffer (format "%s*eldoc%s*" - (if (string-match "^ " (buffer-name)) " " "") - (if things-reported-on - (format " for %s" - (mapconcat - (lambda (s) (format "%s" s)) - things-reported-on - ", ")) - ""))))) - eldoc--doc-buffer) - -(defun eldoc--echo-area-render (docs) - "Similar to `eldoc--format-doc-buffer', but for echo area. -Helper for `eldoc-display-in-echo-area'." - (cl-loop for (item . rest) on docs - for (this-doc . plist) = item - for echo = (plist-get plist :echo) - for thing = (plist-get plist :thing) - unless (eq echo 'skip) do - (setq this-doc - (cond ((integerp echo) (substring this-doc 0 echo)) - ((stringp echo) echo) - (t this-doc))) - (when thing (setq this-doc - (concat - (propertize (format "%s" thing) - 'face (plist-get plist :face)) - ": " - this-doc))) - (insert this-doc) - (when rest (insert "\n")))) - -(defun eldoc--echo-area-substring (available) - "Given AVAILABLE lines, get buffer substring to display in echo area. -Helper for `eldoc-display-in-echo-area'." - (let ((start (prog1 (progn - (goto-char (point-min)) - (skip-chars-forward " \t\n") - (point)) - (forward-visible-line (1- available)) - (end-of-visible-line) - (skip-chars-backward " \t\n"))) - (truncated (save-excursion - (skip-chars-forward " \t\n") - (not (eobp))))) - (cond ((eldoc--echo-area-prefer-doc-buffer-p truncated) - nil) - ((and truncated - (> available 1) - eldoc-echo-area-display-truncation-message) - (forward-visible-line -1) - (end-of-visible-line) - (concat (buffer-substring start (point)) - (format - "\n(Documentation truncated. Use `%s' to see rest)" - (substitute-command-keys "\\[eldoc-doc-buffer]")))) - (t - (buffer-substring start (point)))))) - -(defun eldoc--echo-area-prefer-doc-buffer-p (truncatedp) - "Tell if display in the echo area should be skipped. -Helper for `eldoc-display-in-echo-area'. If TRUNCATEDP the -documentation to potentially appear in the echo area is -known to be truncated." - (and (or (eq eldoc-echo-area-prefer-doc-buffer t) - (and truncatedp - (eq eldoc-echo-area-prefer-doc-buffer - 'maybe))) - (get-buffer-window eldoc--doc-buffer t))) - -(defun eldoc-display-in-echo-area (docs interactive) - "Display DOCS in echo area. -INTERACTIVE is non-nil if user explictly invoked ElDoc. Honor -`eldoc-echo-area-use-multiline-p' and -`eldoc-echo-area-prefer-doc-buffer'." - (cond - ((and (not interactive) - ;; When called non-interactively, check if we have permission - ;; to mess with echo area at all. For example, if - ;; this-command is non-nil while running via an idle timer, - ;; we're still in the middle of executing a command, e.g. a - ;; query-replace where it would be annoying to overwrite the - ;; echo area. - (or - (not (eldoc-display-message-no-interference-p)) - this-command - (not (eldoc--message-command-p last-command))))) - (;; If nothing to report, clear the echo area. - (null docs) - (eldoc--message nil)) - (t - ;; Otherwise, proceed to change the echo area. Start by - ;; establishing some parameters. - (let* - ((width (1- (window-width (minibuffer-window)))) - (val (if (and (symbolp eldoc-echo-area-use-multiline-p) - eldoc-echo-area-use-multiline-p) - max-mini-window-height - eldoc-echo-area-use-multiline-p)) - (available (cl-typecase val - (float (truncate (* (frame-height) val))) - (integer val) - (t 'just-one-line))) - single-doc single-doc-sym) - (let ((echo-area-message - (cond - (;; To output to the echo area, we handle the - ;; `truncate-sym-name-if-fit' special case first, by - ;; checking for a lot of special conditions. - (and - (eq 'truncate-sym-name-if-fit eldoc-echo-area-use-multiline-p) - (null (cdr docs)) - (setq single-doc (caar docs)) - (setq single-doc-sym - (format "%s" (plist-get (cdar docs) :thing))) - (< (length single-doc) width) - (not (string-match "\n" single-doc)) - (> (+ (length single-doc) (length single-doc-sym) 2) width)) - single-doc) - ((and (numberp available) - (cl-plusp available)) - ;; Else, given a positive number of logical lines, grab - ;; as many as we can. - (with-temp-buffer - (eldoc--echo-area-render docs) - (eldoc--echo-area-substring available))) - (t ;; this is the "truncate brutally" situation - (let ((string - (with-temp-buffer - (eldoc--echo-area-render docs) - (buffer-substring (goto-char (point-min)) - (progn (end-of-visible-line) - (point)))))) - (if (> (length string) width) ; truncation to happen - (unless (eldoc--echo-area-prefer-doc-buffer-p t) - (truncate-string-to-width string width)) - (unless (eldoc--echo-area-prefer-doc-buffer-p nil) - string))))))) - (when echo-area-message - (eldoc--message echo-area-message))))))) - -(defun eldoc-display-in-buffer (docs interactive) - "Display DOCS in a dedicated buffer. -If INTERACTIVE is t, also display the buffer." - (eldoc--format-doc-buffer docs) - (when interactive (eldoc-doc-buffer t))) - -(defun eldoc-documentation-default () - "Show the first non-nil documentation string for item at point. -This is the default value for `eldoc-documentation-strategy'." - (run-hook-wrapped 'eldoc-documentation-functions - (lambda (f) - (funcall f (eldoc--make-callback :eager f))))) - -(defun eldoc-documentation-compose () - "Show multiple documentation strings together after waiting for all of them. -This is meant to be used as a value for `eldoc-documentation-strategy'." - (let (fns-and-callbacks) - ;; Make all the callbacks, setting up state inside - ;; `eldoc--invoke-strategy' to know how many callbacks to wait for - ;; before displaying the result (bug#62816). - (run-hook-wrapped 'eldoc-documentation-functions - (lambda (f) - (push (cons f (eldoc--make-callback :patient f)) - fns-and-callbacks) - nil)) - ;; Now call them. The last one will trigger the display. - (cl-loop for (f . callback) in fns-and-callbacks - for str = (funcall f callback) - when (or (null str) (stringp str)) do (funcall callback str))) - t) - -(defun eldoc-documentation-compose-eagerly () - "Show multiple documentation strings one by one as soon as possible. -This is meant to be used as a value for `eldoc-documentation-strategy'." - (run-hook-wrapped 'eldoc-documentation-functions - (lambda (f) - (let* ((callback (eldoc--make-callback :eager f)) - (str (funcall f callback))) - (if (or (null str) (stringp str)) (funcall callback str)) - nil))) - t) - -(defun eldoc-documentation-enthusiast () - "Show most important documentation string produced so far. -This is meant to be used as a value for `eldoc-documentation-strategy'." - (run-hook-wrapped 'eldoc-documentation-functions - (lambda (f) - (let* ((callback (eldoc--make-callback :enthusiast f)) - (str (funcall f callback))) - (if (stringp str) (funcall callback str)) - nil))) - t) - -;; JT@2020-07-10: ElDoc is pre-loaded, so in Emacs < 28 we can't -;; make the "old" `eldoc-documentation-function' point to the new -;; `eldoc-documentation-strategy', so we do the reverse. This allows -;; for ElDoc to be loaded in those older Emacs versions and work with -;; whomever (major-modes, extensions, user) sets one or the other -;; variable. -(defmacro eldoc--documentation-strategy-defcustom - (main secondary value docstring &rest more) - "Defcustom helper macro for sorting `eldoc-documentation-strategy'." - (declare (indent 2)) - `(if (< emacs-major-version 28) - (progn - (defcustom ,secondary ,value ,docstring ,@more) - (define-obsolete-variable-alias ',main ',secondary "eldoc-1.1.0")) - (progn - (defcustom ,main ,value ,docstring ,@more) - (defvaralias ',secondary ',main ,docstring)))) - -(eldoc--documentation-strategy-defcustom eldoc-documentation-strategy - eldoc-documentation-function - #'eldoc-documentation-default - "How to collect and display results of `eldoc-documentation-functions'. - -This variable controls how to call the functions in the special hook -`eldoc-documentation-functions', and how to organize their results -for display to the user. The functions in `eldoc-documentation-functions' -are the source of documentation, and act as back-end for ElDoc. - -The following values are supported: - -- `eldoc-documentation-default': Call functions in the special - hook in order, until one of them returns a non-nil string - value. Display only that string. - -- `eldoc-documentation-compose': Call all the functions in the - special hook and display all of the resulting strings together, - after all of the functions were called, and in the order of the - functions in the hook. - -- `eldoc-documentation-compose-eagerly': Call all the functions in - the special hook, and display each non-nil string as soon as it - is returned by a function, before calling the next function. - -- `eldoc-documentation-enthusiast': Call all the functions in the - special hook, and display only the most important resulting - string at any given time. A function appearing first in - the special hook is considered more important than those which - appear after it. - -This variable can also be set to a function of no arguments that -returns something other than a string or nil, and allows for some -or all of the special hook `eldoc-documentation-functions' to be -run. In that case, the strategy function should follow that -other variable's protocol closely and display the resulting doc -strings itself. - -For backward compatibility with the \"old\" protocol, this variable -can also be set to a function that returns nil or a doc string, -depending whether or not there is documentation to display at -all." - :link '(info-link "(emacs) Lisp Doc") - :type '(radio (function-item eldoc-documentation-default) - (function-item eldoc-documentation-compose) - (function-item eldoc-documentation-compose-eagerly) - (function-item eldoc-documentation-enthusiast) - (function :tag "Other function")) - :version "28.1") - -(defun eldoc--supported-p () - "Non-nil if an ElDoc function is set for this buffer." - (and (not (memq eldoc-documentation-strategy '(nil ignore))) - (or eldoc-documentation-functions - ;; The old API had major modes set `eldoc-documentation-function' - ;; to provide eldoc support. It's impossible now to determine - ;; reliably whether the `eldoc-documentation-strategy' provides - ;; eldoc support (as in the old API) or whether it just provides - ;; a way to combine the results of the - ;; `eldoc-documentation-functions' (as in the new API). - ;; But at least if it's set buffer-locally it's a good hint that - ;; there's some eldoc support in the current buffer. - (local-variable-p 'eldoc-documentation-strategy)))) - -(defvar eldoc--enthusiasm-curbing-timer nil - "Timer used by the `eldoc-documentation-enthusiast' strategy. -When a doc string is encountered, it must endure a certain amount -of time unchallenged until it is displayed to the user. This -prevents blinking if a lower priority docstring comes in shortly -before a higher priority one.") - -(defalias 'eldoc #'eldoc-print-current-symbol-info) - -;; This variable should be unbound, but that confuses -;; `describe-symbol' for some reason. -(defvar eldoc--make-callback nil "Helper for function `eldoc--make-callback'.") - -;; JT@2020-07-08: the below docstring for the internal function -;; `eldoc--invoke-strategy' could be moved to -;; `eldoc-documentation-strategy' or thereabouts if/when we decide to -;; extend or publish the `make-callback' protocol. -(defun eldoc--make-callback (method origin) - "Make callback suitable for `eldoc-documentation-functions'. -The return value is a function FN whose lambda list is (STRING -&rest PLIST) and can be called by those functions. Its -responsibility is always to register the docstring STRING along -with options specified in PLIST as the documentation to display -for each particular situation. - -METHOD specifies how the callback behaves relative to other -competing elements in `eldoc-documentation-functions'. It can -have the following values: - -- `:enthusiast' says to display STRING as soon as possible if - there's no higher priority doc string; - -- `:patient' says to display STRING along with all other - competing strings but only when all of all - `eldoc-documentation-functions' have been collected; - -- `:eager' says to display STRING along with all other competing - strings so far, as soon as possible. - -ORIGIN is the member of `eldoc-documentation-functions' which -will be responsible for eventually calling the FN." - (funcall eldoc--make-callback method origin)) - -(defun eldoc--invoke-strategy (interactive) - "Invoke `eldoc-documentation-strategy' function. - -If INTERACTIVE is non-nil, the request came directly from a user -command, otherwise it came from ElDoc's idle -timer, `eldoc-timer'. - -That function's job is to run the `eldoc-documentation-functions' -special hook, using the `run-hook' family of functions. ElDoc's -built-in strategy functions play along with the -`eldoc--make-callback' protocol, using it to produce a callback -argument to feed the functions that the user places in -`eldoc-documentation-functions'. Whenever the strategy -determines it has information to display to the user, this -function passes responsibility to the functions in -`eldoc-display-functions'. - -Other third-party values of `eldoc-documentation-strategy' should -not use `eldoc--make-callback'. They must find some alternate -way to produce callbacks to feed to -`eldoc-documentation-functions' and should endeavor to display -the docstrings eventually produced, using -`eldoc-display-functions'." - (let* (;; How many callbacks have been created by the strategy - ;; function and passed to elements of - ;; `eldoc-documentation-functions'. - (howmany 0) - ;; How many calls to callbacks we're still waiting on. Used - ;; by `:patient'. - (want 0) - ;; The doc strings and corresponding options registered so - ;; far. - (docs-registered '())) - (cl-labels - ((register-doc - (pos string plist origin) - (when (and string (> (length string) 0)) - (push (cons pos (cons string `(:origin ,origin ,@plist))) - docs-registered))) - (display-doc - () - (run-hook-with-args - 'eldoc-display-functions (mapcar #'cdr - (setq docs-registered - (sort docs-registered - (lambda (a b) (< (car a) (car b)))))) - interactive)) - (make-callback - (method origin) - (let ((pos (prog1 howmany (cl-incf howmany)))) - (cl-ecase method - (:enthusiast - (lambda (string &rest plist) - (when (and string (cl-loop for (p) in docs-registered - never (< p pos))) - (setq docs-registered '()) - (register-doc pos string plist origin)) - (when (and (timerp eldoc--enthusiasm-curbing-timer) - (memq eldoc--enthusiasm-curbing-timer - timer-list)) - (cancel-timer eldoc--enthusiasm-curbing-timer)) - (setq eldoc--enthusiasm-curbing-timer - (run-at-time (unless (zerop pos) 0.3) - nil #'display-doc)) - t)) - (:patient - (cl-incf want) - (lambda (string &rest plist) - (register-doc pos string plist origin) - (when (zerop (cl-decf want)) (display-doc)) - t)) - (:eager - (lambda (string &rest plist) - (register-doc pos string plist origin) - (display-doc) - t)))))) - (let* ((eldoc--make-callback #'make-callback) - (res (funcall eldoc-documentation-strategy))) - ;; Observe the old and the new protocol: - (cond (;; Old protocol: got string, e-d-strategy is iself the - ;; origin function, and we output immediately; - (stringp res) - (register-doc 0 res nil eldoc-documentation-strategy) - (display-doc)) - (;; Old protocol: got nil, clear the echo area; - (null res) (eldoc--message nil)) - (;; New protocol: trust callback will be called; - t)))))) - -(defun eldoc-print-current-symbol-info (&optional interactive) - "Document thing at point." - (interactive '(t)) - (let (token) - (cond (interactive - (eldoc--invoke-strategy t)) - ((not (equal (setq token (eldoc--request-state)) - eldoc--last-request-state)) - (let ((non-essential t)) - (setq eldoc--last-request-state token) - (eldoc--invoke-strategy nil)))))) - - -;; This section only affects ElDoc output to the echo area, as in -;; `eldoc-display-in-echo-area'. -;; -;; When point is in a sexp, the function args are not reprinted in the echo -;; area after every possible interactive command because some of them print -;; their own messages in the echo area; the eldoc functions would instantly -;; overwrite them unless it is more restrained. -;; These functions do display-command table management. - -(defun eldoc-add-command (&rest cmds) - "Add each of CMDS to the obarray `eldoc-message-commands'." - (dolist (name cmds) - (and (symbolp name) - (setq name (symbol-name name))) - (set (intern name eldoc-message-commands) t))) - -(defun eldoc-add-command-completions (&rest names) - "Pass every prefix completion of NAMES to `eldoc-add-command'." - (dolist (name names) - (apply #'eldoc-add-command (all-completions name obarray 'commandp)))) - -(defun eldoc-remove-command (&rest cmds) - "Remove each of CMDS from the obarray `eldoc-message-commands'." - (dolist (name cmds) - (and (symbolp name) - (setq name (symbol-name name))) - (unintern name eldoc-message-commands))) - -(defun eldoc-remove-command-completions (&rest names) - "Pass every prefix completion of NAMES to `eldoc-remove-command'." - (dolist (name names) - (apply #'eldoc-remove-command - (all-completions name eldoc-message-commands)))) - -;; Prime the command list. -(eldoc-add-command-completions - "back-to-indentation" - "backward-" "beginning-of-" "delete-other-windows" "delete-window" - "down-list" "end-of-" "exchange-point-and-mark" "forward-" "goto-" - "handle-select-window" "indent-for-tab-command" "left-" "mark-page" - "mark-paragraph" "mouse-set-point" "move-" "move-beginning-of-" - "move-end-of-" "newline" "next-" "other-window" "pop-global-mark" - "previous-" "recenter" "right-" "scroll-" "self-insert-command" - "split-window-" "up-list" "touch-screen-handle-touch" - "analyze-text-conversion") - -(provide 'eldoc) - -;;; eldoc.el ends here blob - 0795d9bf14e8ab2545379fc034ca31f1fa85f7bf (mode 644) blob + /dev/null --- elpa/eldoc-1.15.0.signed +++ /dev/null @@ -1,2 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2023-12-05T23:05:03+0100 using RSA -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2023-12-05T23:05:03+0100 using EDDSA \ No newline at end of file blob - 17d02c04916f128139c7668d4c8211a5707a0652 (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/README.md +++ /dev/null @@ -1,409 +0,0 @@ -# Elfeed Emacs Web Feed Reader - -Elfeed is an extensible web feed reader for Emacs, supporting both -Atom and RSS. It requires Emacs 24.3 and is available for download -from [MELPA](http://melpa.milkbox.net/) or -[el-get](https://github.com/dimitri/el-get). Elfeed was inspired by -[notmuch](http://notmuchmail.org/). - -For a longer overview, - - * [Introducing Elfeed, an Emacs Web Feed Reader](http://nullprogram.com/blog/2013/09/04/). - * [Tips and Tricks](http://nullprogram.com/blog/2013/11/26/) - * [Read your RSS feeds in Emacs with Elfeed -](http://pragmaticemacs.com/emacs/read-your-rss-feeds-in-emacs-with-elfeed/) - * [Scoring Elfeed articles](http://kitchingroup.cheme.cmu.edu/blog/2017/01/05/Scoring-elfeed-articles/) - * [Using Emacs 29](https://www.youtube.com/watch?v=pOFqzK1Ymr4), - [30](https://www.youtube.com/watch?v=tjnK1rkO7RU), - [31](https://www.youtube.com/watch?v=5zuSUbAHH8c) - * [Take Elfeed everywhere: Mobile rss reading Emacs-style (for free/cheap)](http://babbagefiles.blogspot.com/2017/03/take-elfeed-everywhere-mobile-rss.html) - * [Elfeed Rules!](https://noonker.github.io/posts/2020-04-22-elfeed/) ([reddit](https://old.reddit.com/r/emacs/comments/g6oowz/elfeed_rules/)) - * [Elfeed with Tiny Tiny RSS](https://codingquark.com/emacs/2020/04/19/elfeed-protocol-ttrss.html) ([hn](https://news.ycombinator.com/item?id=22915200)) - * [Open Emacs elfeed links in the background](http://xenodium.com/open-emacs-elfeed-links-in-background/) - * [Using Emacs 72](https://cestlaz.github.io/post/using-emacs-72-customizing-elfeed/) - * [Lazy Elfeed](https://karthinks.com/blog/lazy-elfeed/) - * [Using Elfeed to View Videos](https://joshrollinswrites.com/help-desk-head-desk/20200611/) - * [Manage podcasts in Emacs with Elfeed and Bongo](https://protesilaos.com/codelog/2020-09-11-emacs-elfeed-bongo/) - * [... more ...](http://nullprogram.com/tags/elfeed/) - * [... and more ...](http://pragmaticemacs.com/category/elfeed/) - -[![](http://i.imgur.com/kxgF5AH.png)](http://i.imgur.com/kxgF5AH.png) - -The database format is stable and is never expected to change. - -## Prerequisites - -**It is *strongly* recommended you have cURL installed**, either in -your PATH or configured via `elfeed-curl-program-name`. Elfeed will -prefer it to Emacs' own URL-fetching mechanism, `url-retrieve`. It's -also essential for running Elfeed on Windows, where `url-retrieve` is -broken. Updates using cURL are significantly faster than the built-in -method, both for you and the feed hosts. - -If this is giving you problems, fetching with cURL can be disabled by -setting `elfeed-use-curl` to nil. - -## Extensions - -These projects extend Elfeed with additional features: - -* [elfeed-org](https://github.com/remyhonig/elfeed-org) -* [elfeed-goodies](https://github.com/algernon/elfeed-goodies) -* [elfeed-protocol](https://github.com/fasheng/elfeed-protocol) -* [elfeed-score](https://github.com/sp1ff/elfeed-score) -* [Elfeed Android interface](https://github.com/areina/elfeed-cljsrn) - ([Google Play](https://play.google.com/store/apps/details?id=com.elfeedcljsrn)) -* [elfeed-dashboard](https://github.com/Manoj321/elfeed-dashboard) - -## Getting Started - -Elfeed is broken into a multiple source files, so if you manually -install it you will need to add the Elfeed package directory to your -`load-path`. If installed via package.el or el-get, this will be done -automatically. - -It is recommended that you make a global binding for `elfeed`. - -```el -(global-set-key (kbd "C-x w") 'elfeed) -``` - -Running the interactive function `elfeed` will pop up the -`*elfeed-search*` buffer, which will display feed items. - - * g: refresh view of the feed listing - * G: fetch feed updates from the servers - * s: update the search filter (see tags) - * c: clear the search filter - -This buffer will be empty until you add your feeds to the -`elfeed-feeds` list and initiate an update with `M-x elfeed-update` -(or G in the Elfeed buffer). This will populate the Elfeed -database with entries. - -```el -;; Somewhere in your .emacs file -(setq elfeed-feeds - '("http://nullprogram.com/feed/" - "http://planet.emacsen.org/atom.xml")) -``` - -Another option for providing a feed list is with an OPML file. Running -`M-x elfeed-load-opml` will fill `elfeed-feeds` with feeds listed in -an OPML file. When `elfeed-load-opml` is called interactively, it will -automatically save the feedlist to your customization file, so you -will only need to do this once. - -If there are a lot of feeds, the initial update will take noticeably -longer than normal operation because of the large amount of -information being written the database. Future updates will only need -to write new or changed data. If updating feeds slows down Emacs too -much for you, reduce the number of concurrent fetches via -`elfeed-set-max-connections`. - -If you're getting many "Queue timeout exceeded" errors, increase the -fetch timeout via `elfeed-set-timeout`. - -~~~el -(setf url-queue-timeout 30) -~~~ - -From the search buffer there are a number of ways to interact with -entries. Entries are selected by placing the point over an entry. -Multiple entries are selected at once by using an active region. - - * RET: view selected entry in a buffer - * b: open selected entries in your browser (`browse-url`) - * y: copy selected entries URL to the clipboard - * r: mark selected entries as read - * u: mark selected entries as unread - * +: add a specific tag to selected entries - * -: remove a specific tag from selected entries - -## Tags - -Elfeed maintains a list of arbitrary tags -- symbols attached to an -entry. The tag `unread` is treated specially by default, with unread -entries appearing in bold. - -### Autotagging - -Tags can automatically be applied to entries discovered in specific -feeds through extra syntax in `elfeed-feeds`. Normally this is a list -of strings, but an item can also be a list, providing set of -"autotags" for a feed's entries. - -```el -(setq elfeed-feeds - '(("http://nullprogram.com/feed/" blog emacs) - "http://www.50ply.com/atom.xml" ; no autotagging - ("http://nedroid.com/feed/" webcomic))) -``` - -### Filter Syntax - -To make tags useful, the Elfeed entry listing buffer can be filtered -by tags. Use `elfeed-search-set-filter` (or s) to update -the filter. Use `elfeed-search-clear-filter` to restore the default. - -Any component of the search string beginning with a `+` or -a `-` is treated like a tag. `+` means the tag is required, `-` means -the tag must not be present. - -A component beginning with a `@` indicates an age or a date range. An -age is a relative time expression or an absolute date expression. -Entries older than this age are filtered out. The age description -accepts plain English, but cannot have spaces, so use dashes. For -example, `"@2-years-old"`, `"@3-days-ago"` or `"@2019-06-24"`. A date -range are two ages seperated by a `--`, e.g. -`"@2019-06-20--2019-06-24"` or `"@5-days-ago--1-day-ago"`. The entry -must be newer than the first expression but older than the second. The -database is date-oriented, so **filters that include an age -restriction are significantly more efficient.** - -A component beginning with a `!` is treated as an "inverse" regular -expression. This means that any entry matching this regular expression -will be filtered out. The regular expression begins *after* the `!` -character. You can read this as "entry not matching `foo`". - -A component beginning with a `#` limits the total number of entries -displayed to the number immediately following the symbol. For example, -to limit the display to 20 entries: `#20`. - -A component beginning with a `=` is a regular expression matching the -entry's feed (title or URL). Only entries belonging to a feed that -matches at least one of the `=` expressions will be shown. - -A component beginning with a `~` is a regular expression matching the -entry's feed (title or URL). Only entries belonging to a feed that -matches none of the `~` expressions will be shown. - -All other components are treated as a regular expression, and only -entries matching it (title or URL) will be shown. - -Here are some example filters. - - * `@6-months-ago +unread` - -Only show unread entries of the last six months. This is the default filter. - - * `linu[xs] @1-year-old` - -Only show entries about Linux or Linus from the last year. - - * `-unread +youtube #10` - -Only show the most recent 10 previously-read entries tagged as -`youtube`. - - * `+unread !x?emacs` - -Only show unread entries not having `emacs` or `xemacs` in the title -or link. - -* `+emacs =http://example.org/feed/` - -Only show entries tagged as `emacs` from a specific feed. - -#### Default Search Filter - -You can set your default search filter by changing the default value -of `elfeed-search-filter`. It only changes buffer-locally when you're -adjusting the filter within Elfeed. For example, some users prefer to -have a space on the end for easier quick searching. - - (setq-default elfeed-search-filter "@1-week-ago +unread ") - -### Tag Hooks - -The last example assumes you've tagged posts with `youtube`. You -probably want to do this sort of thing automatically, either through -the "autotags" feature mentioned above, or with the -`elfeed-new-entry-hook`. Functions in this hook are called with new -entries, allowing them to be manipulated, such as adding tags. - -```el -;; Mark all YouTube entries -(add-hook 'elfeed-new-entry-hook - (elfeed-make-tagger :feed-url "youtube\\.com" - :add '(video youtube))) -``` - -Avoiding tagging old entries as `unread`: - -```el -;; Entries older than 2 weeks are marked as read -(add-hook 'elfeed-new-entry-hook - (elfeed-make-tagger :before "2 weeks ago" - :remove 'unread)) -``` - -Or building your own subset feeds: - -```el -(add-hook 'elfeed-new-entry-hook - (elfeed-make-tagger :feed-url "example\\.com" - :entry-title '(not "something interesting") - :add 'junk - :remove 'unread)) -``` - -Use `M-x elfeed-apply-hooks-now` to apply `elfeed-new-entry-hook` to -all existing entries. Otherwise hooks will only apply to new entries -on discovery. - -### Custom Tag Faces - -By default, entries marked `unread` will have bolded titles in the -`*elfeed-search*` listing. You can customize how tags affect an -entry's appearance by customizing `elfeed-search-face-alist`. For -example, this configuration makes entries tagged `important` stand out -in red. - -~~~el -(defface important-elfeed-entry - '((t :foreground "#f77")) - "Marks an important Elfeed entry.") - -(push '(important important-elfeed-entry) - elfeed-search-face-alist) -~~~ - -All faces from all tags will be applied to the entry title. The faces -will be ordered as they appear in `elfeed-search-face-alist`. - -## Bookmarks - -Filters can be saved and restored using Emacs' built-in [bookmarks -feature][bm]. While in the search buffer, use `M-x bookmark-set` to -save the current filter, and `M-x bookmark-jump` to restore a saved -filter. Emacs automatically persists bookmarks across sessions. - -[bm]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Bookmarks.html - -## Metadata Plist - -All feed and entry objects have plist where you can store your own -arbitrary, [readable values][rd]. These values are automatically -persisted in the database. This metadata is accessed using the -polymorphic `elfeed-meta` function. It's setf-able. - -~~~el -(setf (elfeed-meta entry :rating) 4) -(elfeed-meta entry :rating) -;; => 4 - -(setf (elfeed-meta feed :title) "My Better Title") -~~~ - -Elfeed itself adds some entries to this plist, some for your use, some -for its own use. Here are the properties that Elfeed uses: - -* `:authors` : A list of author plists (`:name`, `:uri`, `:email`). -* `:canonical-url` : The final URL for the feed after all redirects. -* `:categories` : The feed-supplied categories for this entry. -* `:etag` : HTTP Etag header, for conditional GETs. -* `:failures` : Number of times this feed has failed to update. -* `:last-modified` : HTTP Last-Modified header, for conditional GETs. -* `:title` : Overrides the feed-supplied title for display purposes, - both for feeds and entries. See also `elfeed-search-set-feed-title` - and `elfeed-search-set-entry-title`. - -This list will grow in time, so you might consider namespacing your -own properties to avoid collisions (e.g. `:xyz/rating`), or simply not -using keywords as keys. Elfeed will always use keywords without a -slash. - -[rd]: http://nullprogram.com/blog/2013/12/30/ - -## Hooks - -A number of hooks are available to customize the behavior of Elfeed at -key points without resorting to advice. - -* `elfeed-new-entry-hook` : Called each time a new entry it added to - the database, allowing for automating tagging and such. -* `elfeed-new-entry-parse-hook` : Called with each new entry and the - full XML structure from which it was parsed, allowing for additional - information to be drawn from the original feed XML. -* `elfeed-http-error-hooks` : Allows for special behavior when HTTP - errors occur, beyond simply logging the error to `*elfeed-log*` . -* `elfeed-parse-error-hooks` : Allows for special behavior when feed - parsing fails, beyond logging. -* `elfeed-db-update-hook` : Called any time the database has had a - major modification. - -## Viewing Entries - -Entries are viewed locally in Emacs by typing `RET` while over an -entry in the search listing. The content will be displayed in a -separate buffer using `elfeed-show-mode`, rendered using Emacs' -built-in shr package. This requires an Emacs compiled with `libxml2` -bindings, which provides the necessary HTML parser. - -Sometimes displaying images can slow down or even crash Emacs. Set -`shr-inhibit-images` to disable images if this is a problem. - -## Web Interface - -Elfeed includes a demonstration/toy web interface for remote network -access. It's a single-page web application that follows the database -live as new entries arrive. It's packaged separately as `elfeed-web`. -To fire it up, run `M-x elfeed-web-start` and visit -http://localhost:8080/elfeed/ (check your `httpd-port`) with a -browser. See the `elfeed-web.el` header for endpoint documentation if -you'd like to access the Elfeed database through the web API. - -It's rough and unfinished -- no keyboard shortcuts, read-only, no -authentication, and a narrow entry viewer. This is basically Elfeed's -"mobile" interface. Patches welcome. - -## Platform Support - -Summary: Install cURL and most problems disappear for all platforms. - -I personally only use Elfeed on Linux, but it's occasionally tested on -Windows. Unfortunately the Windows port of Emacs is a bit too unstable -for parallel feed downloads with `url-retrieve`, not to mention the -[tiny, hard-coded, 512 open descriptor limitation][files], so it -limits itself to one feed at a time on this platform. - -[files]: http://msdn.microsoft.com/en-us/library/kdfaxaay%28vs.71%29.aspx - -If you fetch HTTPS feeds without cURL on *any* platform, it's -essential that Emacs is built with the `--with-gnutls` option. -Otherwise Emacs runs gnutls in an inferior process, which rarely works -well. - -## Database Management - -The database should keep itself under control without any manual -intervention, but steps can be taken to minimize the database size if -desired. The simplest option is to run the `elfeed-db-compact` -command, which will pack the loose-file content database into a single -compressed file. This function works well in `kill-emacs-hook`. - -Going further, a function could be added to `elfeed-new-entry-hook` to -strip unwanted/unneeded content from select entries before being -stored in the database. For example, for YouTube videos only the entry -link is of interest and the regularly-changing entry content could be -tossed to save time and storage. - -## Status and Roadmap - -Elfeed is to the point where it can serve 100% of my own web feed -needs. My personal selection of about 150 feeds has been acting as my -test case as I optimize and add features. - -Some things I still might want to add: - -* Database synchronization between computers -* Parallel feed fetching via separate Emacs subprocesses - -## Motivation - -As far as I know, outside of Elfeed there does not exist an -extensible, text-file configured, power-user web feed client that can -handle a reasonable number of feeds. The existing clients I've tried -are missing some important capability that limits its usefulness to -me. blob - a98a8ffe4212d15554dcf82276103b1d335d0d44 (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-autoloads.el +++ /dev/null @@ -1,114 +0,0 @@ -;;; elfeed-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from elfeed.el - -(autoload 'elfeed-update "elfeed" "\ -Update all the feeds in `elfeed-feeds'." t) -(autoload 'elfeed "elfeed" "\ -Enter elfeed." t) -(autoload 'elfeed-load-opml "elfeed" "\ -Load feeds from an OPML file into `elfeed-feeds'. -When called interactively, the changes to `elfeed-feeds' are -saved to your customization file. - -(fn FILE)" t) -(autoload 'elfeed-export-opml "elfeed" "\ -Export the current feed listing to OPML-formatted FILE. - -(fn FILE)" t) -(register-definition-prefixes "elfeed" '("elfeed-")) - - -;;; Generated autoloads from elfeed-csv.el - -(register-definition-prefixes "elfeed-csv" '("elfeed-csv-")) - - -;;; Generated autoloads from elfeed-curl.el - -(register-definition-prefixes "elfeed-curl" '("elfeed-curl-")) - - -;;; Generated autoloads from elfeed-db.el - -(register-definition-prefixes "elfeed-db" '("elfeed-" "with-elfeed-db-visit")) - - -;;; Generated autoloads from elfeed-lib.el - -(register-definition-prefixes "elfeed-lib" '("elfeed-")) - - -;;; Generated autoloads from elfeed-link.el - -(autoload 'elfeed-link-store-link "elfeed-link" "\ -Store a link to an elfeed search or entry buffer. - -When storing a link to an entry, automatically extract all the -entry metadata. These can be used in the capture templates as -%:elfeed-entry-. See `elfeed-entry--create' for the list -of available props.") -(autoload 'elfeed-link-open "elfeed-link" "\ -Jump to an elfeed entry or search. - -Depending on what FILTER-OR-ID looks like, we jump to either -search buffer or show a concrete entry. - -(fn FILTER-OR-ID)") -(eval-after-load 'org `(funcall ',(lambda nil (if (version< (org-version) "9.0") (with-no-warnings (org-add-link-type "elfeed" #'elfeed-link-open) (add-hook 'org-store-link-functions #'elfeed-link-store-link)) (with-no-warnings (org-link-set-parameters "elfeed" :follow #'elfeed-link-open :store #'elfeed-link-store-link)))))) - - -;;; Generated autoloads from elfeed-log.el - -(register-definition-prefixes "elfeed-log" '("elfeed-log")) - - -;;; Generated autoloads from elfeed-search.el - -(autoload 'elfeed-search-bookmark-handler "elfeed-search" "\ -Jump to an elfeed-search bookmarked location. - -(fn RECORD)") -(autoload 'elfeed-search-desktop-restore "elfeed-search" "\ -Restore the state of an elfeed-search buffer on desktop restore. - -(fn FILE-NAME BUFFER-NAME SEARCH-FILTER)") -(add-to-list 'desktop-buffer-mode-handlers '(elfeed-search-mode . elfeed-search-desktop-restore)) -(register-definition-prefixes "elfeed-search" '("elfeed-s")) - - -;;; Generated autoloads from elfeed-show.el - -(autoload 'elfeed-show-bookmark-handler "elfeed-show" "\ -Show the bookmarked entry saved in the `RECORD'. - -(fn RECORD)") -(register-definition-prefixes "elfeed-show" '("elfeed-")) - - -;;; Generated autoloads from xml-query.el - -(register-definition-prefixes "xml-query" '("xml-query")) - -;;; End of scraped data - -(provide 'elfeed-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; elfeed-autoloads.el ends here blob - ae1f1457e26aef01e2f7cfcad9a4b3838850edbd (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-csv.el +++ /dev/null @@ -1,176 +0,0 @@ -;;; elfeed-csv.el --- export database to CSV files -*- lexical-binding: t; -*- - -;;; Commentary: - -;; The `elfeed-csv-export' docstring has a SQL schema recommendation. -;; Given these schemas, these CSV files are trivially imported into a -;; SQLite database using the sqlite3 command line program: - -;; sqlite> .mode csv -;; sqlite> .import feeds.csv feeds -;; sqlite> .import entries.csv entries -;; sqlite> .import tags.csv tags - -;; Note: nil values won't be imported as NULL, but as empty strings. - -;; Here are a few interesting queries to make on your own data: - -;; For each tag in your database, compute a histogram of posts with -;; 1-hour bins across the the day (0-23), in your local timezone. - -;; SELECT tag, -;; cast(strftime('%H', date, 'unixepoch', 'localtime') AS INT) AS hour, -;; count(id) AS count -;; FROM entries -;; JOIN tags ON tags.entry = entries.id AND tags.feed = entries.feed -;; GROUP BY tag, hour; - -;; Like above, but per week-day (0-6). - -;; SELECT tag, -;; cast(strftime('%w', date, 'unixepoch', 'localtime') AS INT) AS day, -;; count(id) AS count -;; FROM entries -;; JOIN tags ON tags.entry = entries.id AND tags.feed = entries.feed -;; GROUP BY tag, day; - -;; For each feed, compute the number of entries and last entry date. - -;; SELECT feeds.title AS title, -;; count(url) AS entry_count, -;; datetime(max(date), 'unixepoch') AS last_entry_date -;; FROM feeds -;; JOIN entries ON feeds.url = entries.feed -;; GROUP BY url -;; ORDER BY max(date) DESC; - -;; Compute a histogram of entry title lengths. - -;; SELECT length(title) AS length, -;; count(*) AS count -;; FROM entries -;; GROUP BY length -;; ORDER BY length; - -;; Again, but this time group by tag. - -;; SELECT tag, -;; length(title) AS length, -;; count(*) AS count -;; FROM entries -;; JOIN tags ON tags.entry = entries.id AND tags.feed = entries.feed -;; GROUP BY tag, length -;; ORDER BY length; - -;; What's the relationship between title length and time of day of an -;; entry? (Scatter plot this result.) - -;; SELECT (date % (24*60*60)) / (24*60*60) AS day_time, -;; length(title) AS length -;; FROM entries -;; JOIN tags ON tags.entry = entries.id AND tags.feed = entries.feed; - -;;; Code: - -(require 'cl-lib) -(require 'elfeed-db) - -(defvar elfeed-csv-nil "" - "The string representation to use for nil. -Consider let-binding this around your `elfeed-csv-quote' call.") - -(defun elfeed-csv-quote (sexp) - "Return CSV string representation of SEXP." - (cond ((null sexp) - elfeed-csv-nil) - ((not (stringp sexp)) - (elfeed-csv-quote (prin1-to-string sexp))) - ((string-match-p "[\"\n,]" sexp) - (concat "\"" (replace-regexp-in-string "\"" "\"\"" sexp) "\"")) - (sexp))) - -(defun elfeed-csv-insert (seq) - "Insert a row of CSV data to the current buffer." - (cl-loop for value being the elements of seq - for column upfrom 0 - when (> column 0) - do (insert ",") - do (insert (elfeed-csv-quote value)) - finally (newline))) - -(cl-defun elfeed-csv-export (feeds-file entries-file tags-file &key headers-p) - "Create separate CSV files for feeds, entries, and tags. - -These CSV files are intended for an analysis of an Elfeed -database. They are suitable for importing as tables into a -relational database such as SQLite. Here's the recommended SQL -schema, reflecting the structure of the data. - -CREATE TABLE feeds ( - url TEXT PRIMARY KEY, - title TEXT, - canonical_url TEXT, - author TEXT -); - -CREATE TABLE entries ( - id TEXT NOT NULL, - feed TEXT NOT NULL REFERENCES feeds (url), - title TEXT, - link TEXT NOT NULL, - date REAL NOT NULL, - PRIMARY KEY (id, feed) -); - -CREATE TABLE tags ( - entry TEXT NOT NULL, - feed TEXT NOT NULL, - tag TEXT NOT NULL, - FOREIGN KEY (entry, feed) REFERENCES entries (id, feed) -);" - (let ((feeds-buffer (generate-new-buffer " *csv-feeds*")) - (entries-buffer (generate-new-buffer " *csv-entries*")) - (tags-buffer (generate-new-buffer " *csv-tags*")) - (seen (make-hash-table :test 'eq))) - ;; Write headers - (when headers-p - (with-current-buffer feeds-buffer - (elfeed-csv-insert [url title canonical-url author])) - (with-current-buffer entries-buffer - (elfeed-csv-insert [id feed title link date])) - (with-current-buffer tags-buffer - (elfeed-csv-insert [entry feed tag]))) - ;; Write data - (with-elfeed-db-visit (entry feed) - (unless (gethash feed seen) - (setf (gethash feed seen) t) - (let ((url (elfeed-feed-url feed)) - (title (elfeed-feed-title feed)) - (canonical-url (elfeed-meta feed :canonical-url)) - (author (elfeed-feed-author feed))) - (with-current-buffer feeds-buffer - (elfeed-csv-insert (list url title canonical-url author))))) - (let ((id (cdr (elfeed-entry-id entry))) - (feed-id (elfeed-entry-feed-id entry)) - (title (elfeed-entry-title entry)) - (link (elfeed-entry-link entry)) - (date (elfeed-entry-date entry))) - (with-current-buffer entries-buffer - (elfeed-csv-insert (list id feed-id title link date))) - (with-current-buffer tags-buffer - (dolist (tag (elfeed-entry-tags entry)) - (elfeed-csv-insert (list id feed-id tag)))))) - ;; Write files - (with-current-buffer tags-buffer - (write-region nil nil tags-file nil 0) - (kill-buffer)) - (with-current-buffer entries-buffer - (write-region nil nil entries-file nil 0) - (kill-buffer)) - (with-current-buffer feeds-buffer - (write-region nil nil feeds-file nil 0) - (kill-buffer)))) - -(provide 'elfeed-csv) - -;;; elfeed-csv.el ends here blob - b24d9f9b2ca39b0da4bfae9a20de85efcee76df5 (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-curl.el +++ /dev/null @@ -1,532 +0,0 @@ -;;; elfeed-curl.el --- curl backend for Elfeed -*- lexical-binding: t; -*- - -;;; Comments: - -;; An alternative to `url-retrieve' and `url-queue' that fetches URLs -;; using the curl command line program. - -;; The API is three functions: - -;; * `elfeed-curl-retrieve' -;; * `elfeed-curl-retrieve-synchronously' -;; * `elfeed-curl-enqueue' - -;; And has four buffer-local variables for use in callbacks: - -;; * `elfeed-curl-headers' -;; * `elfeed-curl-status-code' -;; * `elfeed-curl-error-message' -;; * `elfeed-curl-location' - -;; The buffer delivered to callbacks may contain multiple requests. It -;; will be narrowed to the specific content for the current request. -;; It's vitally important that callbacks do not kill the buffer -;; because it may be needed for other callbacks. It also means the -;; buffer won't necessarily be around when the callback returns. -;; Callbacks should also avoid editing the buffer, though this -;; generally shouldn't impact other requests. - -;; Sometimes Elfeed asks curl to retrieve multiple requests and -;; deliver them concatenated. Due to the possibility of HTTP/1.0 being -;; involved — and other ambiguous-length protocols — there's no -;; perfectly unambiguous way to split the output. To work around this, -;; I use curl's --write-out to insert a randomly-generated token after -;; each request. It's highly unlikely (1 in ~1e38) that this token -;; will appear in content, so I can use it to identify the end of each -;; request. - -;;; Code: - -(require 'url) -(require 'cl-lib) -(require 'elfeed-lib) -(require 'elfeed-log) - -(defcustom elfeed-curl-program-name "curl" - "Name/path by which to invoke the curl program." - :group 'elfeed - :type 'string) - -(defcustom elfeed-curl-max-connections 16 - "Maximum number of concurrent fetches." - :group 'elfeed - :type 'integer) - -(defcustom elfeed-curl-timeout 30 - "Maximum number of seconds a fetch is allowed to take once started." - :group 'elfeed - :type 'integer) - -(defcustom elfeed-curl-extra-arguments () - "A list of additional arguments to pass to cURL. -These extra arguments are appended after Elfeed's own arguments, -and care must be taken to not interfere with Elfeed's needs. The -guideline is to avoid arguments that change anything about cURL's -output format." - :group 'elfeed - :type '(repeat string)) - -(defvar elfeed-curl-queue () - "List of pending curl requests.") - -(defvar elfeed-curl-queue-active 0 - "Number of concurrent requests currently active.") - -(defvar-local elfeed-curl-headers nil - "Alist of HTTP response headers.") - -(defvar-local elfeed-curl-status-code nil - "Numeric HTTP response code, nil for non-HTTP protocols.") - -(defvar-local elfeed-curl-error-message nil - "Human-friendly message describing the error.") - -(defvar-local elfeed-curl-location nil - "Actual URL fetched (after any redirects).") - -(defvar-local elfeed-curl--regions () - "List of markers bounding separate requests.") - -(defvar-local elfeed-curl--requests () - "List of URL / callback pairs for the current buffer.") - -(defvar-local elfeed-curl--token nil - "Unique token that splits requests.") - -(defvar-local elfeed-curl--refcount nil - "Number of callbacks waiting on the current buffer.") - -(defvar elfeed-curl--error-codes - '((1 . "Unsupported protocol.") - (2 . "Failed to initialize.") - (3 . "URL malformed. The syntax was not correct.") - (4 . "A feature or option that was needed to perform the desired request was not enabled or was explicitly disabled at build-time.") - (5 . "Couldn't resolve proxy. The given proxy host could not be resolved.") - (6 . "Couldn't resolve host. The given remote host was not resolved.") - (7 . "Failed to connect to host.") - (8 . "FTP weird server reply. The server sent data curl couldn't parse.") - (9 . "FTP access denied.") - (11 . "FTP weird PASS reply.") - (13 . "FTP weird PASV reply.") - (14 . "FTP weird 227 format.") - (15 . "FTP can't get host.") - (16 . "A problem was detected in the HTTP2 framing layer.") - (17 . "FTP couldn't set binary.") - (18 . "Partial file. Only a part of the file was transferred.") - (19 . "FTP couldn't download/access the given file, the RETR (or similar) command failed.") - (21 . "FTP quote error. A quote command returned error from the server.") - (22 . "HTTP page not retrieved.") - (23 . "Write error.") - (25 . "FTP couldn't STOR file.") - (26 . "Read error. Various reading problems.") - (27 . "Out of memory. A memory allocation request failed.") - (28 . "Operation timeout.") - (30 . "FTP PORT failed.") - (31 . "FTP couldn't use REST.") - (33 . "HTTP range error. The range \"command\" didn't work.") - (34 . "HTTP post error. Internal post-request generation error.") - (35 . "SSL connect error. The SSL handshaking failed.") - (36 . "FTP bad download resume.") - (37 . "FILE couldn't read file.") - (38 . "LDAP bind operation failed.") - (39 . "LDAP search failed.") - (41 . "Function not found. A required LDAP function was not found.") - (42 . "Aborted by callback.") - (43 . "Internal error. A function was called with a bad parameter.") - (45 . "Interface error. A specified outgoing interface could not be used.") - (47 . "Too many redirects.") - (48 . "Unknown option specified to libcurl.") - (49 . "Malformed telnet option.") - (51 . "The peer's SSL certificate or SSH MD5 fingerprint was not OK.") - (52 . "The server didn't reply anything, which here is considered an error.") - (53 . "SSL crypto engine not found.") - (54 . "Cannot set SSL crypto engine as default.") - (55 . "Failed sending network data.") - (56 . "Failure in receiving network data.") - (58 . "Problem with the local certificate.") - (59 . "Couldn't use specified SSL cipher.") - (60 . "Peer certificate cannot be authenticated with known CA certificates.") - (61 . "Unrecognized transfer encoding.") - (62 . "Invalid LDAP URL.") - (63 . "Maximum file size exceeded.") - (64 . "Requested FTP SSL level failed.") - (65 . "Sending the data requires a rewind that failed.") - (66 . "Failed to initialise SSL Engine.") - (67 . "The user name, password, or similar was not accepted and curl failed to log in.") - (68 . "File not found on TFTP server.") - (69 . "Permission problem on TFTP server.") - (70 . "Out of disk space on TFTP server.") - (71 . "Illegal TFTP operation.") - (72 . "Unknown TFTP transfer ID.") - (73 . "File already exists (TFTP).") - (74 . "No such user (TFTP).") - (75 . "Character conversion failed.") - (76 . "Character conversion functions required.") - (77 . "Problem with reading the SSL CA cert (path? access rights?).") - (78 . "The resource referenced in the URL does not exist.") - (79 . "An unspecified error occurred during the SSH session.") - (80 . "Failed to shut down the SSL connection.") - (82 . "Could not load CRL file, missing or wrong format (added in 7.19.0).") - (83 . "Issuer check failed (added in 7.19.0).") - (84 . "The FTP PRET command failed") - (85 . "RTSP: mismatch of CSeq numbers") - (86 . "RTSP: mismatch of Session Identifiers") - (87 . "unable to parse FTP file list") - (88 . "FTP chunk callback reported error") - (89 . "No connection available, the session will be queued") - (90 . "SSL public key does not matched pinned public key"))) - -(defvar elfeed-curl--capabilities-cache - (make-hash-table :test 'eq :weakness 'key) - "Used to avoid invoking curl more than once for version info.") - -(defun elfeed-curl-get-capabilities () - "Return capabilities plist for the curl at `elfeed-curl-program-name'. -:version -- cURL's version string -:compression -- non-nil if --compressed is supported -:protocols -- symbol list of supported protocols -:features -- string list of supported features" - (let* ((cache elfeed-curl--capabilities-cache) - (cache-value (gethash elfeed-curl-program-name cache))) - (if cache-value - cache-value - (with-temp-buffer - (call-process elfeed-curl-program-name nil t nil "--version") - (let ((version - (progn - (setf (point) (point-min)) - (when (re-search-forward "[.0-9]+" nil t) - (match-string 0)))) - (protocols - (progn - (setf (point) (point-min)) - (when (re-search-forward "^Protocols: \\(.*\\)$" nil t) - (mapcar #'intern (split-string (match-string 1)))))) - (features - (progn - (setf (point) (point-min)) - (when (re-search-forward "^Features: \\(.*\\)$") - (split-string (match-string 1)))))) - (setf (gethash elfeed-curl-program-name cache) - (list :version version - :compression (not (null (member "libz" features))) - :protocols protocols - :features features))))))) - -(defun elfeed-curl-get-version () - "Return the version of curl for `elfeed-curl-program-name'." - (plist-get (elfeed-curl-get-capabilities) :version)) -(make-obsolete 'elfeed-curl-get-version 'elfeed-curl-get-capabilities "3.0.1") - -(defun elfeed-curl--token () - "Return a unique, random string that prints as a symbol without escapes. -This token is used to split requests. The % is excluded since -it's special to --write-out." - (let* ((token (make-string 22 ?=)) - (set "!$&*+-/0123456789:<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_\ -abcdefghijklmnopqrstuvwxyz|~")) - (prog1 token ; workaround bug#16206 - (dotimes (i (- (length token) 2)) - (setf (aref token (1+ i)) (aref set (cl-random (length set)))))))) - -(defun elfeed-curl--parse-write-out () - "Parse curl's write-out (-w) messages into `elfeed-curl--regions'." - (widen) - (setf (point) (point-max) - elfeed-curl--regions ()) - (while (> (point) (point-min)) - (search-backward elfeed-curl--token) - (cl-decf (point)) - (let ((end (point))) - (cl-destructuring-bind (_ . header) (read (current-buffer)) - (setf (point) end) - ;; Find next sentinel token - (if (search-backward elfeed-curl--token nil t) - (search-forward ")" nil t) - (setf (point) (point-min))) - (let* ((header-start (point)) - (header-end (+ (point) header)) - (content-start (+ (point) header)) - (content-end end) - (regions (list header-start header-end - content-start content-end)) - (markers (cl-loop for p in regions - for marker = (make-marker) - collect (set-marker marker p)))) - (push markers elfeed-curl--regions)))))) - -(defun elfeed-curl--narrow (kind n) - "Narrow to Nth region of KIND (:header, :content)." - (let ((region (nth n elfeed-curl--regions))) - (cl-destructuring-bind (h-start h-end c-start c-end) region - (cl-ecase kind - (:header (narrow-to-region h-start h-end)) - (:content (narrow-to-region c-start c-end)))))) - -(defun elfeed-curl--parse-http-headers () - "Parse the current HTTP response headers into buffer-locals. -Sets `elfeed-curl-headers'and `elfeed-curl-status-code'. -Use `elfeed-curl--narrow' to select a header." - (when (> (- (point-max) (point-min)) 0) - (setf (point) (point-max)) - (re-search-backward "HTTP/[.0-9]+ +\\([0-9]+\\)") - (setf elfeed-curl-status-code (string-to-number (match-string 1))) - (cl-loop initially (setf (point) (point-max)) - while (re-search-backward "^\\([^:]+\\): +\\([^\r\n]+\\)" nil t) - for key = (downcase (match-string 1)) - for value = (match-string 2) - collect (cons key value) into headers - finally (setf elfeed-curl-headers headers)))) - -(defun elfeed-curl--decode () - "Try to decode the buffer based on the headers." - (let ((content-type (cdr (assoc "Content-Type" elfeed-curl-headers)))) - (if (and content-type (string-match "charset=\\(.+\\)" content-type)) - (decode-coding-region (point-min) (point-max) - (coding-system-from-name - (match-string 1 content-type))) - (decode-coding-region (point-min) (point-max) 'utf-8)))) - -(defun elfeed-curl--final-location (location headers) - "Given start LOCATION and HEADERS, find the final location." - (cl-loop for (key . value) in headers - when (equal key "location") - do (setf location (elfeed-update-location location value)) - finally return location)) - -(defun elfeed-curl--args (url token &optional headers method data) - "Build an argument list for curl for URL. -URL can be a string or a list of URL strings." - (let* ((args ()) - (capabilities (elfeed-curl-get-capabilities))) - (push "--disable" args) - (when (plist-get capabilities :compression) - (push "--compressed" args)) - (push "--silent" args) - (push "--location" args) - (push (format "-w(%s . %%{size_header})" token) args) - (push (format "-m%s" elfeed-curl-timeout) args) - (push "-D-" args) - (dolist (header headers) - (cl-destructuring-bind (key . value) header - (push (format "-H%s: %s" key value) args))) - (when method (push (format "-X%s" method) args)) - (when data (push (format "-d%s" data) args)) - (setf args (nconc (reverse elfeed-curl-extra-arguments) args)) - (if (listp url) - (nconc (nreverse args) url) - (nreverse (cons url args))))) - -(defun elfeed-curl--prepare-response (url n protocol) - "Prepare response N for delivery to user." - (elfeed-curl--narrow :header n) - (when (eq protocol 'http) - (elfeed-curl--parse-http-headers)) - (setf elfeed-curl-location - (elfeed-curl--final-location url elfeed-curl-headers)) - (elfeed-curl--narrow :content n) - (elfeed-curl--decode) - (current-buffer)) - -(cl-defun elfeed-curl-retrieve-synchronously (url &key headers method data) - "Retrieve the contents for URL and return a new buffer with them. - -HEADERS is an alist of additional headers to add to the HTTP request. -METHOD is the HTTP method to use. -DATA is the content to include in the request." - (with-current-buffer (generate-new-buffer " *curl*") - (setf elfeed-curl--token (elfeed-curl--token)) - (let ((args (elfeed-curl--args url elfeed-curl--token headers method data)) - (coding-system-for-read 'binary)) - (apply #'call-process elfeed-curl-program-name nil t nil args)) - (elfeed-curl--parse-write-out) - (elfeed-curl--prepare-response url 0 (elfeed-curl--protocol-type url)))) - -(defun elfeed-curl--protocol-type (url) - (let ((scheme (intern (or (url-type (url-generic-parse-url url)) "nil")))) - (cl-case scheme - ((https nil) 'http) - (otherwise scheme)))) - -(defun elfeed-curl--call-callback (buffer n url cb) - "Prepare the buffer for callback N and call it." - (let ((result nil) - (protocol (elfeed-curl--protocol-type url))) - (with-current-buffer buffer - (setf elfeed-curl-error-message "unable to parse curl response") - (unwind-protect - (progn - (elfeed-curl--prepare-response url n protocol) - (cond ((eq protocol 'file) - ;; No status code is returned by curl for file:// urls - (setf result t - elfeed-curl-error-message nil)) - ((eq protocol 'gopher) - (setf result t - elfeed-curl-error-message nil - elfeed-curl-status-code nil)) - ((and (>= elfeed-curl-status-code 400) - (<= elfeed-curl-status-code 599)) - (setf elfeed-curl-error-message - (format "HTTP %d" elfeed-curl-status-code))) - (t - (setf result t - elfeed-curl-error-message nil))) - ;; Always call callback - (unwind-protect - (funcall cb result) - ;; Always clean up - (when (zerop (cl-decf elfeed-curl--refcount)) - (kill-buffer)))))))) - -(defun elfeed-curl--fail-callback (buffer cb) - "Inform the callback the request failed." - (with-current-buffer buffer - (unwind-protect - (funcall cb nil) - (when (zerop (cl-decf elfeed-curl--refcount)) - (kill-buffer))))) - -(defun elfeed-curl--sentinel (process status) - "Manage the end of a curl process' life." - (let ((buffer (process-buffer process))) - (with-current-buffer buffer - ;; Fire off callbacks in separate interpreter turns so they can - ;; each fail in isolation from each other. - (if (equal status "finished\n") - (cl-loop with handler = #'elfeed-curl--call-callback - initially do (elfeed-curl--parse-write-out) - for (url . cb) in elfeed-curl--requests - for n upfrom 0 - do (run-at-time 0 nil handler buffer n url cb)) - (if (string-match "exited abnormally with code \\([0-9]+\\)" status) - (let* ((code (string-to-number (match-string 1 status))) - (message (cdr (assoc code elfeed-curl--error-codes)))) - (setf elfeed-curl-error-message - (format "(%d) %s" code - (or message "Unknown curl error!")))) - (setf elfeed-curl-error-message status)) - (cl-loop with handler = #'elfeed-curl--fail-callback - for (_ . cb) in elfeed-curl--requests - do (run-at-time 0 nil handler buffer cb)))))) - -(cl-defun elfeed-curl-retrieve (url cb &key headers method data) - "Retrieve URL contents asynchronously, calling CB with one status argument. - -The callback must *not* kill the buffer! - -The destination buffer is set at the current buffer for the -callback. - -HEADERS is an alist of additional headers to add to HTTP requests. -METHOD is the HTTP method to use. -DATA is the content to include in the request. - -URL can be a list of URLs, which will fetch them all in the same -curl process. In this case, CB can also be either a list of the -same length, or just a single function to be called once for each -URL in the list. Headers will be common to all requests. A TCP or -DNS failure in one will cause all to fail, but 4xx and 5xx -results will not." - (with-current-buffer (generate-new-buffer " *curl*") - (setf elfeed-curl--token (elfeed-curl--token)) - (let* ((coding-system-for-read 'binary) - (process-connection-type nil) - (args (elfeed-curl--args url elfeed-curl--token headers method data)) - (process (apply #'start-process "elfeed-curl" (current-buffer) - elfeed-curl-program-name args))) - (prog1 process - (if (listp url) - (progn - (when (functionp cb) - (setf cb (make-list (length url) cb))) - (setf elfeed-curl--requests (cl-mapcar #'cons url cb) - elfeed-curl--refcount (length url))) - (push (cons url cb) elfeed-curl--requests) - (setf elfeed-curl--refcount 1)) - (set-process-query-on-exit-flag process nil) - (setf (process-sentinel process) #'elfeed-curl--sentinel))))) - -(defun elfeed-curl--request-key (url headers method data) - "Try to fetch URLs with matching keys at the same time." - (unless (listp url) - (let* ((urlobj (url-generic-parse-url url))) - (list (url-type urlobj) - (url-host urlobj) - (url-portspec urlobj) - headers - method - data)))) - -(defun elfeed-curl--queue-consolidate (queue-in) - "Group compatible requests together and return a new queue. -Compatible means the requests have the same protocol, domain, -port, headers, method, and body, allowing them to be used safely -in the same curl invocation." - (let ((table (make-hash-table :test 'equal)) - (keys ()) - (queue-out ())) - (dolist (entry queue-in) - (cl-destructuring-bind (url _ headers method data) entry - (let* ((key (elfeed-curl--request-key url headers method data))) - (push key keys) - (push entry (gethash key table nil))))) - (dolist (key (nreverse keys)) - (let ((entry (gethash key table))) - (when entry - (let ((rotated (list (nreverse (cl-mapcar #'car entry)) - (nreverse (cl-mapcar #'cadr entry)) - (cl-caddar entry) - (elt (car entry) 3) - (elt (car entry) 4)))) - (push rotated queue-out) - (setf (gethash key table) nil))))) - (nreverse queue-out))) - -(defun elfeed-curl--queue-wrap (cb) - "Wrap the curl CB so that it operates the queue." - (lambda (status) - (cl-decf elfeed-curl-queue-active) - (elfeed-curl--run-queue) - (funcall cb status))) - -(defvar elfeed-curl--run-queue-queued nil - "Non-nil if run-queue has already been queued for the next turn.") - -(defun elfeed-curl--run-queue () - "Possibly fire off some new requests." - (when elfeed-curl--run-queue-queued - (setf elfeed-curl--run-queue-queued nil - ;; Try to consolidate the new requests. - elfeed-curl-queue - (elfeed-curl--queue-consolidate elfeed-curl-queue))) - (while (and (< elfeed-curl-queue-active elfeed-curl-max-connections) - (> (length elfeed-curl-queue) 0)) - (cl-destructuring-bind (url cb headers method data) (pop elfeed-curl-queue) - (elfeed-log 'debug "retrieve %s" url) - (cl-incf elfeed-curl-queue-active 1) - (elfeed-curl-retrieve - url - (if (functionp cb) - (elfeed-curl--queue-wrap cb) - (cons (elfeed-curl--queue-wrap (car cb)) - (cdr cb))) - :headers headers - :method method - :data data)))) - -(cl-defun elfeed-curl-enqueue (url cb &key headers method data) - "Just like `elfeed-curl-retrieve', but restricts concurrent fetches." - (unless (or (stringp url) - (and (listp url) (cl-every #'stringp url))) - ;; Signal error synchronously instead of asynchronously in the timer - (signal 'wrong-type-argument (list 'string-p-or-string-list-p url))) - (let ((entry (list url cb headers method data))) - (setf elfeed-curl-queue (nconc elfeed-curl-queue (list entry))) - (unless elfeed-curl--run-queue-queued - (run-at-time 0 nil #'elfeed-curl--run-queue) - (setf elfeed-curl--run-queue-queued t)))) - -(provide 'elfeed-curl) - -;;; elfeed-curl.el ends here blob - 4000a0bb900b562f546be94d77a29025fe109cfb (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-db.el +++ /dev/null @@ -1,646 +0,0 @@ -;;; elfeed-db.el --- database and model for elfeed -*- lexical-binding: t; -*- - -;; This is free and unencumbered software released into the public domain. - -;;; Commentary: - -;; Elfeed is aware of two type of things: feeds and entries. All dates -;; are stored as floating point epoch seconds. - -;; Feeds are keyed by their user-provided feed URL, which acts as the -;; feed identity regardless of any other stated identity. Feeds have a -;; list of entries. - -;; Entries are keyed in order of preference by id (Atom), guid (RSS), -;; or link. To avoid circular references, entries refer to their -;; parent feeds by URL. - -;; Feed content is stored in a content-addressable loose-file -;; database, very similar to an unpacked Git object database. Entries -;; have references to items in this database (elfeed-ref), keeping the -;; actual entry struct memory footprint small. Most importantly, this -;; keeps the core index small so that it can quickly be written as a -;; whole to the filesystem. The wire format is just the s-expression -;; print form of the top-level hash table. - -;; The database can be compacted into a small number of compressed -;; files with the interactive function `elfeed-db-compact'. This could -;; be used as a kill-emacs hook. - -;; An AVL tree containing all database entries ordered by date is -;; maintained as part of the database. We almost always want to look -;; at entries ordered by date and this step accomplished that very -;; efficiently with the AVL tree. This is the reasoning behind the -;; `with-elfeed-db-visit' interface. - -;; Unfortunately there's a nasty bug (bug#15190) in the reader that -;; makes hash tables and `print-circle' incompatible. It's been fixed -;; in trunk, but many users will likely be stuck with this bug for the -;; next few years. This means the database format can't exploit -;; circular references. - -;; Entry and feed objects can have arbitrary metadata attached, -;; automatically stored in the database. The setf-able `elfeed-meta' -;; function is used to access these. - -;;; Code: - -(require 'cl-lib) -(require 'avl-tree) -(require 'elfeed-lib) - -(defcustom elfeed-db-directory "~/.elfeed" - "Directory where elfeed will store its database." - :group 'elfeed - :type 'directory) - -(defvar elfeed-db nil - "The core database for elfeed.") - -(defvar elfeed-db-feeds nil - "Feeds hash table, part of `elfeed-db'.") - -(defvar elfeed-db-entries nil - "Entries hash table, part of `elfeed-db'.") - -(defvar elfeed-db-index nil - "Collection of all entries sorted by date, part of `elfeed-db'.") - -(defvar elfeed-db-version - ;; If records are avaiable (Emacs 26), use the newer database format - (if (functionp 'record) - 4 - "0.0.3") - "The database version this version of Elfeed expects to use.") - -(defvar elfeed-new-entry-hook () - "Functions in this list are called with the new entry as its argument. -This is a chance to add custom tags to new entries.") - -(defvar elfeed-db-update-hook () - "Functions in this list are called with no arguments any time -the :last-update time is updated.") - -(defvar elfeed-db-unload-hook () - "Hook to run immediately after `elfeed-db-unload'.") - -;; Data model: - -(cl-defstruct (elfeed-feed (:constructor elfeed-feed--create)) - "A web feed, contains elfeed-entry structs." - id url title author meta) - -(cl-defstruct (elfeed-entry (:constructor elfeed-entry--create)) - "A single entry from a feed, normalized towards Atom." - id title link date content content-type enclosures tags feed-id meta) - -(defun elfeed-entry-merge (a b) - "Merge B into A, preserving A's tags. Return true if an actual -update occurred, not counting content." - (setf (elfeed-entry-tags b) (elfeed-entry-tags a) - (elfeed-entry-content a) (elfeed-entry-content b)) - (cl-loop for (key value) on (elfeed-entry-meta b) by #'cddr - do (setf (elfeed-entry-meta a) - (plist-put (elfeed-entry-meta a) key value))) - (not - (zerop - (cl-loop for i from 1 below (1- (length a)) - for part-a = (aref a i) - for part-b = (aref b i) - count (not (equal part-a part-b)) - do (setf (aref a i) part-b))))) - -(defun elfeed-db-get-feed (id) - "Get/create the feed for ID." - (elfeed-db-ensure) - (let ((feed (gethash id elfeed-db-feeds))) - (or feed - (setf (gethash id elfeed-db-feeds) - (elfeed-feed--create :id id))))) - -(defun elfeed-db-get-entry (id) - "Get the entry for ID." - (elfeed-db-ensure) - (gethash id elfeed-db-entries)) - -(defun elfeed-db-compare (a b) - "Return true if entry A is newer than entry B." - (let* ((entry-a (elfeed-db-get-entry a)) - (entry-b (elfeed-db-get-entry b)) - (date-a (elfeed-entry-date entry-a)) - (date-b (elfeed-entry-date entry-b))) - (if (= date-a date-b) - (string< (prin1-to-string b) (prin1-to-string a)) - (> date-a date-b)))) - -(defun elfeed-db-set-update-time () - "Update the database last-update time." - (setf elfeed-db (plist-put elfeed-db :last-update (float-time))) - (run-hooks 'elfeed-db-update-hook)) - -(defun elfeed-db-add (entries) - "Add ENTRIES to the database." - (elfeed-db-ensure) - (cl-loop for entry in entries - for id = (elfeed-entry-id entry) - for original = (gethash id elfeed-db-entries) - for new-date = (elfeed-entry-date entry) - for original-date = (and original (elfeed-entry-date original)) - do (elfeed-deref-entry entry) - when original count - (if (= new-date original-date) - (elfeed-entry-merge original entry) - (avl-tree-delete elfeed-db-index id) - (prog1 (elfeed-entry-merge original entry) - (avl-tree-enter elfeed-db-index id))) - into change-count - else count - (setf (gethash id elfeed-db-entries) entry) - into change-count - and do - (progn - (avl-tree-enter elfeed-db-index id) - (cl-loop for hook in elfeed-new-entry-hook - do (funcall hook entry))) - finally - (unless (zerop change-count) - (elfeed-db-set-update-time))) - :success) - -(defun elfeed-entry-feed (entry) - "Get the feed struct for ENTRY." - (elfeed-db-get-feed (elfeed-entry-feed-id entry))) - -(defun elfeed-normalize-tags (tags &rest more-tags) - "Return the normalized tag list for TAGS." - (let ((all (apply #'append tags (nconc more-tags (list ()))))) - (cl-delete-duplicates (cl-sort all #'string< :key #'symbol-name)))) - -(defun elfeed-tag-1 (entry &rest tags) - "Add TAGS to ENTRY." - (let ((current (elfeed-entry-tags entry))) - (setf (elfeed-entry-tags entry) - (elfeed-normalize-tags (append tags current))))) - -(defun elfeed-untag-1 (entry &rest tags) - "Remove TAGS from ENTRY." - (setf (elfeed-entry-tags entry) - (cl-loop for tag in (elfeed-entry-tags entry) - unless (memq tag tags) collect tag))) - -(defun elfeed-tag (entry-or-entry-list &rest tags) - "Add TAGS to ENTRY-OR-ENTRY-LIST and run `elfeed-tag-hooks'." - (let* ((entries (if (elfeed-entry-p entry-or-entry-list) - (list entry-or-entry-list) - entry-or-entry-list))) - (run-hook-with-args 'elfeed-tag-hooks entries tags) - (cl-loop for entry in entries do (apply #'elfeed-tag-1 entry tags)))) - -(defun elfeed-untag (entry-or-entry-list &rest tags) - "Remove TAGS from ENTRY-OR-ENTRY-LIST and run `elfeed-untag-hooks'." - (let* ((entries (if (elfeed-entry-p entry-or-entry-list) - (list entry-or-entry-list) - entry-or-entry-list))) - (run-hook-with-args 'elfeed-untag-hooks entries tags) - (cl-loop for entry in entries do (apply #'elfeed-untag-1 entry tags)))) - -(defun elfeed-tagged-p (tag entry) - "Return true if ENTRY is tagged by TAG." - (memq tag (elfeed-entry-tags entry))) - -(defun elfeed-db-last-update () - "Return the last database update time in (`float-time') seconds." - (elfeed-db-ensure) - (or (plist-get elfeed-db :last-update) 0)) - -(defmacro with-elfeed-db-visit (entry-and-feed &rest body) - "Visit each entry in the database from newest to oldest. -Use `elfeed-db-return' to exit early and optionally return data. - - (with-elfeed-db-visit (entry feed) - (do-something entry) - (when (some-date-criteria-p entry) - (elfeed-db-return)))" - (declare (indent defun)) - `(catch 'elfeed-db-done - (prog1 nil - (elfeed-db-ensure) - (avl-tree-mapc - (lambda (id) - (let* ((,(cl-first entry-and-feed) (elfeed-db-get-entry id)) - (,(cl-second entry-and-feed) - (elfeed-entry-feed ,(cl-first entry-and-feed)))) - ,@body)) - elfeed-db-index)))) - -(defun elfeed-feed-entries (feed-or-id) - "Return a list of all entries for a particular feed. -The FEED-OR-ID may be a feed struct or a feed ID (url)." - (let ((feed-id (if (elfeed-feed-p feed-or-id) - (elfeed-feed-id feed-or-id) - feed-or-id))) - (let ((entries)) - (with-elfeed-db-visit (entry feed) - (when (equal (elfeed-feed-id feed) feed-id) - (push entry entries))) - (nreverse entries)))) - -(defun elfeed-apply-hooks-now () - "Apply `elfeed-new-entry-hook' to all entries in the database." - (interactive) - (with-elfeed-db-visit (entry _) - (cl-loop for hook in elfeed-new-entry-hook - do (funcall hook entry)))) - -(defmacro elfeed-db-return (&optional value) - "Use this to exit early and return VALUE from `with-elfeed-db-visit'." - `(throw 'elfeed-db-done ,value)) - -(defun elfeed-db-get-all-tags () - "Return a list of all tags currently in the database." - (let ((table (make-hash-table :test 'eq))) - (with-elfeed-db-visit (e _) - (dolist (tag (elfeed-entry-tags e)) - (setf (gethash tag table) tag))) - (let ((tags ())) - (maphash (lambda (k _) (push k tags)) table) - (cl-sort tags #'string< :key #'symbol-name)))) - -;; Saving and Loading: - -(defun elfeed-db-save () - "Write the database index to the filesystem." - (elfeed-db-ensure) - (setf elfeed-db (plist-put elfeed-db :version elfeed-db-version)) - (mkdir elfeed-db-directory t) - (let ((coding-system-for-write 'utf-8)) - (with-temp-file (expand-file-name "index" elfeed-db-directory) - (let ((standard-output (current-buffer)) - (print-level nil) - (print-length nil) - (print-circle nil)) - (princ (format ";;; Elfeed Database Index (version %s)\n\n" - elfeed-db-version)) - (when (eql elfeed-db-version 4) - ;; Put empty dummy index in front - (princ ";; Dummy index for backwards compatablity:\n") - (prin1 (elfeed-db--dummy)) - (princ "\n\n;; Real index:\n")) - (prin1 elfeed-db) - :success)))) - -(defun elfeed-db-save-safe () - "Run `elfeed-db-save' without triggering any errors, for use as a safe hook." - (ignore-errors (elfeed-db-save))) - -(defun elfeed-db-upgrade (db) - "Upgrade the database from a previous format." - (if (not (vectorp (plist-get db :index))) - db ; Database is already in record format - (let* ((new-db (elfeed-db--empty)) - ;; Dynamically bind for other functions - (elfeed-db-feeds (plist-get new-db :feeds)) - (elfeed-db-entries (plist-get new-db :entries)) - (elfeed-db-index (plist-get new-db :index))) - ;; Fix up feeds - (cl-loop with table = (plist-get new-db :feeds) - for feed hash-values of (plist-get db :feeds) - for id = (aref feed 1) - for fixed = (elfeed-feed--create - :id id - :url (aref feed 2) - :title (aref feed 3) - :author (aref feed 4) - :meta (aref feed 5)) - do (setf (gethash id table) fixed)) - ;; Fix up entries - (cl-loop with table = (plist-get new-db :entries) - with index = (plist-get new-db :index) - for entry hash-values of (plist-get db :entries) - for id = (aref entry 1) - for content = (aref entry 5) - for fixed = (elfeed-entry--create - :id id - :title (aref entry 2) - :link (aref entry 3) - :date (aref entry 4) - :content (if (vectorp content) - (elfeed-ref--create - :id (aref content 1)) - content) - :content-type (aref entry 6) - :enclosures (aref entry 7) - :tags (aref entry 8) - :feed-id (aref entry 9) - :meta (aref entry 10)) - do (setf (gethash id table) fixed) - do (avl-tree-enter index id)) - (plist-put new-db :last-update (plist-get db :last-update))))) - -(defun elfeed-db--empty () - "Create an empty database object." - `(:version ,elfeed-db-version - :feeds ,(make-hash-table :test 'equal) - :entries ,(make-hash-table :test 'equal) - ;; Compiler may warn about this (bug#15327): - :index ,(avl-tree-create #'elfeed-db-compare))) - -(defun elfeed-db--dummy () - "Create an empty dummy database for Emacs 25 and earlier." - (list :version "0.0.3" - :feeds #s(hash-table size 65 - test equal - rehash-size 1.5 - rehash-threshold 0.8 - data ()) - :entries #s(hash-table size 65 - test equal - rehash-size 1.5 - rehash-threshold 0.8 - data ()) - :index [cl-struct-avl-tree- [nil nil nil 0] elfeed-db-compare])) - -;; To cope with the incompatible struct changes in Emacs 26, Elfeed -;; uses version 4 of the database format when run under Emacs 26. This -;; version saves a dummy, empty index in front of the real database. A -;; user going from Emacs 26 to Emacs 25 will quietly load an empty -;; index since it's unreasonable to downgrade (would require rewriting -;; the Emacs reader from scratch). - -(defun elfeed-db-load () - "Load the database index from the filesystem." - (let ((index (expand-file-name "index" elfeed-db-directory)) - (enable-local-variables nil)) ; don't set local variables from index! - (if (not (file-exists-p index)) - (setf elfeed-db (elfeed-db--empty)) - ;; Override the default value for major-mode. There is no - ;; preventing find-file-noselect from starting the default major - ;; mode while also having it handle buffer conversion. Some - ;; major modes crash Emacs when enabled in large buffers (e.g. - ;; org-mode). This includes the Elfeed index, so we must not let - ;; this happen. - (cl-letf (((default-value 'major-mode) 'fundamental-mode)) - (with-current-buffer (find-file-noselect index :nowarn) - (goto-char (point-min)) - (if (eql elfeed-db-version 4) - ;; May need to skip over dummy database - (let ((db-1 (read (current-buffer))) - (db-2 (ignore-errors (read (current-buffer))))) - (setf elfeed-db (or db-2 db-1))) - ;; Just load first database - (setf elfeed-db (read (current-buffer)))) - (kill-buffer)))) - ;; Perform an upgrade if necessary and possible - (unless (equal (plist-get elfeed-db :version) elfeed-db-version) - (ignore-errors - (copy-file index (concat index ".backup"))) - (message "Upgrading Elfeed index for Emacs 26 ...") - (setf elfeed-db (elfeed-db-upgrade elfeed-db)) - (message "Elfeed index upgrade complete.")) - (setf elfeed-db-feeds (plist-get elfeed-db :feeds) - elfeed-db-entries (plist-get elfeed-db :entries) - elfeed-db-index (plist-get elfeed-db :index) - ;; Internal function use required for security! - (avl-tree--cmpfun elfeed-db-index) #'elfeed-db-compare))) - -(defun elfeed-db-unload () - "Unload the database so that it can be operated on externally. - -Runs `elfeed-db-unload-hook' after unloading the database." - (interactive) - (elfeed-db-save) - (setf elfeed-db nil - elfeed-db-feeds nil - elfeed-db-entries nil - elfeed-db-index nil) - (run-hooks 'elfeed-db-unload-hook)) - -(defun elfeed-db-ensure () - "Ensure that the database has been loaded." - (when (null elfeed-db) (elfeed-db-load))) - -(defun elfeed-db-size () - "Return a count of the number of entries in the database." - (let ((count-table (hash-table-count elfeed-db-entries)) - (count-tree (avl-tree-size elfeed-db-index))) - (if (= count-table count-tree) - count-table - (error "Elfeed database error: entry count mismatch.")))) - -;; Metadata: - -(defun elfeed-meta--plist (thing) - "Get the metadata plist for THING." - (cl-typecase thing - (elfeed-feed (elfeed-feed-meta thing)) - (elfeed-entry (elfeed-entry-meta thing)) - (otherwise (error "Don't know how to access metadata on %S" thing)))) - -(defun elfeed-meta--set-plist (thing plist) - "Set the metadata plist on THING to PLIST." - (cl-typecase thing - (elfeed-feed (setf (elfeed-feed-meta thing) plist)) - (elfeed-entry (setf (elfeed-entry-meta thing) plist)) - (otherwise (error "Don't know how to access metadata on %S" thing)))) - -(defun elfeed-db--plist-fixup (plist) - "Remove nil values from PLIST." - (cl-loop for (k v) on plist by #'cddr - when (not (null v)) - collect k and collect v)) - -(defun elfeed-meta (thing key &optional default) - "Access metadata for THING (entry, feed) under KEY." - (or (plist-get (elfeed-meta--plist thing) key) - default)) - -(defun elfeed-meta--put (thing key value) - "Set metadata to VALUE on THING under KEY." - (when (not (elfeed-readable-p value)) (error "New value must be readable.")) - (let ((new-plist (plist-put (elfeed-meta--plist thing) key value))) - (prog1 value - (elfeed-meta--set-plist thing (elfeed-db--plist-fixup new-plist))))) - -(gv-define-setter elfeed-meta (value thing key &optional _default) - `(elfeed-meta--put ,thing ,key ,value)) - -;; Filesystem storage: - -(defvar elfeed-ref-archive nil - "Index of archived/packed content.") - -(defvar elfeed-ref-cache nil - "Temporary storage of the full archive content.") - -(cl-defstruct (elfeed-ref (:constructor elfeed-ref--create)) - id) - -(defun elfeed-ref--file (ref) - "Determine the storage filename for REF." - (let* ((id (elfeed-ref-id ref)) - (root (expand-file-name "data" elfeed-db-directory)) - (subdir (expand-file-name (substring id 0 2) root))) - (expand-file-name id subdir))) - -(cl-defun elfeed-ref-archive-filename (&optional (suffix "")) - "Return the base filename of the archive files." - (concat (expand-file-name "data/archive" elfeed-db-directory) suffix)) - -(defun elfeed-ref-archive-load () - "Load the archived ref index." - (let ((archive-index (elfeed-ref-archive-filename ".index"))) - (if (file-exists-p archive-index) - (with-temp-buffer - (insert-file-contents archive-index) - (setf elfeed-ref-archive (read (current-buffer)))) - (setf elfeed-ref-archive :empty)))) - -(defun elfeed-ref-archive-ensure () - "Ensure that the archive index is loaded." - (when (null elfeed-ref-archive) (elfeed-ref-archive-load))) - -(defun elfeed-ref-exists-p (ref) - "Return true if REF can be dereferenced." - (elfeed-ref-archive-ensure) - (or (and (hash-table-p elfeed-ref-archive) - (not (null (gethash (elfeed-ref-id ref) elfeed-ref-archive)))) - (file-exists-p (elfeed-ref--file ref)))) - -(defun elfeed-deref (ref) - "Fetch the content behind the reference, or nil if non-existent." - (elfeed-ref-archive-ensure) - (if (not (elfeed-ref-p ref)) - ref - (let ((index (and (hash-table-p elfeed-ref-archive) - (gethash (elfeed-ref-id ref) elfeed-ref-archive))) - (archive-file (elfeed-ref-archive-filename ".gz")) - (coding-system-for-read 'utf-8)) - (if (and index (file-exists-p archive-file)) - (progn - (when (null elfeed-ref-cache) - (with-temp-buffer - (insert-file-contents archive-file) - (setf elfeed-ref-cache (buffer-string))) - ;; Clear cache on next turn. - (run-at-time 0 nil (lambda () (setf elfeed-ref-cache nil)))) - (substring elfeed-ref-cache (car index) (cdr index))) - (let ((file (elfeed-ref--file ref))) - (when (file-exists-p file) - (with-temp-buffer - (insert-file-contents file) - (buffer-string)))))))) - -(defun elfeed-ref (content) - "Create a reference to CONTENT, to be persistently stored." - (if (elfeed-ref-p content) - content - (let* ((id (secure-hash 'sha1 (encode-coding-string content 'utf-8 t))) - (ref (elfeed-ref--create :id id)) - (file (elfeed-ref--file ref))) - (prog1 ref - (unless (elfeed-ref-exists-p ref) - (mkdir (file-name-directory file) t) - (let ((coding-system-for-write 'utf-8) - ;; Content data loss is a tolerable risk. - ;; Fsync will occur soon on index write anyway. - (write-region-inhibit-fsync t)) - (with-temp-file file - (insert content)))))))) - -(defun elfeed-deref-entry (entry) - "Move ENTRY's content to filesystem storage. Return the entry." - (let ((content (elfeed-entry-content entry))) - (prog1 entry - (when (stringp content) - (setf (elfeed-entry-content entry) (elfeed-ref content)))))) - -(defun elfeed-ref-delete (ref) - "Remove the content behind REF from the database." - (ignore-errors - (delete-file (elfeed-ref--file ref)))) - -(defun elfeed-db-gc-empty-feeds () - "Remove feeds with no entries from the database." - (let ((seen (make-hash-table :test 'equal))) - (with-elfeed-db-visit (entry feed) - (setf (gethash (elfeed-feed-id feed) seen) feed)) - (maphash (lambda (id _) - (unless (gethash id seen) - (remhash id elfeed-db-feeds))) - elfeed-db-feeds))) - -(defun elfeed-db-gc (&optional stats-p) - "Clean up unused content from the content database. -If STATS is true, return the space cleared in bytes." - (elfeed-db-gc-empty-feeds) - (let* ((data (expand-file-name "data" elfeed-db-directory)) - (dirs (directory-files data t "^[0-9a-z]\\{2\\}$")) - (ids (cl-mapcan (lambda (d) (directory-files d nil nil t)) dirs)) - (table (make-hash-table :test 'equal))) - (dolist (id ids) - (setf (gethash id table) nil)) - (with-elfeed-db-visit (entry _) - (let ((content (elfeed-entry-content entry))) - (when (elfeed-ref-p content) - (setf (gethash (elfeed-ref-id content) table) t)))) - (cl-loop for id hash-keys of table using (hash-value used) - for used-p = (or used (member id '("." ".."))) - when (and (not used-p) stats-p) - sum (let* ((ref (elfeed-ref--create :id id)) - (file (elfeed-ref--file ref))) - (* 1.0 (nth 7 (file-attributes file)))) - unless used-p - do (elfeed-ref-delete (elfeed-ref--create :id id)) - finally (cl-loop for dir in dirs - when (elfeed-directory-empty-p dir) - do (delete-directory dir))))) - -(defun elfeed-db-pack () - "Pack all content into a single archive for efficient storage." - (let ((coding-system-for-write 'utf-8) - (next-archive (make-hash-table :test 'equal)) - (packed ())) - (make-directory (expand-file-name "data" elfeed-db-directory) t) - (with-temp-file (elfeed-ref-archive-filename ".gz") - (with-elfeed-db-visit (entry _) - (let ((ref (elfeed-entry-content entry)) - (start (1- (point)))) - (when (elfeed-ref-p ref) - (let ((content (elfeed-deref ref))) - (when content - (push ref packed) - (insert content) - (setf (gethash (elfeed-ref-id ref) next-archive) - (cons start (1- (point)))))))))) - (with-temp-file (elfeed-ref-archive-filename ".index") - (let ((standard-output (current-buffer)) - (print-level nil) - (print-length nil) - (print-circle nil)) - (prin1 next-archive))) - (setf elfeed-ref-cache nil) - (setf elfeed-ref-archive next-archive) - (mapc #'elfeed-ref-delete packed) - :success)) - -(defun elfeed-db-compact () - "Minimize the Elfeed database storage size on the filesystem. -This requires that auto-compression-mode can handle -gzip-compressed files, so the gzip program must be in your PATH." - (interactive) - (unless (elfeed-gzip-supported-p) - (error "aborting compaction: gzip auto-compression-mode unsupported")) - (elfeed-db-pack) - (elfeed-db-gc)) - -(defun elfeed-db-gc-safe () - "Run `elfeed-db-gc' without triggering any errors, for use as a safe hook." - (ignore-errors (elfeed-db-gc))) - -(unless noninteractive - (add-hook 'kill-emacs-hook #'elfeed-db-gc-safe :append) - (add-hook 'kill-emacs-hook #'elfeed-db-save-safe)) - -(provide 'elfeed-db) - -;;; elfeed-db.el ends here blob - 4db9f1b3cbb6368a084ffddef616fc05373ed907 (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-lib.el +++ /dev/null @@ -1,385 +0,0 @@ -;;; elfeed-lib.el --- misc functions for elfeed -*- lexical-binding: t; -*- - -;; This is free and unencumbered software released into the public domain. - -;;; Commentary: - -;; These are general functions that aren't specific to web feeds. It's -;; a library of useful functions to Elfeed. - -;;; Code: - -(require 'cl-lib) -(require 'thingatpt) -(require 'time-date) -(require 'url-parse) -(require 'url-util) -(require 'xml) - -(defun elfeed-expose (function &rest args) - "Return an interactive version of FUNCTION, 'exposing' it to the user." - (lambda () (interactive) (apply function args))) - -(defun elfeed-goto-line (n) - "Like `goto-line' but for non-interactive use." - (goto-char (point-min)) - (forward-line (1- n))) - -(defun elfeed-kill-buffer () - "Kill the current buffer." - (interactive) - (kill-buffer (current-buffer))) - -(defun elfeed-kill-line () - "Clear out the current line without touching anything else." - (beginning-of-line) - (let ((start (point))) - (end-of-line) - (delete-region start (point)))) - -(defun elfeed-time-duration (time &optional now) - "Turn a time expression into a number of seconds. Uses -`timer-duration' but allows a bit more flair. - -If `now' is non-nil, use it as the current time (`float-time'). This -is mostly useful for testing." - (cond - ((numberp time) time) - ((let ((iso-time (elfeed-parse-simple-iso-8601 time))) - (when iso-time (- (or now (float-time)) iso-time)))) - ((string-match-p "[[:alpha:]]" time) - (let* ((clean (replace-regexp-in-string "\\(ago\\|old\\|-\\)" " " time)) - (duration (timer-duration clean))) - ;; convert to float since float-time is used elsewhere - (when duration (float duration)))))) - -(defun elfeed-looks-like-url-p (string) - "Return true if STRING looks like it could be a URL." - (and (stringp string) - (not (string-match-p "[ \n\t\r]" string)) - (not (null (url-type (url-generic-parse-url string)))))) - -(defun elfeed-format-column (string width &optional align) - "Return STRING truncated or padded to WIDTH following ALIGNment. -Align should be a keyword :left or :right." - (if (<= width 0) - "" - (format (format "%%%s%d.%ds" (if (eq align :left) "-" "") width width) - string))) - -(defun elfeed-clamp (min value max) - "Clamp a value between two values." - (min max (max min value))) - -(defun elfeed-valid-regexp-p (regexp) - "Return t if REGEXP is a valid REGEXP." - (ignore-errors - (prog1 t - (string-match-p regexp "")))) - -(defun elfeed-cleanup (name) - "Trim trailing and leading spaces and collapse multiple spaces." - (let ((trim (replace-regexp-in-string "[\f\n\r\t\v ]+" " " (or name "")))) - (replace-regexp-in-string "^ +\\| +$" "" trim))) - -(defun elfeed-parse-simple-iso-8601 (string) - "Attempt to parse STRING as a simply formatted ISO 8601 date. -Examples: 2015-02-22, 2015-02, 20150222" - (let* ((re (cl-flet ((re-numbers (num) (format "\\([0-9]\\{%s\\}\\)" num))) - (format "^%s-?%s-?%s?\\(T%s:%s:?%s?\\)?" - (re-numbers 4) - (re-numbers 2) - (re-numbers 2) - (re-numbers 2) - (re-numbers 2) - (re-numbers 2)))) - (matches (save-match-data - (when (string-match re string) - (cl-loop for i from 1 to 7 - collect (let ((match (match-string i string))) - (and match (string-to-number match)))))))) - (when matches - (cl-multiple-value-bind (year month day _ hour min sec) matches - (float-time (encode-time (or sec 0) (or min 0) (or hour 0) - (or day 1) month year t)))))) - -(defun elfeed-new-date-for-entry (old-date new-date) - "Decide entry date, given an existing date (nil for new) and a new date. -Existing entries' dates are unchanged if the new date is not -parseable. New entries with unparseable dates default to the -current time." - (or (elfeed-float-time new-date) - old-date - (float-time))) - -(defun elfeed-float-time (date) - "Like `float-time' but accept anything reasonable for DATE. -Defaults to nil if DATE could not be parsed. Date is allowed to -be relative to now (`elfeed-time-duration')." - (cl-typecase date - (string - (let ((iso-8601 (elfeed-parse-simple-iso-8601 date))) - (if iso-8601 - iso-8601 - (let ((duration (elfeed-time-duration date))) - (if duration - (- (float-time) duration) - (let ((time (ignore-errors (date-to-time date)))) - ;; check if date-to-time failed, silently or otherwise - (unless (or (null time) (equal time '(14445 17280))) - (float-time time)))))))) - (integer date) - (otherwise nil))) - -(defun elfeed-xml-parse-region (&optional beg end buffer parse-dtd _parse-ns) - "Decode (if needed) and parse XML file. Uses coding system from -XML encoding declaration." - (unless beg (setq beg (point-min))) - (unless end (setq end (point-max))) - (setf (point) beg) - (when (re-search-forward - "<\\?xml.*?encoding=[\"']\\([^\"']+\\)[\"'].*?\\?>" nil t) - (let ((coding-system (intern-soft (downcase (match-string 1))))) - (when (ignore-errors (check-coding-system coding-system)) - (let ((mark-beg (make-marker)) - (mark-end (make-marker))) - ;; Region changes with encoding, so use markers to track it. - (set-marker mark-beg beg) - (set-marker mark-end end) - (set-buffer-multibyte t) - (recode-region mark-beg mark-end coding-system 'raw-text) - (setf beg (marker-position mark-beg) - end (marker-position mark-end)))))) - (let ((xml-default-ns ())) - (xml-parse-region beg end buffer parse-dtd 'symbol-qnames))) - -(defun elfeed-xml-unparse (element) - "Inverse of `elfeed-xml-parse-region', writing XML to the buffer." - (cl-destructuring-bind (tag attrs . body) element - (insert (format "<%s" tag)) - (dolist (attr attrs) - (cl-destructuring-bind (key . value) attr - (insert (format " %s='%s'" key (xml-escape-string value))))) - (if (null body) - (insert "/>") - (insert ">") - (dolist (sub body) - (if (stringp sub) - (insert (xml-escape-string sub)) - (elfeed-xml-unparse sub))) - (insert (format "" tag))))) - -(defun elfeed-directory-empty-p (dir) - "Return non-nil if DIR is empty." - (null (cddr (directory-files dir)))) - -(defun elfeed-slurp (file &optional literally) - "Return the contents of FILE as a string." - (with-temp-buffer - (if literally - (insert-file-contents-literally file) - (insert-file-contents file)) - (buffer-string))) - -(cl-defun elfeed-spit (file string &key fsync append (encoding 'utf-8)) - "Write STRING to FILE." - (let ((coding-system-for-write encoding) - (write-region-inhibit-fsync (not fsync))) - (with-temp-buffer - (insert string) - (write-region nil nil file append 0)))) - -(defvar elfeed-gzip-supported-p--cache :unknown - "To avoid running the relatively expensive test more than once.") - -(defun elfeed-gzip-supported-p () - "Return non-nil if `auto-compression-mode' can handle gzip." - (if (not (eq elfeed-gzip-supported-p--cache :unknown)) - elfeed-gzip-supported-p--cache - (setf elfeed-gzip-supported-p--cache - (and (executable-find "gzip") - (ignore-errors - (save-window-excursion - (let ((file (make-temp-file "gziptest" nil ".gz")) - (data (cl-loop for i from 32 to 3200 - collect i into chars - finally - (return (apply #'string chars))))) - (unwind-protect - (progn - (elfeed-spit file data) - (and (string= data (elfeed-slurp file)) - (not (string= data (elfeed-slurp file t))))) - (delete-file file))))))))) - -(defun elfeed-libxml-supported-p () - "Return non-nil if `libxml-parse-html-region' is available." - (with-temp-buffer - (insert "") - (and (fboundp 'libxml-parse-html-region) - (not (null (libxml-parse-html-region (point-min) (point-max))))))) - -(defun elfeed-keyword->symbol (keyword) - "If a keyword, convert KEYWORD into a plain symbol (remove the colon)." - (if (keywordp keyword) - (intern (substring (symbol-name keyword) 1)) - keyword)) - -(defun elfeed-resize-vector (vector length) - "Return a copy of VECTOR set to size LENGTH." - (let ((new-vector (make-vector length nil))) - (prog1 new-vector ; don't use dotimes result (bug#16206) - (dotimes (i (min (length new-vector) (length vector))) - (setf (aref new-vector i) (aref vector i)))))) - -(defun elfeed-readable-p (value) - "Return non-nil if VALUE can be serialized." - (condition-case _ - (prog1 t (read (prin1-to-string value))) - (error nil))) - -(defun elfeed-strip-properties (string) - "Return a copy of STRING with all properties removed. -If STRING is nil, returns nil." - (when string - (let ((copy (copy-sequence string))) - (prog1 copy - (set-text-properties 0 (length copy) nil copy))))) - -(defun elfeed-clipboard-get () - "Try to get a sensible value from the system clipboard. -On systems running X, it will try to use the PRIMARY selection -first, then fall back onto the standard clipboard like other -systems." - (elfeed-strip-properties - (or (and (fboundp 'x-get-selection) - (funcall 'x-get-selection)) - (and (functionp interprogram-paste-function) - (funcall interprogram-paste-function)) - (and (fboundp 'w32-get-clipboard-data) - (funcall 'w32-get-clipboard-data)) - (ignore-errors - (current-kill 0 :non-destructively))))) - -(defun elfeed-get-link-at-point () - "Try to a link at point and return its URL." - (or (get-text-property (point) 'shr-url) - (and (fboundp 'eww-current-url) - (funcall 'eww-current-url)) - (get-text-property (point) :nt-link))) - -(defun elfeed-get-url-at-point () - "Try to get a plain URL at point." - (or (if (fboundp 'thing-at-point-url-at-point) - (thing-at-point-url-at-point) - (with-no-warnings (url-get-url-at-point))) - (thing-at-point 'url))) - -(defun elfeed-move-to-first-empty-line () - "Place point after first blank line, for use with `url-retrieve'. -If no such line exists, point is left in place." - (let ((start (point))) - (setf (point) (point-min)) - (unless (search-forward-regexp "^$" nil t) - (setf (point) start)))) - -(defun elfeed--shuffle (seq) - "Destructively shuffle SEQ." - (let ((n (length seq))) - (prog1 seq ; don't use dotimes result (bug#16206) - (dotimes (i n) - (cl-rotatef (elt seq i) (elt seq (+ i (cl-random (- n i))))))))) - -(defun elfeed-split-ranges-to-numbers (str n) - "Convert STR containing enclosure numbers into a list of numbers. -STR is a string; N is the highest possible number in the list. -This includes expanding e.g. 3-5 into 3,4,5. If the letter -\"a\" ('all')) is given, that is expanded to a list with numbers [1..n]." - (let ((str-split (split-string str)) - beg end list) - (dolist (elem str-split list) - ;; special number "a" converts into all enclosures 1-N. - (when (equal elem "a") - (setf elem (concat "1-" (int-to-string n)))) - (if (string-match "\\([0-9]+\\)-\\([0-9]+\\)" elem) - ;; we have found a range A-B, which needs converting - ;; into the numbers A, A+1, A+2, ... B. - (progn - (setf beg (string-to-number (match-string 1 elem)) - end (string-to-number (match-string 2 elem))) - (while (<= beg end) - (setf list (nconc list (list beg)) - beg (1+ beg)))) - ;; else just a number - (push (string-to-number elem) list))))) - -(defun elfeed-remove-dot-segments (input) - "Relative URL algorithm as described in RFC 3986 §5.2.4." - (cl-loop - with output = "" - for s = input - then (cond - ((string-match-p "^\\.\\./" s) - (substring s 3)) - ((string-match-p "^\\./" s) - (substring s 2)) - ((string-match-p "^/\\./" s) - (substring s 2)) - ((string-match-p "^/\\.$" s) "/") - ((string-match-p "^/\\.\\./" s) - (setf output (replace-regexp-in-string "/?[^/]*$" "" output)) - (substring s 3)) - ((string-match-p "^/\\.\\.$" s) - (setf output (replace-regexp-in-string "/?[^/]*$" "" output)) - "/") - ((string-match-p "^\\.\\.?$" s) - "") - ((string-match "^/?[^/]*" s) - (setf output (concat output (match-string 0 s))) - (replace-regexp-in-string "^/?[^/]*" "" s))) - until (zerop (length s)) - finally return output)) - -(defun elfeed-update-location (old-url new-url) - "Return full URL for maybe-relative NEW-URL based on full OLD-URL." - (if (null new-url) - old-url - (let ((old (url-generic-parse-url old-url)) - (new (url-generic-parse-url new-url))) - (cond - ;; Is new URL absolute already? - ((url-type new) new-url) - ;; Empty is a special case (clear fragment) - ((equal new-url "") - (setf (url-target old) nil) - (url-recreate-url old)) - ;; Does it start with //? Append the old protocol. - ((url-fullness new) (concat (url-type old) ":" new-url)) - ;; Is it a relative path? - ((not (string-match-p "^/" new-url)) - (let* ((old-dir (or (file-name-directory (url-filename old)) "/")) - (concat (concat old-dir new-url)) - (new-file (elfeed-remove-dot-segments concat))) - (setf (url-filename old) nil - (url-target old) nil - (url-attributes old) nil - (url-filename old) new-file) - (url-recreate-url old))) - ;; Replace the relative part. - ((progn - (setf (url-filename old) (elfeed-remove-dot-segments new-url) - (url-target old) nil - (url-attributes old) nil) - (url-recreate-url old))))))) - -(defun elfeed-url-to-namespace (url) - "Compute an ID namespace from URL." - (let* ((urlobj (url-generic-parse-url url)) - (host (url-host urlobj))) - (if (= 0 (length host)) - url - host))) - -(provide 'elfeed-lib) - -;;; elfeed-lib.el ends here blob - 40500677a3e42f403a7cc698b08635d133d70807 (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-link.el +++ /dev/null @@ -1,82 +0,0 @@ -;;; elfeed-link.el --- misc functions for elfeed -*- lexical-binding: t; -*- - -;; This is free and unencumbered software released into the public domain. - -;;; Commentary: - -;; Code for integration with org-mode. - -;; To use, add (require 'elfeed-link) somewhere in your configuration. - -;;; Code: - -(require 'org) -(require 'cl-lib) -(require 'elfeed-db) -(require 'elfeed-show) -(require 'elfeed-search) - -;;;###autoload -(defun elfeed-link-store-link () - "Store a link to an elfeed search or entry buffer. - -When storing a link to an entry, automatically extract all the -entry metadata. These can be used in the capture templates as -%:elfeed-entry-. See `elfeed-entry--create' for the list -of available props." - (cond ((derived-mode-p 'elfeed-search-mode) - (funcall (if (fboundp 'org-link-store-props) - #'org-link-store-props - (with-no-warnings #'org-store-link-props)) - :type "elfeed" - :link (format "elfeed:%s" elfeed-search-filter) - :description elfeed-search-filter)) - ((derived-mode-p 'elfeed-show-mode) - (apply - 'org-store-link-props - :type "elfeed" - :link (format "elfeed:%s#%s" - (car (elfeed-entry-id elfeed-show-entry)) - (cdr (elfeed-entry-id elfeed-show-entry))) - :description (elfeed-entry-title elfeed-show-entry) - (cl-loop for prop in - (list 'id 'title 'link 'date 'content 'content-type 'enclosures 'tags 'feed-id 'meta) - nconc (list - (intern (concat ":elfeed-entry-" (symbol-name prop))) - (funcall - (intern (concat "elfeed-entry-" (symbol-name prop))) - elfeed-show-entry))))))) - -;;;###autoload -(defun elfeed-link-open (filter-or-id) - "Jump to an elfeed entry or search. - -Depending on what FILTER-OR-ID looks like, we jump to either -search buffer or show a concrete entry." - (if (string-match "\\([^#]+\\)#\\(.+\\)" filter-or-id) - (elfeed-show-entry (elfeed-db-get-entry - (cons (match-string 1 filter-or-id) - (match-string 2 filter-or-id)))) - (elfeed) - (elfeed-search-set-filter filter-or-id))) - -;;;###autoload -(eval-after-load 'org - `(funcall - ;; The extra quote below is necessary because uncompiled closures - ;; do not evaluate to themselves. The quote is harmless for - ;; byte-compiled function objects. - ',(lambda () - (if (version< (org-version) "9.0") - (with-no-warnings - (org-add-link-type "elfeed" #'elfeed-link-open) - (add-hook 'org-store-link-functions #'elfeed-link-store-link)) - (with-no-warnings - (org-link-set-parameters - "elfeed" - :follow #'elfeed-link-open - :store #'elfeed-link-store-link)))))) - -(provide 'elfeed-link) - -;;; elfeed-link.el ends here blob - 9f91237f613c81310dcabaa0e0bd249af5931ea4 (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-log.el +++ /dev/null @@ -1,84 +0,0 @@ -;;; elfeed-log.el --- Elfeed's logging system -*- lexical-binding: t; -*- - -;;; Commentary: - -;;; Code: - -(require 'cl-lib) - -(defface elfeed-log-date-face - '((t :inherit font-lock-type-face)) - "Face for showing the date in the elfeed log buffer." - :group 'elfeed) - -(defface elfeed-log-error-level-face - '((t :foreground "red")) - "Face for showing the `error' log level in the elfeed log buffer." - :group 'elfeed) - -(defface elfeed-log-warn-level-face - '((t :foreground "goldenrod")) - "Face for showing the `warn' log level in the elfeed log buffer." - :group 'elfeed) - -(defface elfeed-log-info-level-face - '((t :foreground "deep sky blue")) - "Face for showing the `info' log level in the elfeed log buffer." - :group 'elfeed) - -(defface elfeed-log-debug-level-face - '((t :foreground "magenta2")) - "Face for showing the `debug' log level in the elfeed log buffer." - :group 'elfeed) - -(defvar elfeed-log-buffer-name "*elfeed-log*" - "Name of buffer used for logging Elfeed events.") - -(defvar elfeed-log-level 'info - "Lowest type of messages to be logged.") - -(defun elfeed-log-buffer () - "Returns the buffer for `elfeed-log', creating it as needed." - (let ((buffer (get-buffer elfeed-log-buffer-name))) - (if buffer - buffer - (with-current-buffer (generate-new-buffer elfeed-log-buffer-name) - (special-mode) - (current-buffer))))) - -(defun elfeed-log--level-number (level) - "Return a relative level number for LEVEL." - (cl-case level - (debug -10) - (info 0) - (warn 10) - (error 20) - (otherwise -10))) - -(defun elfeed-log (level fmt &rest objects) - "Write log message FMT at LEVEL to Elfeed's log buffer. - -LEVEL should be a symbol: debug, info, warn, error. -FMT must be a string suitable for `format' given OBJECTS as arguments." - (let ((log-buffer (elfeed-log-buffer)) - (log-level-face (cl-case level - (debug 'elfeed-log-debug-level-face) - (info 'elfeed-log-info-level-face) - (warn 'elfeed-log-warn-level-face) - (error 'elfeed-log-error-level-face))) - (inhibit-read-only t)) - (when (>= (elfeed-log--level-number level) - (elfeed-log--level-number elfeed-log-level)) - (with-current-buffer log-buffer - (goto-char (point-max)) - (insert - (format - (concat "[" (propertize "%s" 'face 'elfeed-log-date-face) "] " - "[" (propertize "%s" 'face log-level-face) "]: %s\n") - (format-time-string "%Y-%m-%d %H:%M:%S") - level - (apply #'format fmt objects))))))) - -(provide 'elfeed-log) - -;;; elfeed-log.el ends here blob - a49f5e0b53865e844714a03a5643c9db6d3d40af (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-pkg.el +++ /dev/null @@ -1,10 +0,0 @@ -(define-package "elfeed" "3.4.1" "an Emacs Atom/RSS feed reader" - '((emacs "24.3")) - :commit "0ccd59aaace34546017a1a0d7c393749747d5bc6" :authors - '(("Christopher Wellons" . "wellons@nullprogram.com")) - :maintainer - '("Christopher Wellons" . "wellons@nullprogram.com") - :url "https://github.com/skeeto/elfeed") -;; Local Variables: -;; no-byte-compile: t -;; End: blob - 43c120c534e0b126990e05db70b763816cf12e02 (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-search.el +++ /dev/null @@ -1,948 +0,0 @@ -;;; elfeed-search.el --- list feed entries -*- lexical-binding: t; -*- - -;; This is free and unencumbered software released into the public domain. - -;;; Code: - -(require 'cl-lib) -(require 'browse-url) -(require 'wid-edit) ; widget-inactive face -(require 'bookmark) -(bookmark-maybe-load-default-file) - -(require 'elfeed) -(require 'elfeed-db) -(require 'elfeed-lib) - -;; Interface to elfeed-show (lazy required) -(declare-function elfeed-show-entry 'elfeed-show (entry)) - -(defvar elfeed-search-entries () - "List of the entries currently on display.") - -(defvar elfeed-search-filter-history nil - "Filter history for `completing-read'.") - -(defvar elfeed-search-last-update 0 - "The last time the buffer was redrawn in epoch seconds.") - -(defvar elfeed-search-update-hook () - "List of functions to run immediately following a search buffer update.") - -(defcustom elfeed-search-filter "@6-months-ago +unread" - "Query string filtering shown entries." - :group 'elfeed - :type 'string) - -(defcustom elfeed-sort-order 'descending - "The order in which entries should be displayed. - -Changing this from the default will lead to misleading results -during live filter editing, but the results be will correct when -live filter editing is exited. " - :group 'elfeed - :type '(choice (const descending) (const ascending))) - -(defcustom elfeed-search-sort-function nil - "Sort predicate applied to the list of entries before display. - -This function must take two entries as arguments, an interface -suitable as the predicate for `sort'. - -Changing this from the default will lead to misleading results -during live filter editing, but the results be will correct when -live filter editing is exited." - :group 'elfeed - :type '(choice function (const nil))) - -(defcustom elfeed-search-remain-on-entry nil - "When non-nil, keep point at entry after performing a command. - -When nil, move to next entry." - :group 'elfeed - :type 'boolean) - -(defcustom elfeed-search-clipboard-type 'PRIMARY - "Selects the clipboard `elfeed-search-yank' should use. -Choices are the symbols PRIMARY, SECONDARY, or CLIPBOARD." - :group 'elfeed - :type '(choice (const PRIMARY) (const SECONDARY) (const CLIPBOARD))) - -(defcustom elfeed-search-date-format '("%Y-%m-%d" 10 :left) - "The `format-time-string' format, target width, and alignment for dates. - -This should be (string integer keyword) for (format width alignment). -Possible alignments are :left and :right." - :group 'elfeed - :type '(list string integer (choice (const :left) (const :right)))) - -(defcustom elfeed-search-compile-filter t - "If non-nil, compile search filters into bytecode on the fly." - :group 'elfeed - :type 'boolean) - -(defvar elfeed-search-filter-active nil - "When non-nil, Elfeed is currently reading a filter from the minibuffer. -When live editing the filter, it is bound to :live.") - -(defvar elfeed-search-filter-overflowing nil - "When non-nil, the current live filter overflows the window.") - -(defvar elfeed-search--offset 1 - "Offset between line numbers and entry list position.") - -(defvar elfeed-search-header-function #'elfeed-search--header - "Function that returns the string to be used for the Elfeed search header.") - -(defvar elfeed-search-print-entry-function #'elfeed-search-print-entry--default - "Function to print entries into the *elfeed-search* buffer.") - -(defalias 'elfeed-search-tag-all-unread - (elfeed-expose #'elfeed-search-tag-all 'unread) - "Add the `unread' tag to all selected entries.") - -(defalias 'elfeed-search-untag-all-unread - (elfeed-expose #'elfeed-search-untag-all 'unread) - "Remove the `unread' tag from all selected entries.") - -(defalias 'elfeed-search-update--force - (elfeed-expose #'elfeed-search-update :force) - "Force refresh view of the feed listing.") - -(defun elfeed-search-quit-window () - "Save the database, then `quit-window'." - (interactive) - (elfeed-db-save) - (quit-window)) - -(defun elfeed-search-last-entry () - "Place point on last entry." - (interactive) - (setf (point) (point-max)) - (forward-line -1)) - -(defun elfeed-search-first-entry () - "Place point on first entry." - (interactive) - (setf (point) (point-min))) - -(defvar elfeed-search-mode-map - (let ((map (make-sparse-keymap))) - (prog1 map - (suppress-keymap map) - (define-key map "h" #'describe-mode) - (define-key map "q" #'elfeed-search-quit-window) - (define-key map "g" #'elfeed-search-update--force) - (define-key map "G" #'elfeed-search-fetch) - (define-key map (kbd "RET") #'elfeed-search-show-entry) - (define-key map "s" #'elfeed-search-live-filter) - (define-key map "S" #'elfeed-search-set-filter) - (define-key map "c" #'elfeed-search-clear-filter) - (define-key map "b" #'elfeed-search-browse-url) - (define-key map "y" #'elfeed-search-yank) - (define-key map "u" #'elfeed-search-tag-all-unread) - (define-key map "r" #'elfeed-search-untag-all-unread) - (define-key map "n" #'next-line) - (define-key map "p" #'previous-line) - (define-key map "+" #'elfeed-search-tag-all) - (define-key map "-" #'elfeed-search-untag-all) - (define-key map "<" #'elfeed-search-first-entry) - (define-key map ">" #'elfeed-search-last-entry))) - "Keymap for elfeed-search-mode.") - -(defun elfeed-search--intro-header () - "Return the header shown to new users." - (with-temp-buffer - (cl-flet ((button (f) - (insert-button (symbol-name f) - 'follow-link t - 'action (lambda (_) (call-interactively f))))) - (insert "Database empty. Use ") - (button 'elfeed-add-feed) - (insert ", or ") - (button 'elfeed-load-opml) - (insert ", or ") - (button 'elfeed-update) - (insert ".") - (buffer-string)))) - -(defun elfeed-search--count-unread () - "Count the number of entries and feeds being currently displayed." - (if (and elfeed-search-filter-active elfeed-search-filter-overflowing) - "?/?:?" - (cl-loop with feeds = (make-hash-table :test 'equal) - for entry in elfeed-search-entries - for feed = (elfeed-entry-feed entry) - for url = (elfeed-feed-url feed) - count entry into entry-count - count (elfeed-tagged-p 'unread entry) into unread-count - do (puthash url t feeds) - finally - (cl-return - (format "%d/%d:%d" - unread-count entry-count - (hash-table-count feeds)))))) - -(defun elfeed-search--header () - "Computes the string to be used as the Elfeed header." - (cond - ((zerop (elfeed-db-last-update)) - (elfeed-search--intro-header)) - ((> (elfeed-queue-count-total) 0) - (let ((total (elfeed-queue-count-total)) - (in-process (elfeed-queue-count-active))) - (format "%d jobs pending, %d active..." - (- total in-process) in-process))) - ((let* ((db-time (seconds-to-time (elfeed-db-last-update))) - (update (format-time-string "%Y-%m-%d %H:%M" db-time)) - (unread (elfeed-search--count-unread))) - (format "Updated %s, %s%s" - (propertize update 'face 'elfeed-search-last-update-face) - (propertize unread 'face 'elfeed-search-unread-count-face) - (cond - (elfeed-search-filter-active "") - ((string-match-p "[^ ]" elfeed-search-filter) - (concat ", " (propertize elfeed-search-filter - 'face 'elfeed-search-filter-face))) - (""))))))) - -(defun elfeed-search-mode () - "Major mode for listing elfeed feed entries. -\\{elfeed-search-mode-map}" - (interactive) - (kill-all-local-variables) - (use-local-map elfeed-search-mode-map) - (setq major-mode 'elfeed-search-mode - mode-name "elfeed-search" - truncate-lines t - buffer-read-only t - desktop-save-buffer #'elfeed-search-desktop-save - header-line-format '(:eval (funcall elfeed-search-header-function))) - (set (make-local-variable 'bookmark-make-record-function) - #'elfeed-search-bookmark-make-record) - (buffer-disable-undo) - (hl-line-mode) - (make-local-variable 'elfeed-search-entries) - (make-local-variable 'elfeed-search-filter) - (add-hook 'elfeed-update-hooks #'elfeed-search-update) - (add-hook 'elfeed-update-init-hooks #'elfeed-search-update--force) - (add-hook 'kill-buffer-hook #'elfeed-db-save t t) - (add-hook 'elfeed-db-unload-hook #'elfeed-search--unload) - (elfeed-search-update :force) - (run-mode-hooks 'elfeed-search-mode-hook)) - -(defun elfeed-search-buffer () - (get-buffer-create "*elfeed-search*")) - -(defun elfeed-search--unload () - "Hook function for `elfeed-db-unload-hook'." - (with-current-buffer (elfeed-search-buffer) - ;; don't try to save the database in this case - (remove-hook 'kill-buffer-hook #'elfeed-db-save t) - (kill-buffer ))) - -(defun elfeed-search-format-date (date) - "Format a date for printing in `elfeed-search-mode'. -The customization `elfeed-search-date-format' sets the formatting." - (cl-destructuring-bind (format target alignment) elfeed-search-date-format - (let* ((string (format-time-string format (seconds-to-time date))) - (width (string-width string))) - (cond - ((> width target) - (if (eq alignment :left) - (substring string 0 target) - (substring string (- width target) width))) - ((< width target) - (let ((pad (make-string (- target width) ?\s))) - (if (eq alignment :left) - (concat string pad) - (concat pad string)))) - (string))))) - -(defface elfeed-search-date-face - '((((class color) (background light)) (:foreground "#aaa")) - (((class color) (background dark)) (:foreground "#77a"))) - "Face used in search mode for dates." - :group 'elfeed) - -(defface elfeed-search-title-face - '((((class color) (background light)) (:foreground "#000")) - (((class color) (background dark)) (:foreground "#fff"))) - "Face used in search mode for titles." - :group 'elfeed) - -(defface elfeed-search-unread-title-face - '((t :weight bold)) - "Face used in search mode for unread entry titles." - :group 'elfeed) - -(defface elfeed-search-feed-face - '((((class color) (background light)) (:foreground "#aa0")) - (((class color) (background dark)) (:foreground "#ff0"))) - "Face used in search mode for feed titles." - :group 'elfeed) - -(defface elfeed-search-tag-face - '((((class color) (background light)) (:foreground "#070")) - (((class color) (background dark)) (:foreground "#0f0"))) - "Face used in search mode for tags." - :group 'elfeed) - -(defface elfeed-search-last-update-face - '((t)) - "Face for showing the date and time the database was last updated." - :group 'elfeed) - -(defface elfeed-search-unread-count-face - '((((class color) (background light)) (:foreground "#000")) - (((class color) (background dark)) (:foreground "#fff"))) - "Face used in search mode for unread entry titles." - :group 'elfeed) - -(defface elfeed-search-filter-face - '((t :inherit mode-line-buffer-id)) - "Face for showing the current Elfeed search filter." - :group 'elfeed) - -(defcustom elfeed-search-title-max-width 70 - "Maximum column width for titles in the elfeed-search buffer." - :group 'elfeed - :type 'integer) - -(defcustom elfeed-search-title-min-width 16 - "Minimum column width for titles in the elfeed-search buffer." - :group 'elfeed - :type 'integer) - -(defcustom elfeed-search-trailing-width 30 - "Space reserved for displaying the feed and tag information." - :group 'elfeed - :type 'integer) - -(defcustom elfeed-search-face-alist - '((unread elfeed-search-unread-title-face)) - "Mapping of tags to faces in the Elfeed entry listing." - :group 'elfeed - :type '(alist :key-type symbol :value-type (repeat face))) - -(defun elfeed-search--faces (tags) - "Return all the faces that apply to an entry with TAGS." - (nconc (cl-loop for (tag . faces) in elfeed-search-face-alist - when (memq tag tags) - append faces) - (list 'elfeed-search-title-face))) - -(defun elfeed-search-print-entry--default (entry) - "Print ENTRY to the buffer." - (let* ((date (elfeed-search-format-date (elfeed-entry-date entry))) - (title (or (elfeed-meta entry :title) (elfeed-entry-title entry) "")) - (title-faces (elfeed-search--faces (elfeed-entry-tags entry))) - (feed (elfeed-entry-feed entry)) - (feed-title - (when feed - (or (elfeed-meta feed :title) (elfeed-feed-title feed)))) - (tags (mapcar #'symbol-name (elfeed-entry-tags entry))) - (tags-str (mapconcat - (lambda (s) (propertize s 'face 'elfeed-search-tag-face)) - tags ",")) - (title-width (- (window-width) 10 elfeed-search-trailing-width)) - (title-column (elfeed-format-column - title (elfeed-clamp - elfeed-search-title-min-width - title-width - elfeed-search-title-max-width) - :left))) - (insert (propertize date 'face 'elfeed-search-date-face) " ") - (insert (propertize title-column 'face title-faces 'kbd-help title) " ") - (when feed-title - (insert (propertize feed-title 'face 'elfeed-search-feed-face) " ")) - (when tags - (insert "(" tags-str ")")))) - -(defun elfeed-search-parse-filter (filter) - "Parse the elements of a search filter into a plist." - (let ((must-have ()) - (must-not-have ()) - (before nil) - (after nil) - (matches ()) - (not-matches ()) - (limit nil) - (feeds ()) - (not-feeds ())) - (cl-loop for element in (split-string filter) - for type = (aref element 0) - do (cl-case type - (?+ - (let ((symbol (intern (substring element 1)))) - (unless (eq '## symbol) - (push symbol must-have)))) - (?- - (let ((symbol (intern (substring element 1)))) - (unless (eq '## symbol) - (push symbol must-not-have)))) - (?@ (cl-multiple-value-bind (a b) - (split-string (substring element 1) "--") - (let ((duration-a (elfeed-time-duration a)) - (duration-b (and b (elfeed-time-duration b)))) - (when (and duration-b (> duration-b duration-a)) - (cl-rotatef duration-a duration-b)) - (when duration-b (setf before duration-b)) - (setf after duration-a)))) - (?! (let ((re (substring element 1))) - (when (elfeed-valid-regexp-p re) - (push re not-matches)))) - (?# (setf limit (string-to-number (substring element 1)))) - (?= (let ((re (substring element 1))) - (when (elfeed-valid-regexp-p re) - (push re feeds)))) - (?~ (let ((re (substring element 1))) - (when (elfeed-valid-regexp-p re) - (push re not-feeds)))) - (otherwise (when (elfeed-valid-regexp-p element) - (push element matches))))) - `(,@(when before - (list :before before)) - ,@(when after - (list :after after)) - ,@(when must-have - (list :must-have must-have)) - ,@(when must-not-have - (list :must-not-have must-not-have)) - ,@(when matches - (list :matches matches)) - ,@(when not-matches - (list :not-matches not-matches)) - ,@(when limit - (list :limit limit)) - ,@(when feeds - (list :feeds feeds)) - ,@(when not-feeds - (list :not-feeds not-feeds))))) - -(defun elfeed-search--recover-time (seconds) - "Pick a reasonable filter representation for SECONDS." - (let ((units '((60 1 "minute") - (60 1 "hour") - (24 1 "day") - (7 1 "week") - (30 7 "month") - (1461 120 "year"))) - (value (float seconds)) - (name "second")) - (cl-loop for (n d unit) in units - for next-value = (/ (* value d) n) - when (< next-value 1.0) - return t - do (setf name unit - value next-value)) - (let ((count (format "%.4g" value))) - (format "%s-%s%s-ago" count name (if (equal count "1") "" "s"))))) - -(defun elfeed-search--recover-units (after-seconds &optional before-seconds) - "Stringify the age or optionally the date range specified by -AFTER-SECONDS and BEFORE-SECONDS." - (apply 'concat "@" - (elfeed-search--recover-time after-seconds) - (when before-seconds - (list "--"(elfeed-search--recover-time before-seconds))))) - -(defun elfeed-search-unparse-filter (filter) - "Inverse of `elfeed-search-parse-filter', returning a string. - -The time (@n-units-ago) filter may not exactly match the -original, but will be equal in its effect." - (let ((output ())) - (cl-destructuring-bind (&key after before - must-have must-not-have - matches not-matches - feeds not-feeds - limit &allow-other-keys) - filter - (when after - (push (elfeed-search--recover-units after before) output)) - (dolist (tag must-have) - (push (format "+%S" tag) output)) - (dolist (tag must-not-have) - (push (format "-%S" tag) output)) - (dolist (re matches) - (push re output)) - (dolist (re not-matches) - (push (concat "!" re) output)) - (when limit - (push (format "#%d" limit) output)) - (dolist (feed feeds) - (push (format "=%s" feed) output)) - (dolist (feed not-feeds) - (push (format "~%s" feed) output)) - (mapconcat #'identity (nreverse output) " ")))) - -(defun elfeed-search-filter (filter entry feed &optional count) - "Return non-nil if ENTRY and FEED pass FILTER. - -COUNT is the total number of entries collected so far, for -filtering against a limit filter (ex. #10). - -See `elfeed-search-set-filter' for format/syntax documentation. -This function must *only* be called within the body of -`with-elfeed-db-visit' because it may perform a non-local exit." - (cl-destructuring-bind (&key must-have must-not-have - matches not-matches - feeds not-feeds - after limit &allow-other-keys) - filter - (let* ((tags (elfeed-entry-tags entry)) - (date (elfeed-entry-date entry)) - (age (- (float-time) date)) - (title (or (elfeed-meta entry :title) (elfeed-entry-title entry))) - (link (elfeed-entry-link entry)) - (feed-title - (or (elfeed-meta feed :title) (elfeed-feed-title feed) "")) - (feed-id (elfeed-feed-id feed))) - (when (or (and after (> age after)) - (and limit (<= limit 0)) - (and limit count (>= count limit))) - (elfeed-db-return)) - (and (cl-every (lambda (tag) (memq tag tags)) must-have) - (cl-notany (lambda (tag) (memq tag tags)) must-not-have) - (or (null matches) - (cl-every - (lambda (m) - (or (and title (string-match-p m title)) - (and link (string-match-p m link)))) - matches)) - (cl-notany (lambda (m) - (or (and title (string-match-p m title)) - (and link (string-match-p m link)))) - not-matches) - (or (null feeds) - (cl-some (lambda (f) - (or (string-match-p f feed-id) - (string-match-p f feed-title))) - feeds)) - (cl-notany (lambda (f) - (or (string-match-p f feed-id) - (string-match-p f feed-title))) - not-feeds))))) - -(defun elfeed-search-compile-filter (filter) - "Compile FILTER into a lambda function for `byte-compile'. - -Executing a filter in bytecode form is generally faster than -\"interpreting\" the filter with `elfeed-search-filter'." - (cl-destructuring-bind (&key after before - must-have must-not-have - matches not-matches - feeds not-feeds - limit &allow-other-keys) - filter - `(lambda (,(if (or after matches not-matches must-have must-not-have) - 'entry - '_entry) - ,(if (or feeds not-feeds) - 'feed - '_feed) - ,(if limit - 'count - '_count)) - (let* (,@(when after - '((date (elfeed-entry-date entry)) - (age (- (float-time) date)))) - ,@(when (or must-have must-not-have) - '((tags (elfeed-entry-tags entry)))) - ,@(when (or matches not-matches) - '((title (or (elfeed-meta entry :title) - (elfeed-entry-title entry))) - (link (elfeed-entry-link entry)))) - ,@(when (or feeds not-feeds) - '((feed-id (elfeed-feed-id feed)) - (feed-title (or (elfeed-meta feed :title) - (elfeed-feed-title feed) ""))))) - ,@(when after - `((when (> age ,after) - (elfeed-db-return)))) - ,@(when limit - `((when (>= count ,limit) - (elfeed-db-return)))) - (and ,@(cl-loop for forbid in must-not-have - collect `(not (memq ',forbid tags))) - ,@(cl-loop for forbid in must-have - collect `(memq ',forbid tags)) - ,@(cl-loop for regex in matches collect - `(or (string-match-p ,regex title) - (string-match-p ,regex link))) - ,@(cl-loop for regex in not-matches collect - `(not - (or (string-match-p ,regex title) - (string-match-p ,regex link)))) - ,@(when feeds - `((or ,@(cl-loop - for regex in feeds - collect `(string-match-p ,regex feed-id) - collect `(string-match-p ,regex feed-title))))) - ,@(when not-feeds - `((not - (or ,@(cl-loop - for regex in not-feeds - collect `(string-match-p ,regex feed-id) - collect `(string-match-p ,regex feed-title)))))) - ,@(when before - `((> age ,before)))))))) - -(defun elfeed-search--prompt (current) - "Prompt for a new filter, starting with CURRENT." - (read-from-minibuffer - "Filter: " - (if (or (string= "" current) - (string-match-p " $" current)) - current - (concat current " ")) - nil nil 'elfeed-search-filter-history)) - -(defun elfeed-search-clear-filter () - "Reset the search filter to the default value of `elfeed-search-filter'." - (interactive) - (setf elfeed-search-filter (default-value 'elfeed-search-filter)) - (elfeed-search-update--force)) - -(defun elfeed-search-set-filter (new-filter) - "Set a new search filter for the elfeed-search buffer. - -When NEW-FILTER is nil, reset the filter to the default value. - -When given a prefix argument, the current filter is not displayed -in the minibuffer when prompting for a new filter. - -Any component beginning with a + or - is treated as a tag. If + -the tag must be present on the entry. If - the tag must *not* be -present on the entry. Ex. \"+unread\" or \"+unread -comic\". - -Any component beginning with an @ is an age limit or an age -range. If a limit, no posts older than this are allowed. If a -range, posts dates have to be inbetween the specified date -range. Examples: -- \"@3-days-ago\" -- \"@1-year-old\" -- \"@2019-06-24\" -- \"@2019-06-24--2019-06-24\" -- \"@5-days-ago--1-day-ago\" - -Any component beginning with a # is an entry count maximum. The -number following # determines the maxiumum number of entries -to be shown (descending by date). Ex. \"#20\" or \"#100\". - -Any component beginning with a = is a regular expression matching -the entry's feed (title or URL). Only entries belonging to a feed -that match at least one of the = expressions will be shown. - -Every other space-seperated element is treated like a regular -expression, matching against entry link, title, and feed title." - (interactive - (let ((elfeed-search-filter-active :non-interactive)) - (list (elfeed-search--prompt - (if current-prefix-arg "" elfeed-search-filter))))) - (with-current-buffer (elfeed-search-buffer) - (setf elfeed-search-filter - (or new-filter (default-value 'elfeed-search-filter))) - (elfeed-search-update :force))) - -(defun elfeed-search--update-list () - "Update `elfeed-search-filter' list." - (let* ((filter (elfeed-search-parse-filter elfeed-search-filter)) - (head (list nil)) - (tail head) - (count 0)) - (if elfeed-search-compile-filter - ;; Force lexical bindings regardless of the current - ;; buffer-local value. Lexical scope uses the faster - ;; stack-ref opcode instead of the traditional varref opcode. - (let ((lexical-binding t) - (func (byte-compile (elfeed-search-compile-filter filter)))) - (with-elfeed-db-visit (entry feed) - (when (funcall func entry feed count) - (setf (cdr tail) (list entry) - tail (cdr tail) - count (1+ count))))) - (with-elfeed-db-visit (entry feed) - (when (elfeed-search-filter filter entry feed count) - (setf (cdr tail) (list entry) - tail (cdr tail) - count (1+ count))))) - ;; Determine the final list order - (let ((entries (cdr head))) - (when elfeed-search-sort-function - (setf entries (sort entries elfeed-search-sort-function))) - (when (eq elfeed-sort-order 'ascending) - (setf entries (nreverse entries))) - (setf elfeed-search-entries - entries)))) - -(defmacro elfeed-save-excursion (&rest body) - "Like `save-excursion', but by entry/line/column instead of point." - (declare (indent defun)) - `(let ((entry (elfeed-search-selected :single)) - (line (line-number-at-pos)) - (column (current-column))) - (unwind-protect - (progn ,@body) - (let ((entry-position (cl-position entry elfeed-search-entries))) - (elfeed-goto-line (if entry-position - (+ elfeed-search--offset entry-position) - line)) - (move-to-column column))))) - -(defun elfeed-search-update (&optional force) - "Update the elfeed-search buffer listing to match the database. -When FORCE is non-nil, redraw even when the database hasn't changed." - (interactive) - (with-current-buffer (elfeed-search-buffer) - (when (or force (and (not elfeed-search-filter-active) - (< elfeed-search-last-update (elfeed-db-last-update)))) - (elfeed-save-excursion - (let ((inhibit-read-only t) - (standard-output (current-buffer))) - (erase-buffer) - (elfeed-search--update-list) - (dolist (entry elfeed-search-entries) - (funcall elfeed-search-print-entry-function entry) - (insert "\n")) - (setf elfeed-search-last-update (float-time)))) - (when (zerop (buffer-size)) - ;; If nothing changed, force a header line update - (force-mode-line-update)) - (run-hooks 'elfeed-search-update-hook)))) - -(defun elfeed-search-fetch (prefix) - "Update all feeds via `elfeed-update', or only visible feeds with PREFIX. -Given a prefix, this function becomes `elfeed-search-fetch-visible'." - (interactive "P") - (if prefix - (elfeed-search-fetch-visible) - (elfeed-update))) - -(defun elfeed-search-fetch-visible () - "Update any feed with an entry currently displayed in the search buffer." - (interactive) - (cl-loop with seen = (make-hash-table :test 'equal) - for entry in elfeed-search-entries - for feed = (elfeed-entry-feed entry) - for url = (elfeed-feed-url feed) - when (not (gethash url seen)) - do (elfeed-update-feed (setf (gethash url seen) url)))) - -(defun elfeed-search-update-line (&optional n) - "Redraw the current line." - (let ((inhibit-read-only t)) - (save-excursion - (when n (elfeed-goto-line n)) - (let ((entry (elfeed-search-selected :ignore-region))) - (when entry - (elfeed-kill-line) - (funcall elfeed-search-print-entry-function entry)))))) - -(defun elfeed-search-update-entry (entry) - "Redraw a specific entry." - (let ((n (cl-position entry elfeed-search-entries))) - (when n (elfeed-search-update-line (+ elfeed-search--offset n))))) - -(defun elfeed-search-selected (&optional ignore-region-p) - "Return a list of the currently selected feeds. - -If IGNORE-REGION-P is non-nil, only return the entry under point." - (let ((use-region (and (not ignore-region-p) (use-region-p)))) - (let ((start (if use-region (region-beginning) (point))) - (end (if use-region (region-end) (point)))) - (cl-loop for line from (line-number-at-pos start) - to (line-number-at-pos end) - for offset = (- line elfeed-search--offset) - when (and (>= offset 0) (nth offset elfeed-search-entries)) - collect it into selected - finally (return (if ignore-region-p - (car selected) - selected)))))) - -(defun elfeed-search-browse-url (&optional use-generic-p) - "Visit the current entry in your browser using `browse-url'. -If there is a prefix argument, visit the current entry in the -browser defined by `browse-url-generic-program'." - (interactive "P") - (let ((entries (elfeed-search-selected))) - (cl-loop for entry in entries - do (elfeed-untag entry 'unread) - when (elfeed-entry-link entry) - do (if use-generic-p - (browse-url-generic it) - (browse-url it))) - (mapc #'elfeed-search-update-entry entries) - (unless (or elfeed-search-remain-on-entry (use-region-p)) - (forward-line)))) - -(defun elfeed-search-yank () - "Copy the selected feed items to clipboard and kill-ring." - (interactive) - (let* ((entries (elfeed-search-selected)) - (links (mapcar #'elfeed-entry-link entries)) - (links-str (mapconcat #'identity links " "))) - (when entries - (elfeed-untag entries 'unread) - (kill-new links-str) - (if (fboundp 'gui-set-selection) - (gui-set-selection elfeed-search-clipboard-type links-str) - (with-no-warnings - (x-set-selection elfeed-search-clipboard-type links-str))) - (message "Copied: %s" links-str) - (mapc #'elfeed-search-update-entry entries) - (unless (or elfeed-search-remain-on-entry (use-region-p)) - (forward-line))))) - -(defun elfeed-search-tag-all (tag) - "Apply TAG to all selected entries." - (interactive (list (intern (read-from-minibuffer "Tag: ")))) - (let ((entries (elfeed-search-selected))) - (elfeed-tag entries tag) - (mapc #'elfeed-search-update-entry entries) - (unless (or elfeed-search-remain-on-entry (use-region-p)) - (forward-line)))) - -(defun elfeed-search-untag-all (tag) - "Remove TAG from all selected entries." - (interactive (list (intern (read-from-minibuffer "Tag: ")))) - (let ((entries (elfeed-search-selected))) - (elfeed-untag entries tag) - (mapc #'elfeed-search-update-entry entries) - (unless (or elfeed-search-remain-on-entry (use-region-p)) - (forward-line)))) - -(defun elfeed-search-toggle-all (tag) - "Toggle TAG on all selected entries." - (interactive (list (intern (read-from-minibuffer "Tag: ")))) - (let ((entries (elfeed-search-selected)) entries-tag entries-untag) - (cl-loop for entry in entries - when (elfeed-tagged-p tag entry) - do (push entry entries-untag) - else do (push entry entries-tag)) - (elfeed-tag entries-tag tag) - (elfeed-untag entries-untag tag) - (mapc #'elfeed-search-update-entry entries) - (unless (or elfeed-search-remain-on-entry (use-region-p)) - (forward-line)))) - -(defun elfeed-search-show-entry (entry) - "Display the currently selected item in a buffer." - (interactive (list (elfeed-search-selected :ignore-region))) - (require 'elfeed-show) - (when (elfeed-entry-p entry) - (elfeed-untag entry 'unread) - (elfeed-search-update-entry entry) - (unless elfeed-search-remain-on-entry (forward-line)) - (elfeed-show-entry entry))) - -(defun elfeed-search-set-entry-title (title) - "Manually set the title for the entry under point. -Sets the :title key of the entry's metadata. See `elfeed-meta'." - (interactive "sTitle: ") - (let ((entry (elfeed-search-selected :ignore-region))) - (unless entry - (error "No entry selected!")) - (setf (elfeed-meta entry :title) title) - (elfeed-search-update-entry entry))) - -(defun elfeed-search-set-feed-title (title) - "Manually set the title for the feed belonging to the entry under point. -Sets the :title key of the feed's metadata. See `elfeed-meta'." - (interactive "sTitle: ") - (let ((entry (elfeed-search-selected :ignore-region))) - (unless entry - (error "No entry selected!")) - (let ((feed (elfeed-entry-feed entry))) - (setf (elfeed-meta feed :title) title) - (dolist (to-fix elfeed-search-entries) - (elfeed-search-update-entry to-fix))))) - -;; Live Filters - -(defvar elfeed-search-filter-syntax-table - (let ((table (make-syntax-table))) - (prog1 table - (modify-syntax-entry ?+ "w" table) - (modify-syntax-entry ?- "w" table) - (modify-syntax-entry ?= "w" table) - (modify-syntax-entry ?@ "w" table))) - "Syntax table active when editing the filter in the minibuffer.") - -(defun elfeed-search--minibuffer-setup () - "Set up the minibuffer for live filtering." - (when elfeed-search-filter-active - (set-syntax-table elfeed-search-filter-syntax-table) - (when (eq :live elfeed-search-filter-active) - (add-hook 'post-command-hook 'elfeed-search--live-update nil :local)))) - -(add-hook 'minibuffer-setup-hook 'elfeed-search--minibuffer-setup) - -(defun elfeed-search--live-update () - "Update the elfeed-search buffer based on the contents of the minibuffer." - (when (eq :live elfeed-search-filter-active) - (let ((buffer (elfeed-search-buffer)) - (current-filter (minibuffer-contents-no-properties))) - (when buffer - (with-current-buffer buffer - (let* ((window (get-buffer-window (elfeed-search-buffer))) - (height (window-total-height window)) - (limiter (if window - (format "#%d " height) - "#1 ")) - (elfeed-search-filter (concat limiter current-filter))) - (elfeed-search-update :force) - (setf elfeed-search-filter-overflowing - (= (length elfeed-search-entries) - height)))))))) - -(defun elfeed-search-live-filter () - "Filter the elfeed-search buffer as the filter is written." - (interactive) - (unwind-protect - (let ((elfeed-search-filter-active :live)) - (setq elfeed-search-filter - (read-from-minibuffer "Filter: " elfeed-search-filter))) - (elfeed-search-update :force))) - -;; Bookmarks - -;;;###autoload -(defun elfeed-search-bookmark-handler (record) - "Jump to an elfeed-search bookmarked location." - (elfeed) - (elfeed-search-set-filter (bookmark-prop-get record 'location))) - -(defun elfeed-search-bookmark-make-record () - "Return a bookmark record for the current elfeed-search buffer." - (let* ((filter (elfeed-search-parse-filter elfeed-search-filter)) - (tags (plist-get filter :must-have))) - `(,(format "elfeed %s" elfeed-search-filter) - (location . ,elfeed-search-filter) - (tags ,@(mapcar #'symbol-name tags)) - (handler . elfeed-search-bookmark-handler)))) - -;; Desktop Save - -(defun elfeed-search-desktop-save (_desktop-dirname) - "Save the state of the current elfeed-search buffer so that it - may be restored as part of a saved desktop. Also save the state - of the db for when `desktop-auto-save-timeout' is enabled." - (elfeed-db-save) - elfeed-search-filter) - -;;;###autoload -(defun elfeed-search-desktop-restore (_file-name _buffer-name search-filter) - "Restore the state of an elfeed-search buffer on desktop restore." - (elfeed) - (elfeed-search-set-filter search-filter) - (current-buffer)) - -;;;###autoload -(add-to-list 'desktop-buffer-mode-handlers - '(elfeed-search-mode . elfeed-search-desktop-restore)) - -(provide 'elfeed-search) - -;;; elfeed-search.el ends here blob - 4915cae4d5a0e3046f0fb92986a5e4019003be8d (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed-show.el +++ /dev/null @@ -1,500 +0,0 @@ -;;; elfeed-show.el --- display feed entries -*- lexical-binding: t; -*- - -;; This is free and unencumbered software released into the public domain. - -;;; Code: - -(require 'cl-lib) -(require 'shr) -(require 'url-parse) -(require 'browse-url) -(require 'message) ; faces -(require 'bookmark) -(bookmark-maybe-load-default-file) - -(require 'elfeed) -(require 'elfeed-db) -(require 'elfeed-lib) -(require 'elfeed-search) - -(defcustom elfeed-show-truncate-long-urls t - "When non-nil, use an ellipsis to shorten very long displayed URLs." - :group 'elfeed - :type 'boolean) - -(defcustom elfeed-show-entry-author t - "When non-nil, show the entry's author (if it's in the entry's metadata)." - :group 'elfeed - :type 'boolean) - -(defvar elfeed-show-entry nil - "The entry being displayed in this buffer.") - -(defcustom elfeed-show-entry-switch #'switch-to-buffer - "Function used to display the feed entry buffer." - :group 'elfeed - :type '(choice (function-item switch-to-buffer) - (function-item pop-to-buffer) - function)) - -(defcustom elfeed-show-entry-delete #'elfeed-kill-buffer - "Function called when quitting from the elfeed-entry buffer. -Called without arguments." - :group 'elfeed - :type '(choice (function-item elfeed-kill-buffer) - (function-item delete-window) - function)) - -(defvar elfeed-show-refresh-function #'elfeed-show-refresh--mail-style - "Function called to refresh the `*elfeed-entry*' buffer.") - -(defvar elfeed-show-mode-map - (let ((map (make-sparse-keymap))) - (prog1 map - (suppress-keymap map) - (define-key map "h" #'describe-mode) - (define-key map "d" #'elfeed-show-save-enclosure) - (define-key map "q" #'elfeed-kill-buffer) - (define-key map "g" #'elfeed-show-refresh) - (define-key map "n" #'elfeed-show-next) - (define-key map "p" #'elfeed-show-prev) - (define-key map "s" #'elfeed-show-new-live-search) - (define-key map "b" #'elfeed-show-visit) - (define-key map "y" #'elfeed-show-yank) - (define-key map "u" #'elfeed-show-tag--unread) - (define-key map "+" #'elfeed-show-tag) - (define-key map "-" #'elfeed-show-untag) - (define-key map "<" #'beginning-of-buffer) - (define-key map ">" #'end-of-buffer) - (define-key map (kbd "SPC") #'scroll-up-command) - (define-key map (kbd "DEL") #'scroll-down-command) - (define-key map (kbd "TAB") #'elfeed-show-next-link) - (define-key map "\e\t" #'shr-previous-link) - (define-key map [backtab] #'shr-previous-link) - (define-key map "c" #'elfeed-kill-link-url-at-point) - (define-key map [mouse-2] #'shr-browse-url) - (define-key map "A" #'elfeed-show-add-enclosure-to-playlist) - (define-key map "P" #'elfeed-show-play-enclosure))) - "Keymap for `elfeed-show-mode'.") - -(defun elfeed-show-mode () - "Mode for displaying Elfeed feed entries. -\\{elfeed-show-mode-map}" - (interactive) - (kill-all-local-variables) - (use-local-map elfeed-show-mode-map) - (setq major-mode 'elfeed-show-mode - mode-name "elfeed-show" - buffer-read-only t) - (buffer-disable-undo) - (make-local-variable 'elfeed-show-entry) - (set (make-local-variable 'bookmark-make-record-function) - #'elfeed-show-bookmark-make-record) - (run-mode-hooks 'elfeed-show-mode-hook)) - -(defalias 'elfeed-show-tag--unread - (elfeed-expose #'elfeed-show-tag 'unread) - "Mark the current entry unread.") - -(defun elfeed-insert-html (html &optional base-url) - "Converted HTML markup to a propertized string." - (shr-insert-document - (if (elfeed-libxml-supported-p) - (with-temp-buffer - ;; insert to work around libxml-parse-html-region bug - (when base-url - (insert (format "" base-url))) - (insert html) - (libxml-parse-html-region (point-min) (point-max) base-url)) - '(i () "Elfeed: libxml2 functionality is unavailable")))) - -(cl-defun elfeed-insert-link (url &optional (content url)) - "Insert a clickable hyperlink to URL titled CONTENT." - (when (and elfeed-show-truncate-long-urls - (integerp shr-width) - (> (length content) (- shr-width 8))) - (let ((len (- (/ shr-width 2) 10))) - (setq content (format "%s[...]%s" - (substring content 0 len) - (substring content (- len)))))) - (elfeed-insert-html (format "%s" url content))) - -(defun elfeed-compute-base (url) - "Return the base URL for URL, useful for relative paths." - (let ((obj (url-generic-parse-url url))) - (setf (url-filename obj) nil) - (setf (url-target obj) nil) - (url-recreate-url obj))) - -(defun elfeed--show-format-author (author) - "Format author plist for the header." - (cl-destructuring-bind (&key name uri email &allow-other-keys) - author - (cond ((and name uri email) - (format "%s <%s> (%s)" name email uri)) - ((and name email) - (format "%s <%s>" name email)) - ((and name uri) - (format "%s (%s)" name uri)) - (name name) - (email email) - (uri uri) - ("[unknown]")))) - -(defun elfeed-show-refresh--mail-style () - "Update the buffer to match the selected entry, using a mail-style." - (interactive) - (let* ((inhibit-read-only t) - (title (elfeed-entry-title elfeed-show-entry)) - (date (seconds-to-time (elfeed-entry-date elfeed-show-entry))) - (authors (elfeed-meta elfeed-show-entry :authors)) - (link (elfeed-entry-link elfeed-show-entry)) - (tags (elfeed-entry-tags elfeed-show-entry)) - (tagsstr (mapconcat #'symbol-name tags ", ")) - (nicedate (format-time-string "%a, %e %b %Y %T %Z" date)) - (content (elfeed-deref (elfeed-entry-content elfeed-show-entry))) - (type (elfeed-entry-content-type elfeed-show-entry)) - (feed (elfeed-entry-feed elfeed-show-entry)) - (feed-title (elfeed-feed-title feed)) - (base (and feed (elfeed-compute-base (elfeed-feed-url feed))))) - (erase-buffer) - (insert (format (propertize "Title: %s\n" 'face 'message-header-name) - (propertize title 'face 'message-header-subject))) - (when elfeed-show-entry-author - (dolist (author authors) - (let ((formatted (elfeed--show-format-author author))) - (insert - (format (propertize "Author: %s\n" 'face 'message-header-name) - (propertize formatted 'face 'message-header-to)))))) - (insert (format (propertize "Date: %s\n" 'face 'message-header-name) - (propertize nicedate 'face 'message-header-other))) - (insert (format (propertize "Feed: %s\n" 'face 'message-header-name) - (propertize feed-title 'face 'message-header-other))) - (when tags - (insert (format (propertize "Tags: %s\n" 'face 'message-header-name) - (propertize tagsstr 'face 'message-header-other)))) - (insert (propertize "Link: " 'face 'message-header-name)) - (elfeed-insert-link link link) - (insert "\n") - (cl-loop for enclosure in (elfeed-entry-enclosures elfeed-show-entry) - do (insert (propertize "Enclosure: " 'face 'message-header-name)) - do (elfeed-insert-link (car enclosure)) - do (insert "\n")) - (insert "\n") - (if content - (if (eq type 'html) - (elfeed-insert-html content base) - (insert content)) - (insert (propertize "(empty)\n" 'face 'italic))) - (goto-char (point-min)))) - -(defun elfeed-show-refresh () - "Update the buffer to match the selected entry." - (interactive) - (call-interactively elfeed-show-refresh-function)) - -(defcustom elfeed-show-unique-buffers nil - "When non-nil, every entry buffer gets a unique name. -This allows for displaying multiple show buffers at the same -time." - :group 'elfeed - :type 'boolean) - -(defun elfeed-show--buffer-name (entry) - "Return the appropriate buffer name for ENTRY. -The result depends on the value of `elfeed-show-unique-buffers'." - (if elfeed-show-unique-buffers - (format "*elfeed-entry-<%s %s>*" - (elfeed-entry-title entry) - (format-time-string "%F" (elfeed-entry-date entry))) - "*elfeed-entry*")) - -(defun elfeed-show-entry (entry) - "Display ENTRY in the current buffer." - (let ((buff (get-buffer-create (elfeed-show--buffer-name entry)))) - (with-current-buffer buff - (elfeed-show-mode) - (setq elfeed-show-entry entry) - (elfeed-show-refresh)) - (funcall elfeed-show-entry-switch buff))) - -(defun elfeed-show-next () - "Show the next item in the elfeed-search buffer." - (interactive) - (funcall elfeed-show-entry-delete) - (with-current-buffer (elfeed-search-buffer) - (when elfeed-search-remain-on-entry (forward-line 1)) - (call-interactively #'elfeed-search-show-entry))) - -(defun elfeed-show-prev () - "Show the previous item in the elfeed-search buffer." - (interactive) - (funcall elfeed-show-entry-delete) - (with-current-buffer (elfeed-search-buffer) - (when elfeed-search-remain-on-entry (forward-line 1)) - (forward-line -2) - (call-interactively #'elfeed-search-show-entry))) - -(defun elfeed-show-new-live-search () - "Kill the current buffer, search again in *elfeed-search*." - (interactive) - (elfeed-kill-buffer) - (elfeed) - (elfeed-search-live-filter)) - -(defun elfeed-show-visit (&optional use-generic-p) - "Visit the current entry in your browser using `browse-url'. -If there is a prefix argument, visit the current entry in the -browser defined by `browse-url-generic-program'." - (interactive "P") - (let ((link (elfeed-entry-link elfeed-show-entry))) - (when link - (message "Sent to browser: %s" link) - (if use-generic-p - (browse-url-generic link) - (browse-url link))))) - -(defun elfeed-show-yank () - "Copy the current entry link URL to the clipboard." - (interactive) - (let ((link (elfeed-entry-link elfeed-show-entry))) - (when link - (kill-new link) - (if (fboundp 'gui-set-selection) - (gui-set-selection 'PRIMARY link) - (with-no-warnings - (x-set-selection 'PRIMARY link))) - (message "Yanked: %s" link)))) - -(defun elfeed-show-tag (&rest tags) - "Add TAGS to the displayed entry." - (interactive (list (intern (read-from-minibuffer "Tag: ")))) - (let ((entry elfeed-show-entry)) - (apply #'elfeed-tag entry tags) - (with-current-buffer (elfeed-search-buffer) - (elfeed-search-update-entry entry)) - (elfeed-show-refresh))) - -(defun elfeed-show-untag (&rest tags) - "Remove TAGS from the displayed entry." - (interactive (let* ((tags (elfeed-entry-tags elfeed-show-entry)) - (names (mapcar #'symbol-name tags)) - (select (completing-read "Untag: " names nil :match))) - (list (intern select)))) - (let ((entry elfeed-show-entry)) - (apply #'elfeed-untag entry tags) - (with-current-buffer (elfeed-search-buffer) - (elfeed-search-update-entry entry)) - (elfeed-show-refresh))) - -;; Enclosures: - -(defcustom elfeed-enclosure-default-dir (expand-file-name "~") - "Default directory for saving enclosures. -This can be either a string (a file system path), or a function -that takes a filename and the mime-type as arguments, and returns -the enclosure dir." - :type 'directory - :group 'elfeed - :safe 'stringp) - -(defcustom elfeed-save-multiple-enclosures-without-asking nil - "If non-nil, saving multiple enclosures asks once for a -directory and saves all attachments in the chosen directory." - :type 'boolean - :group 'elfeed) - -(defvar elfeed-show-enclosure-filename-function - #'elfeed-show-enclosure-filename-remote - "Function called to generate the filename for an enclosure.") - -(defun elfeed--download-enclosure (url path) - "Download asynchronously the enclosure from URL to PATH." - (if (require 'async nil :noerror) - (with-no-warnings - (async-start - (lambda () - (url-copy-file url path t)) - (lambda (_) - (message (format "%s downloaded" url))))) - (url-copy-file url path t))) - -(defun elfeed--get-enclosure-num (prompt entry &optional multi) - "Ask the user with PROMPT for an enclosure number for ENTRY. -The number is [1..n] for enclosures \[0..(n-1)] in the entry. If -MULTI is nil, return the number for the enclosure; -otherwise (MULTI is non-nil), accept ranges of enclosure numbers, -as per `elfeed-split-ranges-to-numbers', and return the -corresponding string." - (let* ((count (length (elfeed-entry-enclosures entry))) - def) - (when (zerop count) - (error "No enclosures to this entry")) - (if (not multi) - (if (= count 1) - (read-number (format "%s: " prompt) 1) - (read-number (format "%s (1-%d): " prompt count))) - (progn - (setq def (if (= count 1) "1" (format "1-%d" count))) - (read-string (format "%s (default %s): " prompt def) - nil nil def))))) - -(defun elfeed--request-enclosure-path (fname path) - "Ask the user where to save FNAME (default is PATH/FNAME)." - (let ((fpath (expand-file-name - (read-file-name "Save as: " path nil nil fname) path))) - (if (file-directory-p fpath) - (expand-file-name fname fpath) - fpath))) - -(defun elfeed--request-enclosures-dir (path) - "Ask the user where to save multiple enclosures (default is PATH)." - (let ((fpath (expand-file-name - (read-directory-name - (format "Save in directory: ") path nil nil nil) path))) - (if (file-directory-p fpath) - fpath))) - -(defun elfeed-show-enclosure-filename-remote (_entry url-enclosure) - "Returns the remote filename as local filename for an enclosure." - (file-name-nondirectory - (url-unhex-string - (car (url-path-and-query (url-generic-parse-url - url-enclosure)))))) - -(defun elfeed-show-save-enclosure-single (&optional entry enclosure-index) - "Save enclosure number ENCLOSURE-INDEX from ENTRY. -If ENTRY is nil use the elfeed-show-entry variable. -If ENCLOSURE-INDEX is nil ask for the enclosure number." - (interactive) - (let* ((path elfeed-enclosure-default-dir) - (entry (or entry elfeed-show-entry)) - (enclosure-index (or enclosure-index - (elfeed--get-enclosure-num - "Enclosure to save" entry))) - (url-enclosure (car (elt (elfeed-entry-enclosures entry) - (- enclosure-index 1)))) - (fname - (funcall elfeed-show-enclosure-filename-function - entry url-enclosure)) - (retry t) - (fpath)) - (while retry - (setf fpath (elfeed--request-enclosure-path fname path) - retry (and (file-exists-p fpath) - (not (y-or-n-p (format "Overwrite '%s'?" fpath)))))) - (elfeed--download-enclosure url-enclosure fpath))) - -(defun elfeed-show-save-enclosure-multi (&optional entry) - "Offer to save multiple entry enclosures from the current entry. -Default is to save all enclosures, [1..n], where n is the number of -enclosures. You can type multiple values separated by space, e.g. - 1 3-6 8 -will save enclosures 1,3,4,5,6 and 8. - -Furthermore, there is a shortcut \"a\" which so means all -enclosures, but as this is the default, you may not need it." - (interactive) - (let* ((entry (or entry elfeed-show-entry)) - (attachstr (elfeed--get-enclosure-num - "Enclosure number range (or 'a' for 'all')" entry t)) - (count (length (elfeed-entry-enclosures entry))) - (attachnums (elfeed-split-ranges-to-numbers attachstr count)) - (path elfeed-enclosure-default-dir) - (fpath)) - (if elfeed-save-multiple-enclosures-without-asking - (let ((attachdir (elfeed--request-enclosures-dir path))) - (dolist (enclosure-index attachnums) - (let* ((url-enclosure - (aref (elfeed-entry-enclosures entry) enclosure-index)) - (fname - (funcall elfeed-show-enclosure-filename-function - entry url-enclosure)) - (retry t)) - (while retry - (setf fpath (expand-file-name (concat attachdir fname) path) - retry - (and (file-exists-p fpath) - (not (y-or-n-p (format "Overwrite '%s'?" fpath)))))) - (elfeed--download-enclosure url-enclosure fpath)))) - (dolist (enclosure-index attachnums) - (elfeed-show-save-enclosure-single entry enclosure-index))))) - -(defun elfeed-show-save-enclosure (&optional multi) - "Offer to save enclosure(s). -If MULTI (prefix-argument) is nil, save a single one, otherwise, -offer to save a range of enclosures." - (interactive "P") - (if multi - (elfeed-show-save-enclosure-multi) - (elfeed-show-save-enclosure-single))) - -(defun elfeed--enclosure-maybe-prompt-index (entry) - "Prompt for an enclosure if there are multiple in ENTRY." - (if (= 1 (length (elfeed-entry-enclosures entry))) - 1 - (elfeed--get-enclosure-num "Enclosure to play" entry))) - -(defun elfeed-show-play-enclosure (enclosure-index) - "Play enclosure number ENCLOSURE-INDEX from current entry using EMMS. -Prompts for ENCLOSURE-INDEX when called interactively." - (interactive (list (elfeed--enclosure-maybe-prompt-index elfeed-show-entry))) - (elfeed-show-add-enclosure-to-playlist enclosure-index) - (with-no-warnings - (with-current-emms-playlist - (save-excursion - (emms-playlist-last) - (emms-playlist-mode-play-current-track))))) - -(defun elfeed-show-add-enclosure-to-playlist (enclosure-index) - "Add enclosure number ENCLOSURE-INDEX to current EMMS playlist. -Prompts for ENCLOSURE-INDEX when called interactively." - - (interactive (list (elfeed--enclosure-maybe-prompt-index elfeed-show-entry))) - (require 'emms) ;; optional - (with-no-warnings ;; due to lazy (require ) - (emms-add-url (car (elt (elfeed-entry-enclosures elfeed-show-entry) - (- enclosure-index 1)))))) - -(defun elfeed-show-next-link () - "Skip to the next link, exclusive of the Link header." - (interactive) - (let ((properties (text-properties-at (line-beginning-position)))) - (when (memq 'message-header-name properties) - (forward-paragraph)) - (shr-next-link))) - -(defun elfeed-kill-link-url-at-point () - "Get link URL at point and store in kill-ring." - (interactive) - (let ((url (or (elfeed-get-link-at-point) - (elfeed-get-url-at-point)))) - (if url - (progn (kill-new url) (message url)) - (call-interactively 'shr-copy-url)))) - -;; Bookmarks - -;;;###autoload -(defun elfeed-show-bookmark-handler (record) - "Show the bookmarked entry saved in the `RECORD'." - (let* ((id (bookmark-prop-get record 'id)) - (entry (elfeed-db-get-entry id)) - (position (bookmark-get-position record))) - (elfeed-show-entry entry) - (goto-char position))) - -(defun elfeed-show-bookmark-make-record () - "Save the current position and the entry into a bookmark." - (let ((id (elfeed-entry-id elfeed-show-entry)) - (position (point)) - (title (elfeed-entry-title elfeed-show-entry))) - `(,(format "elfeed entry \"%s\"" title) - (id . ,id) - (location . ,title) - (position . ,position) - (handler . elfeed-show-bookmark-handler)))) - -(provide 'elfeed-show) - -;;; elfeed-show.el ends here blob - bbfef261ea6889e18293cd02a11fed62dac59bfb (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/elfeed.el +++ /dev/null @@ -1,665 +0,0 @@ -;;; elfeed.el --- an Emacs Atom/RSS feed reader -*- lexical-binding: t; -*- - -;; This is free and unencumbered software released into the public domain. - -;; Author: Christopher Wellons -;; URL: https://github.com/skeeto/elfeed - -;;; Commentary: - -;; Elfeed is a web feed client for Emacs, inspired by notmuch. See -;; the README for full documentation. - -;;; Code: - -(require 'cl-lib) -(require 'xml) -(require 'xml-query) -(require 'url-parse) -(require 'url-queue) - -(require 'elfeed-db) -(require 'elfeed-lib) -(require 'elfeed-log) -(require 'elfeed-curl) - -;; Interface to elfeed-search (lazy required) -(declare-function elfeed-search-buffer 'elfeed-search ()) -(declare-function elfeed-search-mode 'elfeed-search ()) - -(defgroup elfeed () - "An Emacs web feed reader." - :group 'comm) - -(defconst elfeed-version "3.4.1") - -(defcustom elfeed-feeds () - "List of all feeds that Elfeed should follow. -You must add your feeds to this list. - -In its simplest form this will be a list of strings of feed URLs. -Items in this list can also be list whose car is the feed URL -and cdr is a list of symbols to be applied to all discovered -entries as tags (\"autotags\"). For example, - - (setq elfeed-feeds '(\"http://foo/\" - \"http://bar/\" - (\"http://baz/\" comic))) - -All entries from the \"baz\" feed will be tagged as \"comic\" -when they are first discovered." - :group 'elfeed - :type '(repeat (choice string - (cons string (repeat symbol))))) - -(defcustom elfeed-feed-functions - '(elfeed-get-link-at-point - elfeed-get-url-at-point - elfeed-clipboard-get) - "List of functions to use to get possible feeds for `elfeed-add-feed'. -Each function should accept no arguments, and return a string or nil." - :group 'elfeed - :type 'hook - :options '(elfeed-get-link-at-point - elfeed-get-url-at-point - elfeed-clipboard-get)) - -(defcustom elfeed-use-curl - (not (null (executable-find elfeed-curl-program-name))) - "If non-nil, fetch feeds using curl instead of `url-retrieve'." - :group 'elfeed - :type 'boolean) - -(defcustom elfeed-user-agent (format "Emacs Elfeed %s" elfeed-version) - "User agent string to use for Elfeed (requires `elfeed-use-curl')." - :group 'elfeed - :type 'string) - -(defcustom elfeed-initial-tags '(unread) - "Initial tags for new entries." - :group 'elfeed - :type '(repeat symbol)) - -;; Fetching: - -(defvar elfeed-http-error-hooks () - "Hooks to run when an http connection error occurs. -It is called with 2 arguments. The first argument is the url of -the failing feed. The second argument is the http status code.") - -(defvar elfeed-parse-error-hooks () - "Hooks to run when an error occurs during the parsing of a feed. -It is called with 2 arguments. The first argument is the url of -the failing feed. The second argument is the error message .") - -(defvar elfeed-update-hooks () - "Hooks to run any time a feed update has completed a request. -It is called with 1 argument: the URL of the feed that was just -updated. The hook is called even when no new entries were -found.") - -(defvar elfeed-update-init-hooks () - "Hooks called when one or more feed updates have begun. -Receivers may want to, say, update a display to indicate that -updates are pending.") - -(defvar elfeed-tag-hooks () - "Hooks called when one or more entries add tags. -It is called with 2 arguments. The first argument is the entry -list. The second argument is the tag list.") - -(defvar elfeed-untag-hooks () - "Hooks called when one or more entries remove tags. -It is called with 2 arguments. The first argument is the entry -list. The second argument is the tag list.") - -(defvar elfeed--inhibit-update-init-hooks nil - "When non-nil, don't run `elfeed-update-init-hooks'.") - -(defun elfeed-queue-count-active () - "Return the number of items in process." - (if elfeed-use-curl - elfeed-curl-queue-active - (cl-count-if #'url-queue-buffer url-queue))) - -(defun elfeed-queue-count-total () - "Return the number of items in process." - (if elfeed-use-curl - (+ (length elfeed-curl-queue) elfeed-curl-queue-active) - (length url-queue))) - -(defun elfeed-set-max-connections (n) - "Limit the maximum number of concurrent connections to N." - (if elfeed-use-curl - (setf elfeed-curl-max-connections n) - (setf url-queue-parallel-processes n))) - -(defun elfeed-get-max-connections () - "Get the maximum number of concurrent connections." - (if elfeed-use-curl - elfeed-curl-max-connections - url-queue-parallel-processes)) - -(defun elfeed-set-timeout (seconds) - "Limit the time for fetching a feed to SECONDS." - (if elfeed-use-curl - (setf elfeed-curl-timeout seconds) - (setf url-queue-timeout seconds))) - -(defun elfeed-get-timeout () - "Get the time limit for fetching feeds in SECONDS." - (if elfeed-use-curl - elfeed-curl-timeout - url-queue-timeout)) - -(defun elfeed-is-status-error (status use-curl) - "Check if HTTP request returned status means a error." - (or (and use-curl (null status)) ; nil = error - (and (not use-curl) (eq (car status) :error)))) - -(defmacro elfeed-with-fetch (url &rest body) - "Asynchronously run BODY in a buffer with the contents from URL. -This macro is anaphoric, with STATUS referring to the status from -`url-retrieve'/cURL and USE-CURL being the original invoked-value -of `elfeed-use-curl'." - (declare (indent defun)) - `(let* ((use-curl elfeed-use-curl) ; capture current value in closure - (cb (lambda (status) ,@body))) - (if elfeed-use-curl - (let* ((feed (elfeed-db-get-feed url)) - (last-modified (elfeed-meta feed :last-modified)) - (etag (elfeed-meta feed :etag)) - (headers `(("User-Agent" . ,elfeed-user-agent)))) - (when etag - (push `("If-None-Match" . ,etag) headers)) - (when last-modified - (push `("If-Modified-Since" . ,last-modified) headers)) - (elfeed-curl-enqueue ,url cb :headers headers)) - (url-queue-retrieve ,url cb () t t)))) - -(defun elfeed-unjam () - "Manually clear the connection pool when connections fail to timeout. -This is a workaround for issues in `url-queue-retrieve'." - (interactive) - (if elfeed-use-curl - (setf elfeed-curl-queue nil - elfeed-curl-queue-active 0) - (let ((fails (mapcar #'url-queue-url url-queue))) - (when fails - (elfeed-log 'warn "Elfeed aborted feeds: %s" - (mapconcat #'identity fails " "))) - (setf url-queue nil))) - (run-hooks 'elfeed-update-init-hooks)) - -;; Parsing: - -(defun elfeed-feed-type (content) - "Return the feed type (:atom, :rss, :rss1.0) or nil for unknown." - (let ((top (xml-query-strip-ns (caar content)))) - (cadr (assoc top '((feed :atom) - (rss :rss) - (RDF :rss1.0)))))) - -(defun elfeed-generate-id (&optional content) - "Generate an ID based on CONTENT or from the current time." - (concat "urn:sha1:" (sha1 (format "%s" (or content (float-time)))))) - -(defun elfeed--atom-content (entry) - "Get content string from ENTRY." - (let ((content-type (xml-query* (content :type) entry))) - (if (equal content-type "xhtml") - (with-temp-buffer - (let ((xhtml (cddr (xml-query* (content) entry)))) - (dolist (element xhtml) - (if (stringp element) - (insert element) - (elfeed-xml-unparse element)))) - (buffer-string)) - (let ((all-content - (or (xml-query-all* (content *) entry) - (xml-query-all* (summary *) entry)))) - (when all-content - (apply #'concat all-content)))))) - -(defvar elfeed-new-entry-parse-hook '() - "Hook to be called after parsing a new entry. - -Take three arguments: the feed TYPE, the XML structure for the -entry, and the Elfeed ENTRY object. Return value is ignored, and -is called for side-effects on the ENTRY object.") - -(defsubst elfeed--fixup-protocol (protocol url) - "Prepend PROTOCOL to URL if it is protocol-relative. -If PROTOCOL is nil, returns URL." - (if (and protocol url (string-match-p "^//[^/]" url)) - (concat protocol ":" url) - url)) - -(defsubst elfeed--atom-authors-to-plist (authors) - "Parse list of author XML tags into list of plists." - (let ((result ())) - (dolist (author authors) - (let ((plist ()) - (name (xml-query* (name *) author)) - (uri (xml-query* (uri *) author)) - (email (xml-query* (email *) author))) - (when email - (setf plist (list :email (elfeed-cleanup email)))) - (when uri - (setf plist (nconc (list :uri (elfeed-cleanup uri)) plist))) - (when name - (setf plist (nconc (list :name (elfeed-cleanup name)) plist))) - (push plist result))) - (nreverse result))) - -(defsubst elfeed--creators-to-plist (creators) - "Convert Dublin Core list of creators into an authors plist." - (cl-loop for creator in creators - collect (list :name creator))) - -(defun elfeed-entries-from-atom (url xml) - "Turn parsed Atom content into a list of elfeed-entry structs." - (let* ((feed-id url) - (protocol (url-type (url-generic-parse-url url))) - (namespace (elfeed-url-to-namespace url)) - (feed (elfeed-db-get-feed feed-id)) - (title (elfeed-cleanup (xml-query* (feed title *) xml))) - (authors (xml-query-all* (feed author) xml)) - (xml-base (or (xml-query* (feed :base) xml) url)) - (autotags (elfeed-feed-autotags url))) - (setf (elfeed-feed-url feed) url - (elfeed-feed-title feed) title - (elfeed-feed-author feed) (elfeed--atom-authors-to-plist authors)) - (cl-loop for entry in (xml-query-all* (feed entry) xml) collect - (let* ((title (or (xml-query* (title *) entry) "")) - (xml-base (elfeed-update-location - xml-base (xml-query* (:base) (list entry)))) - (anylink (xml-query* (link :href) entry)) - (altlink (xml-query* (link [rel "alternate"] :href) entry)) - (link (elfeed--fixup-protocol - protocol - (elfeed-update-location xml-base - (or altlink anylink)))) - (date (or (xml-query* (published *) entry) - (xml-query* (updated *) entry) - (xml-query* (date *) entry) - (xml-query* (modified *) entry) ; Atom 0.3 - (xml-query* (issued *) entry))) ; Atom 0.3 - (authors (nconc (elfeed--atom-authors-to-plist - (xml-query-all* (author) entry)) - ;; Dublin Core - (elfeed--creators-to-plist - (xml-query-all* (creator *) entry)))) - (categories (xml-query-all* (category :term) entry)) - (content (elfeed--atom-content entry)) - (id (or (xml-query* (id *) entry) link - (elfeed-generate-id content))) - (type (or (xml-query* (content :type) entry) - (xml-query* (summary :type) entry) - "")) - (tags (elfeed-normalize-tags autotags elfeed-initial-tags)) - (content-type (if (string-match-p "html" type) 'html nil)) - (etags (xml-query-all* (link [rel "enclosure"]) entry)) - (enclosures - (cl-loop for enclosure in etags - for wrap = (list enclosure) - for href = (xml-query* (:href) wrap) - for type = (xml-query* (:type) wrap) - for length = (xml-query* (:length) wrap) - collect (list href type length))) - (db-entry (elfeed-entry--create - :title (elfeed-cleanup title) - :feed-id feed-id - :id (cons namespace (elfeed-cleanup id)) - :link (elfeed-cleanup link) - :tags tags - :date (or (elfeed-float-time date) (float-time)) - :content content - :enclosures enclosures - :content-type content-type - :meta `(,@(when authors - (list :authors authors)) - ,@(when categories - (list :categories categories)))))) - (dolist (hook elfeed-new-entry-parse-hook) - (funcall hook :atom entry db-entry)) - db-entry)))) - -(defsubst elfeed--rss-author-to-plist (author) - "Parse an RSS author element into an authors plist." - (when author - (let ((clean (elfeed-cleanup author))) - (if (string-match "^\\(.*\\) (\\([^)]+\\))$" clean) - (list (list :name (match-string 2 clean) - :email (match-string 1 clean))) - (list (list :email clean)))))) - -(defun elfeed-entries-from-rss (url xml) - "Turn parsed RSS content into a list of elfeed-entry structs." - (let* ((feed-id url) - (protocol (url-type (url-generic-parse-url url))) - (namespace (elfeed-url-to-namespace url)) - (feed (elfeed-db-get-feed feed-id)) - (title (elfeed-cleanup (xml-query* (rss channel title *) xml))) - (autotags (elfeed-feed-autotags url))) - (setf (elfeed-feed-url feed) url - (elfeed-feed-title feed) title) - (cl-loop for item in (xml-query-all* (rss channel item) xml) collect - (let* ((title (or (xml-query* (title *) item) "")) - (guid (xml-query* (guid *) item)) - (link (elfeed--fixup-protocol - protocol - (or (xml-query* (link *) item) guid))) - (date (or (xml-query* (pubDate *) item) - (xml-query* (date *) item))) - (authors (nconc (elfeed--rss-author-to-plist - (xml-query* (author *) item)) - ;; Dublin Core - (elfeed--creators-to-plist - (xml-query-all* (creator *) item)))) - (categories (xml-query-all* (category *) item)) - (content (or (xml-query-all* (encoded *) item) - (xml-query-all* (description *) item))) - (description (apply #'concat content)) - (id (or guid link (elfeed-generate-id description))) - (full-id (cons namespace (elfeed-cleanup id))) - (original (elfeed-db-get-entry full-id)) - (original-date (and original (elfeed-entry-date original))) - (tags (elfeed-normalize-tags autotags elfeed-initial-tags)) - (etags (xml-query-all* (enclosure) item)) - (enclosures - (cl-loop for enclosure in etags - for wrap = (list enclosure) - for url = (xml-query* (:url) wrap) - for type = (xml-query* (:type) wrap) - for length = (xml-query* (:length) wrap) - collect (list url type length))) - (db-entry (elfeed-entry--create - :title (elfeed-cleanup title) - :id full-id - :feed-id feed-id - :link (elfeed-cleanup link) - :tags tags - :date (elfeed-new-date-for-entry - original-date date) - :enclosures enclosures - :content description - :content-type 'html - :meta `(,@(when authors - (list :authors authors)) - ,@(when categories - (list :categories categories)))))) - (dolist (hook elfeed-new-entry-parse-hook) - (funcall hook :rss item db-entry)) - db-entry)))) - -(defun elfeed-entries-from-rss1.0 (url xml) - "Turn parsed RSS 1.0 content into a list of elfeed-entry structs." - (let* ((feed-id url) - (namespace (elfeed-url-to-namespace url)) - (feed (elfeed-db-get-feed feed-id)) - (title (elfeed-cleanup (xml-query* (RDF channel title *) xml))) - (autotags (elfeed-feed-autotags url))) - (setf (elfeed-feed-url feed) url - (elfeed-feed-title feed) title) - (cl-loop for item in (xml-query-all* (RDF item) xml) collect - (let* ((title (or (xml-query* (title *) item) "")) - (link (xml-query* (link *) item)) - (date (or (xml-query* (pubDate *) item) - (xml-query* (date *) item))) - (description - (apply #'concat (xml-query-all* (description *) item))) - (id (or link (elfeed-generate-id description))) - (full-id (cons namespace (elfeed-cleanup id))) - (original (elfeed-db-get-entry full-id)) - (original-date (and original (elfeed-entry-date original))) - (tags (elfeed-normalize-tags autotags elfeed-initial-tags)) - (db-entry (elfeed-entry--create - :title (elfeed-cleanup title) - :id full-id - :feed-id feed-id - :link (elfeed-cleanup link) - :tags tags - :date (elfeed-new-date-for-entry - original-date date) - :content description - :content-type 'html))) - (dolist (hook elfeed-new-entry-parse-hook) - (funcall hook :rss1.0 item db-entry)) - db-entry)))) - -(defun elfeed-feed-list () - "Return a flat list version of `elfeed-feeds'. -Only a list of strings will be returned." - ;; Validate elfeed-feeds and fail early rather than asynchronously later. - (dolist (feed elfeed-feeds) - (unless (cl-typecase feed - (list (and (stringp (car feed)) - (cl-every #'symbolp (cdr feed)))) - (string t)) - (error "elfeed-feeds malformed, bad entry: %S" feed))) - (cl-loop for feed in elfeed-feeds - when (listp feed) collect (car feed) - else collect feed)) - -(defun elfeed-feed-autotags (url-or-feed) - "Return tags to automatically apply to all entries from URL-OR-FEED." - (let ((url (if (elfeed-feed-p url-or-feed) - (or (elfeed-feed-url url-or-feed) - (elfeed-feed-id url-or-feed)) - url-or-feed))) - (mapcar #'elfeed-keyword->symbol (cdr (assoc url elfeed-feeds))))) - -(defun elfeed-apply-autotags-now () - "Apply autotags to existing entries according to `elfeed-feeds'." - (interactive) - (with-elfeed-db-visit (entry feed) - (apply #'elfeed-tag entry (elfeed-feed-autotags feed)))) - -(defun elfeed-handle-http-error (url status) - "Handle an http error during retrieval of URL with STATUS code." - (cl-incf (elfeed-meta (elfeed-db-get-feed url) :failures 0)) - (run-hook-with-args 'elfeed-http-error-hooks url status) - (elfeed-log 'error "%s: %S" url status)) - -(defun elfeed-handle-parse-error (url error) - "Handle parse error during parsing of URL with ERROR message." - (cl-incf (elfeed-meta (elfeed-db-get-feed url) :failures 0)) - (run-hook-with-args 'elfeed-parse-error-hooks url error) - (elfeed-log 'error "%s: %s" url error)) - -(defun elfeed-update-feed (url) - "Update a specific feed." - (interactive (list (completing-read "Feed: " (elfeed-feed-list)))) - (unless elfeed--inhibit-update-init-hooks - (run-hooks 'elfeed-update-init-hooks)) - (elfeed-with-fetch url - (if (elfeed-is-status-error status use-curl) - (let ((print-escape-newlines t)) - (elfeed-handle-http-error - url (if use-curl elfeed-curl-error-message status))) - (condition-case error - (let ((feed (elfeed-db-get-feed url))) - (unless use-curl - (elfeed-move-to-first-empty-line) - (set-buffer-multibyte t)) - (unless (eql elfeed-curl-status-code 304) - ;; Update Last-Modified and Etag - (setf (elfeed-meta feed :last-modified) - (cdr (assoc "last-modified" elfeed-curl-headers)) - (elfeed-meta feed :etag) - (cdr (assoc "etag" elfeed-curl-headers))) - (if (equal url elfeed-curl-location) - (setf (elfeed-meta feed :canonical-url) nil) - (setf (elfeed-meta feed :canonical-url) elfeed-curl-location)) - (let* ((xml (elfeed-xml-parse-region (point) (point-max))) - (entries (cl-case (elfeed-feed-type xml) - (:atom (elfeed-entries-from-atom url xml)) - (:rss (elfeed-entries-from-rss url xml)) - (:rss1.0 (elfeed-entries-from-rss1.0 url xml)) - (otherwise - (error (elfeed-handle-parse-error - url "Unknown feed type.")))))) - (elfeed-db-add entries)))) - (error (elfeed-handle-parse-error url error)))) - (unless use-curl - (kill-buffer)) - (run-hook-with-args 'elfeed-update-hooks url))) - -(defun elfeed-candidate-feeds () - "Return a list of possible feeds from `elfeed-feed-functions'." - (let (res) - (run-hook-wrapped - 'elfeed-feed-functions - (lambda (fun) - (let* ((val (elfeed-cleanup (funcall fun)))) - (when (and (not (zerop (length val))) - (elfeed-looks-like-url-p val)) - (cl-pushnew val res :test #'equal))) - nil)) - (nreverse res))) - -(cl-defun elfeed-add-feed (url &key save) - "Manually add a feed to the database. -If SAVE is non-nil the new value of ‘elfeed-feeds’ is saved. When -called interactively, SAVE is set to t." - (interactive - (list - (let* ((feeds (elfeed-candidate-feeds)) - (prompt (if feeds (concat "URL (default " (car feeds) "): ") - "URL: ")) - (input (read-from-minibuffer prompt nil nil nil nil feeds)) - (result (elfeed-cleanup input))) - (cond ((not (zerop (length result))) result) - (feeds (car feeds)) - ((user-error "No feed to add")))) - :save t)) - (cl-pushnew url elfeed-feeds :test #'equal) - (when save - (customize-save-variable 'elfeed-feeds elfeed-feeds)) - (elfeed-update-feed url)) - -;;;###autoload -(defun elfeed-update () - "Update all the feeds in `elfeed-feeds'." - (interactive) - (elfeed-log 'info "Elfeed update: %s" - (format-time-string "%B %e %Y %H:%M:%S %Z")) - (let ((elfeed--inhibit-update-init-hooks t)) - (mapc #'elfeed-update-feed (elfeed--shuffle (elfeed-feed-list)))) - (run-hooks 'elfeed-update-init-hooks) - (elfeed-db-save)) - -;;;###autoload -(defun elfeed () - "Enter elfeed." - (interactive) - (switch-to-buffer (elfeed-search-buffer)) - (unless (eq major-mode 'elfeed-search-mode) - (elfeed-search-mode))) - -;; New entry filtering - -(cl-defun elfeed-make-tagger - (&key feed-title feed-url entry-title entry-link after before - add remove callback) - "Create a function that adds or removes tags on matching entries. - -FEED-TITLE, FEED-URL, ENTRY-TITLE, and ENTRY-LINK are regular -expressions or a list (not ), which indicates a negative -match. AFTER and BEFORE are relative times (see -`elfeed-time-duration'). Entries must match all provided -expressions. If an entry matches, add tags ADD and remove tags -REMOVE. - -Examples, - - (elfeed-make-tagger :feed-url \"youtube\\\\.com\" - :add '(video youtube)) - - (elfeed-make-tagger :before \"1 week ago\" - :remove 'unread) - - (elfeed-make-tagger :feed-url \"example\\\\.com\" - :entry-title '(not \"something interesting\") - :add 'junk) - -The returned function should be added to `elfeed-new-entry-hook'." - (let ((after-time (and after (elfeed-time-duration after))) - (before-time (and before (elfeed-time-duration before)))) - (when (and add (symbolp add)) (setf add (list add))) - (when (and remove (symbolp remove)) (setf remove (list remove))) - (lambda (entry) - (let ((feed (elfeed-entry-feed entry)) - (date (elfeed-entry-date entry)) - (case-fold-search t)) - (cl-flet ((match (r s) - (or (null r) - (if (listp r) - (not (string-match-p (cl-second r) s)) - (string-match-p r s))))) - (when (and - (match feed-title (elfeed-feed-title feed)) - (match feed-url (elfeed-feed-url feed)) - (match entry-title (elfeed-entry-title entry)) - (match entry-link (elfeed-entry-link entry)) - (or (not after-time) (> date (- (float-time) after-time))) - (or (not before-time) (< date (- (float-time) before-time)))) - (when add - (apply #'elfeed-tag entry add)) - (when remove - (apply #'elfeed-untag entry remove)) - (when callback - (funcall callback entry)) - entry)))))) - -;; OPML - -(defun elfeed--parse-opml (xml) - "Parse XML (from `xml-parse-region') into `elfeed-feeds' list." - (cl-loop for (tag attr . content) in (cl-remove-if-not #'listp xml) - count tag into work-around-bug ; bug#15326 - when (assoc 'xmlUrl attr) collect (cdr it) - else append (elfeed--parse-opml content))) - -;;;###autoload -(defun elfeed-load-opml (file) - "Load feeds from an OPML file into `elfeed-feeds'. -When called interactively, the changes to `elfeed-feeds' are -saved to your customization file." - (interactive "fOPML file: ") - (let* ((xml (xml-parse-file file)) - (feeds (elfeed--parse-opml xml)) - (full (append feeds elfeed-feeds))) - (prog1 (setf elfeed-feeds (cl-delete-duplicates full :test #'string=)) - (when (called-interactively-p 'any) - (customize-save-variable 'elfeed-feeds elfeed-feeds) - (elfeed-log 'notice "%d feeds loaded from %s" (length feeds) file))))) - -;;;###autoload -(defun elfeed-export-opml (file) - "Export the current feed listing to OPML-formatted FILE." - (interactive "FOutput OPML file: ") - (with-temp-file file - (let ((standard-output (current-buffer))) - (princ "\n") - (xml-print - `((opml ((version . "1.0")) - (head () (title () "Elfeed Export")) - (body () - ,@(cl-loop for url in (elfeed-feed-list) - for feed = (elfeed-db-get-feed url) - for title = (or (elfeed-feed-title feed) "") - collect `(outline ((xmlUrl . ,url) - (title . ,title))))))))))) - -(provide 'elfeed) - -(cl-eval-when (load eval) - ;; run-time only, so don't load when compiling other files - (unless byte-compile-root-dir - (require 'elfeed-csv) - (require 'elfeed-show) - (require 'elfeed-search))) - -;;; elfeed.el ends here blob - fd3cc2d8f7eb40da516f966ba722495f5a27b8de (mode 644) blob + /dev/null --- elpa/elfeed-3.4.1/xml-query.el +++ /dev/null @@ -1,231 +0,0 @@ -;;; xml-query.el --- query engine complimenting the xml package - -;; This is free and unencumbered software released into the public domain. - -;;; Commentary: - -;; This provides a very rudimentary, jQuery-like, XML selector -;; s-expression language. It operates on the output of the xml -;; package, such as `xml-parse-region' and `xml-parse-file'. It was -;; written to support Elfeed. - -;; See the docstring for `xml-query-all'. - -;; The macro forms, `xml-query*' and `xml-query-all*', are an order of -;; magnitude faster, but only work on static selectors and need the -;; namespaces to be pre-stripped. - -;; Examples: - -;; This query grabs the top-level paragraph content from XHTML. - -;; (xml-query-all '(html body p *) xhtml) - -;; This query extracts all the links from an Atom feed. - -;; (xml-query-all '(feed entry link [rel "alternate"] :href) xml) - -;;; Code: - -(require 'cl-lib) - -(defun xml-query-strip-ns (tag) - "Remove the namespace, if any, from TAG." - (when (symbolp tag) - (let ((name (symbol-name tag))) - (if (cl-find ?\: name) - (intern (replace-regexp-in-string "^.+:" "" name)) - tag)))) - -(defun xml-query--tag-all (match xml) - (cl-loop for (tag attribs . content) in (cl-remove-if-not #'listp xml) - when (or (eq tag match) (eq (xml-query-strip-ns tag) match)) - collect (cons tag (cons attribs content)))) - -(defun xml-query--attrib-all (attrib value xml) - (cl-loop for (tag attribs . content) in (cl-remove-if-not #'listp xml) - when (equal (cdr (assoc attrib attribs)) value) - collect (cons tag (cons attribs content)))) - -(defun xml-query--keyword (matcher xml) - (cl-loop with match = (intern (substring (symbol-name matcher) 1)) - for (tag attribs . content) in (cl-remove-if-not #'listp xml) - when (cdr (assoc match attribs)) - collect it)) - -(defun xml-query--symbol (matcher xml) - (xml-query--tag-all matcher xml)) - -(defun xml-query--vector (matcher xml) - (let ((attrib (aref matcher 0)) - (value (aref matcher 1))) - (xml-query--attrib-all attrib value xml))) - -(defun xml-query--list (matchers xml) - (cl-loop for matcher in matchers - append (xml-query-all (if (listp matcher) - matcher - (list matcher)) xml))) - -(defun xml-query--append (xml) - (cl-loop for (tag attribs . content) in (cl-remove-if-not #'listp xml) - append content)) - -(defun xml-query--stringp (thing) - "Return non-nil of THING is a non-blank string." - (and (stringp thing) (string-match "[^ \t\r\n]" thing))) - -(defun xml-query-all (query xml) - "Given a list of tags, XML, apply QUERY and return a list of -matching tags. - -A query is a list of matchers. - - SYMBOL: filters to matching tags - - LIST: each element is a full sub-query, whose results are concatenated - - VECTOR: filters to tags with matching attribute, [tag attrib value] - - KEYWORD: filters to an attribute value (must be last) - - * (an asterisk symbol): filters to content strings (must be last) - -For example, to find all the 'alternate' link URL in a typical -Atom feed: - - (xml-query-all '(feed entry link [rel \"alternate\"] :href) xml)" - (if (null query) - xml - (cl-destructuring-bind (matcher . rest) query - (cond - ((keywordp matcher) (xml-query--keyword matcher xml)) - ((eq matcher '*) - (cl-remove-if-not #'xml-query--stringp (xml-query--append xml))) - (:else - (let ((matches - (cl-etypecase matcher - (symbol (xml-query--symbol matcher xml)) - (vector (xml-query--vector matcher xml)) - (list (xml-query--list matcher xml))))) - (cond - ((null rest) matches) - ((and (or (symbolp (car rest)) - (listp (car rest))) - (not (keywordp (car rest))) - (not (eq '* (car rest)))) - (xml-query-all (cdr query) (xml-query--append matches))) - (:else (xml-query-all rest matches))))))))) - -(defun xml-query (query xml) - "Like `xml-query-all' but only return the first result." - (let ((result (xml-query-all query xml))) - (if (xml-query--stringp result) - result - (car (xml-query-all query xml))))) - -;; Macro alternatives: - -;; This is a slightly less capable alternative with significantly -;; better performance (x10 speedup) that requires a static selector. -;; The selector is compiled into Lisp code via macro at compile-time, -;; which is then carried through to byte-code by the compiler. In -;; byte-code form, the macro performs no function calls other than -;; `throw' in the case of `xml-query*', where it's invoked less than -;; once per evaluation (only on success). - -;; Queries are compiled tail-to-head with a result handler at the -;; deepest level. The generated code makes multiple bindings of the -;; variable "v" as it dives deeper into the query, using the layers of -;; bindings as a breadcrumb stack. - -;; For `xml-query*', which has a single result, the whole expression -;; is wrapped in a catch, and the first successful match is thrown to -;; it from the result handler. - -;; For `xml-query-all*', the result is pushed into an output list. - -(defun xml-query--compile-tag (tag subexp subloop-p) - `(when (and (consp v) (eq (car v) ',tag)) - ,(if subloop-p - `(dolist (v (cddr v)) - ,subexp) - subexp))) - -(defun xml-query--compile-attrib (pair subexp subloop-p) - `(let ((value (cdr (assq ',(aref pair 0) (cadr v))))) - (when (equal value ,(aref pair 1)) - ,(if subloop-p - `(dolist (v (cddr v)) - ,subexp) - subexp)))) - -(defun xml-query--compile-keyword (keyword subexp) - (let ((attrib (intern (substring (symbol-name keyword) 1)))) - `(let ((v (cdr (assq ',attrib (cadr v))))) - (when v - ,subexp)))) - -(defun xml-query--compile-star (subexp) - `(when (and (stringp v) (string-match "[^ \t\r\n]" v)) - ,subexp)) - -(defun xml-query--compile-top (query input subexp) - (let* ((rquery (reverse query)) - (prev nil)) - (while rquery - (let ((matcher (pop rquery)) - ;; Should the next item loop over its children? - (subloop-p (and (not (null prev)) - (not (keywordp prev)) - (symbolp prev)))) - (cond - ((eq '* matcher) - (setf subexp (xml-query--compile-star subexp))) - ((keywordp matcher) - (setf subexp (xml-query--compile-keyword matcher subexp))) - ((symbolp matcher) - (setf subexp (xml-query--compile-tag matcher subexp subloop-p))) - ((vectorp matcher) - (setf subexp (xml-query--compile-attrib matcher subexp subloop-p))) - ((error "Bad query: %S" query))) - (setf prev matcher))) - `(dolist (v ,input) - ,subexp))) - -(defun xml-query--compile (query input) - (let ((tag (make-symbol "done"))) - `(catch ',tag - ,(xml-query--compile-top query input `(throw ',tag v))))) - -(defmacro xml-query* (query sexp) - "Like `xml-query' but generate code to execute QUERY on SEXP. - -Unlike `xml-query', QUERY must be a static, compile-time -s-expression. See `xml-query-all*' for more information. - -QUERY is *not* evaluated, so it should not be quoted." - (xml-query--compile query sexp)) - -(defun xml-query-all--compile (query input) - (let ((output (make-symbol "output"))) - `(let ((,output ())) - ,(xml-query--compile-top query input `(push v ,output)) - (nreverse ,output)))) - -(defmacro xml-query-all* (query sexp) - "Like `xml-query-all' but generate code to execute QUERY on SEXP. - -Unlike `xml-query-all', QUERY must be a static, compile-time -s-expression. This macro compiles the query into actual code. The -result is faster since the query will be compiled into byte-code -rather than \"interpreted\" at run time. - -Also unlike `xml-query-all', the parsed XML s-expression must -also have its namespace pre-stripped. This is accomplished by -setting the optional PARSE-NS argument of `xml-parse-region' to -symbol-qnames. - -Sub-expression lists are not supported by this macro. - -QUERY is *not* evaluated, so it should not be quoted." - (xml-query-all--compile query sexp)) - -(provide 'xml-query) - -;;; xml-query.el ends here blob - d46ea5886a85842ce7845f21e08b1ff5803852a5 (mode 644) blob + /dev/null --- elpa/elfeed-tube-0.15/elfeed-tube-autoloads.el +++ /dev/null @@ -1,41 +0,0 @@ -;;; elfeed-tube-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from elfeed-tube.el - - (autoload 'elfeed-tube-fetch "elfeed-tube" "Fetch youtube metadata for Youtube video or Elfeed entry ENTRIES." t nil) -(register-definition-prefixes "elfeed-tube" '("elfeed-tube-")) - - -;;; Generated autoloads from elfeed-tube-fill.el - - (autoload 'elfeed-tube-fill-feeds "elfeed-tube-utils" "Fetch and add all channel videos for ENTRIES' feeds." t nil) -(register-definition-prefixes "elfeed-tube-fill" '("elfeed-tube--")) - - -;;; Generated autoloads from elfeed-tube-utils.el - - (autoload 'elfeed-tube-add-feeds "elfeed-tube-utils" "Add youtube feeds to the Elfeed database by QUERIES." t nil) -(register-definition-prefixes "elfeed-tube-utils" '("elfeed-tube-")) - -;;; End of scraped data - -(provide 'elfeed-tube-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; elfeed-tube-autoloads.el ends here blob - 76f52c88385988c96da0a62412669fc1222465ab (mode 644) blob + /dev/null --- elpa/elfeed-tube-0.15/elfeed-tube-fill.el +++ /dev/null @@ -1,353 +0,0 @@ -;;; elfeed-tube-fill.el --- Back-fill elfeed-tube feeds -*- lexical-binding: t; -*- - -;; Copyright (C) 2022 Karthik Chikmagalur - -;; Author: Karthik Chikmagalur -;; Keywords: multimedia, convenience - -;; SPDX-License-Identifier: UNLICENSE - -;; This file is NOT part of GNU Emacs. - -;;; Commentary: -;; -;; This file contains commands to back-fill Elfeed YouTube feeds. Back-filling a -;; feed fetches all historical entries for the corresponding YouTube channel or -;; playlist and adds them to the Elfeed database. Youtube RSS feeds generally -;; contain only the latest 15 entries. -;; -;; Call `elfeed-tube-fill-feeds' in an Elfeed search or entry buffer to -;; back-fill entries for the corresponding feed. You can select a region of -;; entries to fill all the corresponding feeds. -;; -;;; Code: - -(require 'elfeed-tube) - -(declare-function elfeed-tube--get-entries "elfeed-tube") -(defvar elfeed-tube--api-channels-videos-path "/api/v1/channels/videos/%s") -(defvar elfeed-tube--api-playlists-videos-path "/api/v1/playlists/%s") -(defvar elfeed-tube--fill-tags nil - "Alist of Elfeed feed-ids and tags to add. - -These tags (list of symbols) will be added when back-filling the -corresponding feed.") - - -(cl-deftype elfeed-tube--fill-api-data () - `(satisfies - (lambda (coll) - (and (vectorp coll) - (or (= coll 0) - (cl-every (lambda (vd) (and (plist-get vd :videoId) - (plist-get vd :published) - (plist-get vd :title))) - coll)))))) - -;;;###autoload (autoload 'elfeed-tube-fill-feeds "elfeed-tube-utils" "Fetch and add all channel videos for ENTRIES' feeds." t nil) -(aio-defun elfeed-tube-fill-feeds (entries &optional interactive-p) - "Fetch and add all channel videos for ENTRIES' feeds. - -YouTube RSS feeds generally contain only the latest 15 entries. -Use this command to fetch and add to Elfeed all videos -corresponding a channel or playlist. - -ENTRIES is the entry at point or visited entry, or the list of -selected entries if the region is active. - -When called interactively, INTERACTIVE-P is t and a summary -window will be shown before taking any action." - (interactive (list (elfeed-tube--ensure-list (elfeed-tube--get-entries)) - t)) - (let ((feeds (cl-reduce - (lambda (accum entry) - (if-let* ((feed (elfeed-entry-feed entry)) - ((memq feed accum))) - accum - (cons feed accum))) - entries - :initial-value nil))) - (if interactive-p - (elfeed-tube--fill-display-feeds feeds) - (aio-await (elfeed-tube--fill-feeds feeds))))) - -(aio-defun elfeed-tube--fill-feeds (feeds) - "Find videos corresponding to the channels/playlists for Elfeed feeds FEEDS. - -Videos not already present will be added to the Elfeed database." - (cl-check-type feeds (and (not null) (not atom))) - (cl-check-type (car feeds) elfeed-feed) - - (dolist (feed feeds) - (elfeed-tube-log 'debug "[(fill-feeds): Backfilling feed: %s]" (elfeed-feed-title feed)) - (let ((elfeed-tube-auto-fetch-p nil) - (feed-url (elfeed-feed-url feed)) - (feed-id (elfeed-feed-id feed)) - (feed-title (elfeed-feed-title feed)) - (add-count) - (feed-entries-to-add - (thread-first - (elfeed-tube--fill-feed feed) - (aio-await) - (cl-delete-duplicates :key (lambda (x) (plist-get x :videoId)) :test #'string=) - (vconcat) - (elfeed-tube--fill-feed-dates) - (aio-await)))) - - (cl-check-type feed-entries-to-add elfeed-tube--fill-api-data - "Missing video attributes (ID, Title or Publish Date).") - - (if (= (length feed-entries-to-add) 0) - (message "Nothing to retrieve for feed \"%s\" (%s)" feed-title feed-url) - (setq add-count (length feed-entries-to-add)) - - (condition-case error - (thread-last - feed-entries-to-add - (cl-map 'list (apply-partially #'elfeed-tube--entry-create feed-id)) - (cl-map 'list (lambda (entry) - (setf (elfeed-entry-tags entry) - (or (alist-get feed-id elfeed-tube--fill-tags - nil nil #'equal) - '(unread))) - entry)) - (elfeed-db-add)) - (error (elfeed-handle-parse-error feed-url error))) - ;; (prin1 feed-entries-to-add (get-buffer "*scratch*")) - (elfeed-tube-log 'debug "[(elfeed-db): Backfilling feed: %s][Added %d videos]" - feed-title add-count) - (message "Retrieved %d missing videos for feed \"%s\" (%s)" - add-count feed-title feed-url) - (run-hook-with-args 'elfeed-update-hooks feed-url))))) - -;; feed: elfeed-feed struct, page: int or nil -> vector(plist entries for feed videos not in db) -(aio-defun elfeed-tube--fill-feed (feed &optional page) - "Find videos corresponding to the channel/playlist for Elfeed feed FEED. - -Return video metadata as a vector of plists. Metadata -corresponding to videos already in the Elfeed database are -filtered out. - -PAGE corresponds to the page number of results requested from the API." - (cl-check-type feed elfeed-feed "An Elfeed Feed") - (cl-check-type page (or null (integer 0 *)) "A positive integer.") - - (if-let* ((page (or page 1)) - (feed-url (elfeed-feed-url feed)) - (feed-title (elfeed-feed-title feed)) - (api-path (cond ((string-match "playlist_id=\\(.*?\\)/*$" feed-url) - (concat - (format elfeed-tube--api-playlists-videos-path - (match-string 1 feed-url)) - "?fields=videos(title,videoId,author)" - "&page=" (number-to-string (or page 1)))) - ((string-match "channel_id=\\(.*?\\)/*$" feed-url) - (concat - (format elfeed-tube--api-channels-videos-path - (match-string 1 feed-url)) - "?fields=" - "title,videoId,author,published" - "&sort_by=newest" - "&page=" (number-to-string (or page 1)))) - (t (elfeed-tube-log 'error "[Malformed/Not YouTube feed: %s][%s]" - feed-title feed-url) - nil))) - (feed-type (cond ((string-match "playlist_id=\\(.*?\\)/*$" feed-url) 'playlist) - ((string-match "channel_id=\\(.*?\\)/*$" feed-url) 'channel)))) - (let ((feed-entry-video-ids - (mapcar (lambda (e) (elfeed-tube--url-video-id (elfeed-entry-link e))) - (elfeed-feed-entries feed))) - (feed-id (elfeed-feed-id feed))) - (if-let* - ((api-data - (aio-await - (elfeed-tube--aio-fetch - (concat (aio-await (elfeed-tube--get-invidious-url)) api-path) - #'elfeed-tube--nrotate-invidious-servers))) - (api-data (pcase feed-type - ('channel api-data) - ('playlist - (cl-check-type api-data (and (not null) list)) - (plist-get api-data :videos)))) - ((> (length api-data) 0))) - (progn - (cl-check-type api-data elfeed-tube--fill-api-data) - (elfeed-tube-log 'debug "[Backfilling: page %d][Fetched: %d entries]" - (or page 1) (length api-data)) - (vconcat - (cl-delete-if ;remove entries already in db - (lambda (elt) (member (plist-get elt :videoId) feed-entry-video-ids)) - api-data) - (aio-await (elfeed-tube--fill-feed feed (1+ page))))) - (make-vector 0 0))) - (elfeed-tube-log 'error "[Malformed/Not Youtube feed: %s][%s]" feed-title feed-url))) - -;; api-data: vector(plist entries for feed videos) -> vector(plist entries for -;; feed videos with correct dates.) -(aio-defun elfeed-tube--fill-feed-dates (api-data) - "Add or correct dates for videos in API-DATA. - -API-DATA is a vector of plists, one per video. This function -returns a vector of plists with video publish dates -corrected/added as the value of the plist's :published key." - (cl-check-type api-data elfeed-tube--fill-api-data) - (let ((date-queries) - (feed-videos-map (make-hash-table :test 'equal)) - (fix-count 0)) - - (if (= (length api-data) 0) - api-data - (progn - (elfeed-tube-log 'debug "[Fixing publish dates]") - (cl-loop for video-plist across api-data - for video-id = (plist-get video-plist :videoId) - do (puthash video-id video-plist feed-videos-map) - do (push (elfeed-tube--with-label - video-id #'elfeed-tube--aio-fetch - (concat (aio-wait-for (elfeed-tube--get-invidious-url)) - elfeed-tube--api-videos-path - video-id "?fields=published")) - date-queries)) - - (dolist (promise (nreverse date-queries)) - (pcase-let* ((`(,video-id . ,corrected-date) (aio-await promise)) - (video-plist (gethash video-id feed-videos-map))) - - (plist-put video-plist :published (plist-get corrected-date :published)) - (cl-incf fix-count))) - - (elfeed-tube-log 'debug "[Fixed publish dates for %d videos]" fix-count) - - (vconcat (hash-table-values feed-videos-map)))))) - -;; Back-fill GUI - -(defsubst elfeed-tube--fill-tags-strings (taglist) - "Convert a list of tags TAGLIST to a comma separated string." - (mapconcat - (lambda (s) (propertize (symbol-name s) - 'face 'elfeed-search-tag-face)) - taglist ",")) - -(defun elfeed-tube--fill-display-feeds (feeds) - "Produce a summary of Elfeed FEEDS to be back-filled. - -Back-filling a YouTube feed will fetch all its videos not -presently available in its RSS feed or in the Elfeed database." - (let ((buffer (get-buffer-create "*Elfeed-Tube Channels*"))) - (with-current-buffer buffer - (let ((inhibit-read-only t)) (erase-buffer)) - (elfeed-tube-channels-mode) - - (setq tabulated-list-use-header-line t ; default to no header - header-line-format nil - ;; tabulated-list--header-string nil - tabulated-list-format - '[("Channel" 22 t) - ("#Entries" 10 t) - ("Tags to apply" 30 nil) - ("Feed URL" 30 nil)]) - - (setq - tabulated-list-entries - (cl-loop for feed in feeds - for n upfrom 1 - for feed-url = (elfeed-feed-url feed) - for channel-id = (progn (string-match "=\\(.*?\\)$" feed-url) - (match-string 1 feed-url)) - for feed-title = (list (propertize (elfeed-feed-title feed) - 'feed feed) - 'mouse-face 'highlight - 'action - #'elfeed-tube-add--visit-channel - 'follow-link t - 'help-echo - (or (and channel-id - (concat - "https://www.youtube.com/channel/" - channel-id)) - "")) - for feed-count = (number-to-string (length (elfeed-feed-entries feed))) - for feed-tags = (if-let ((taglist - (alist-get (elfeed-feed-id feed) - elfeed-tube--fill-tags nil t #'equal))) - (elfeed-tube--fill-tags-strings taglist) - (propertize "unread" 'face 'elfeed-search-tag-face)) - collect - `(,n - [,feed-title - ,feed-count - ,feed-tags - ,feed-url]))) - - (tabulated-list-init-header) - (tabulated-list-print) - (goto-address-mode 1) - - (goto-char (point-max)) - (let ((inhibit-read-only t) - (continue (propertize "C-c C-c" 'face 'help-key-binding)) - (cancel-q (propertize "q" 'face 'help-key-binding)) - (cancel (propertize "C-c C-k" 'face 'help-key-binding))) - - (let ((inhibit-message t)) (toggle-truncate-lines 1)) - (insert "\n") - (insert - " " (propertize "t" 'face 'help-key-binding) - " or " (propertize "+" 'face 'help-key-binding) - ": Set tags to apply to back-filled entries for feed.\n\n" - " " continue ": Add All (historical) videos from these channels to Elfeed.\n" - cancel-q " or " cancel ": Quit and cancel this operation.\n")) - - (goto-char (point-min)) - - (use-local-map (copy-keymap elfeed-tube-channels-mode-map)) - (local-set-key (kbd "C-c C-c") #'elfeed-tube--fill-confirm) - (local-set-key (kbd "+") #'elfeed-tube--fill-tags-add) - (local-set-key (kbd "t") #'elfeed-tube--fill-tags-add) - - (display-buffer - buffer `(nil - (window-height . ,#'fit-window-to-buffer) - (body-function . ,#'select-window)))))) - -(defun elfeed-tube--fill-tags-add () - "Add tags to back-filled entries fetched for feed at point." - (interactive) - (when-let* ((entry (tabulated-list-get-entry)) - (feed (thread-last (aref entry 0) - (car) - (get-text-property 0 'feed))) - (title (elfeed-feed-title feed)) - (id (elfeed-feed-id feed)) - (tags (read-from-minibuffer - (format "Add tags for \"%s\" (comma separated): " title) - (thread-last - (or (alist-get id elfeed-tube--fill-tags nil t #'equal) '(unread)) - (mapcar #'symbol-name) - (funcall (lambda (tg) (string-join tg ",")))))) - (taglist (thread-last (split-string tags "," t "[ \f\t\n\r\v]+") - (mapcar #'intern-soft) - (elfeed-normalize-tags)))) - (setf (alist-get (elfeed-feed-id feed) elfeed-tube--fill-tags nil nil #'equal) - taglist) - (tabulated-list-set-col 2 (elfeed-tube--fill-tags-strings taglist)))) - -(aio-defun elfeed-tube--fill-confirm () - "Back-fill video entries for the displayed Elfeed feeds." - (interactive) - (cl-assert (derived-mode-p 'elfeed-tube-channels-mode)) - (cl-loop for table-entry in tabulated-list-entries - for feed-title = (car (aref (cadr table-entry) 0)) - collect (get-text-property 0 'feed feed-title) into feeds - finally do (elfeed-tube-log 'debug "[(fill-confirm-feeds): %S]" - (mapcar #'elfeed-feed-title feeds)) - finally do - (progn - (quit-window 'kill-buffer) - (message "Backfilling YouTube feeds...") - (aio-await (elfeed-tube--fill-feeds feeds)) - (message "Backfilling Youtube feeds... done.")))) - -(provide 'elfeed-tube-fill) -;;; elfeed-tube-fill.el ends here blob - 8168e876f3dedaa32217098f0c67a6668766669c (mode 644) blob + /dev/null --- elpa/elfeed-tube-0.15/elfeed-tube-pkg.el +++ /dev/null @@ -1,16 +0,0 @@ -(define-package "elfeed-tube" "0.15" "YouTube integration for Elfeed" - '((emacs "27.1") - (elfeed "3.4.1") - (aio "1.0")) - :commit "7e1409e41628d61d8197ca248d910182ae4fc520" :authors - '(("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) - :maintainers - '(("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) - :maintainer - '("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com") - :keywords - '("news" "hypermedia" "convenience") - :url "https://github.com/karthink/elfeed-tube") -;; Local Variables: -;; no-byte-compile: t -;; End: blob - 076537923041648abbe1cf555502304a400febce (mode 644) blob + /dev/null --- elpa/elfeed-tube-0.15/elfeed-tube-utils.el +++ /dev/null @@ -1,502 +0,0 @@ -;;; elfeed-tube-utils.el --- utilities for elfeed-tube -*- lexical-binding: t; -*- - -;; Copyright (C) 2022 Karthik Chikmagalur - -;; Author: Karthik Chikmagalur -;; Keywords: multimedia, convenience - -;; SPDX-License-Identifier: UNLICENSE - -;; This file is NOT part of GNU Emacs. - -;;; Commentary: -;; -;; Utilities for Elfeed Tube. -;; -;;; Code: -(require 'rx) -(require 'aio) -(require 'elfeed) - -(declare-function elfeed-tube--with-label "elfeed-tube") -(declare-function elfeed-tube--fetch-1 "elfeed-tube") -(declare-function elfeed-tube-show "elfeed-tube") -(declare-function elfeed-tube-curl-enqueue "elfeed-tube") -(declare-function elfeed-tube--attempt-log "elfeed-tube") -(declare-function elfeed-tube-log "elfeed-tube") -(declare-function elfeed-tube--get-invidious-url "elfeed-tube") -(declare-function elfeed-tube--nrotate-invidious-servers "elfeed-tube") - -(defvar elfeed-tube-youtube-regexp) -(defvar elfeed-tube--api-videos-path) -(defvar elfeed-tube--max-retries) - -(defsubst elfeed-tube--ensure-list (var) - "Ensure VAR is a list." - (if (listp var) var (list var))) - -(cl-defstruct (elfeed-tube-channel (:constructor elfeed-tube-channel-create) - (:copier nil)) - "Struct to hold youtube channel information." - query author url feed) - -;;;###autoload (autoload 'elfeed-tube-add-feeds "elfeed-tube-utils" "Add youtube feeds to the Elfeed database by QUERIES." t nil) -(aio-defun elfeed-tube-add-feeds (queries &optional _) - "Add youtube feeds to the Elfeed database by QUERIES. - -Each query can be a video, playlist or channel URL and the -corresponding channel feed will be selected. It can also be a -search term and the best match will be found. You will be asked -to finalize the results before committing them to Elfeed. - -When called interactively, multiple queries can be provided by -separating them with the `crm-separator', typically -comma (\",\"). Search terms cannot include the `crm-separator'. - -When called noninteractively, it accepts a query or a list of -queries." - (interactive - (list (completing-read-multiple - "Video, Channel, Playlist URLs or search queries: " - #'ignore) - current-prefix-arg)) - (message "Finding RSS feeds, hold tight!") - (let ((channels (aio-await (elfeed-tube-add--get-channels queries)))) - (elfeed-tube-add--display-channels channels))) - -(defsubst elfeed-tube--video-p (cand) - "Check if CAND is a Youtube video URL." - (string-match - (concat - elfeed-tube-youtube-regexp - (rx (zero-or-one "watch?v=") - (group (1+ (not "&"))))) - cand)) - -(defsubst elfeed-tube--playlist-p (cand) - "Check if CAND is a Youtube playlist URL." - (string-match - (concat - elfeed-tube-youtube-regexp - "playlist\\?list=" - (rx (group (1+ (not "&"))))) - cand)) - -(defsubst elfeed-tube--channel-p (cand) - "Check if CAND is a Youtube channel URL." - (string-match - (concat - elfeed-tube-youtube-regexp - (rx "channel/" - (group (1+ (not "&"))))) - cand)) - -(aio-defun elfeed-tube-add--get-channels (queries) - (let* ((fetches (aio-make-select)) - (queries (elfeed-tube--ensure-list queries)) - (playlist-base-url - "https://www.youtube.com/feeds/videos.xml?playlist_id=") - (channel-base-url - "https://www.youtube.com/feeds/videos.xml?channel_id=") - channels) - - ;; Add all promises to fetches, an aio-select - (dolist (q queries channels) - (setq q (string-trim q)) - (cond - ((elfeed-tube--channel-p q) - (let* ((chan-id (match-string 1 q)) - (api-url (concat (aio-await (elfeed-tube--get-invidious-url)) - "/api/v1/channels/" - chan-id - "?fields=author,authorUrl")) - (feed (concat channel-base-url chan-id))) - (aio-select-add fetches - (elfeed-tube--with-label - `(:type channel :feed ,feed :query ,q) - #'elfeed-tube--aio-fetch - api-url #'elfeed-tube--nrotate-invidious-servers)))) - - ((string-match - (concat elfeed-tube-youtube-regexp "c/" "\\([^?&]+\\)") q) - ;; Interpret channel url as search query - (let* ((search-url "/api/v1/search") - (api-url (concat (aio-await (elfeed-tube--get-invidious-url)) - search-url - "?q=" (url-hexify-string (match-string 1 q)) - "&type=channel&page=1"))) - (aio-select-add fetches - (elfeed-tube--with-label - `(:type search :query ,q) - #'elfeed-tube--aio-fetch - api-url #'elfeed-tube--nrotate-invidious-servers)))) - - ((elfeed-tube--playlist-p q) - (let* ((playlist-id (match-string 1 q)) - (api-url (concat (aio-await (elfeed-tube--get-invidious-url)) - "/api/v1/playlists/" - playlist-id - "?fields=title,author")) - (feed (concat playlist-base-url playlist-id))) - (aio-select-add fetches - (elfeed-tube--with-label - `(:type playlist :feed ,feed :query ,q) - #'elfeed-tube--aio-fetch - api-url #'elfeed-tube--nrotate-invidious-servers)))) - - ((elfeed-tube--video-p q) - (if-let* ((video-id (match-string 1 q)) - (videos-url "/api/v1/videos/") - (api-url (concat (aio-await (elfeed-tube--get-invidious-url)) - videos-url - video-id - "?fields=author,authorUrl,authorId"))) - (aio-select-add fetches - (elfeed-tube--with-label - `(:type video :query ,q) - #'elfeed-tube--aio-fetch - api-url #'elfeed-tube--nrotate-invidious-servers)) - (push (elfeed-tube-channel-create :query q) - channels))) - - (t ;interpret as search query - (let* ((search-url "/api/v1/search") - (api-url (concat (aio-await (elfeed-tube--get-invidious-url)) - search-url - "?q=" (url-hexify-string q) - "&type=channel&page=1"))) - (aio-select-add fetches - (elfeed-tube--with-label - `(:type search :query ,q) - #'elfeed-tube--aio-fetch - api-url #'elfeed-tube--nrotate-invidious-servers)))))) - - ;; Resolve all promises in the aio-select - (while (aio-select-promises fetches) - (pcase-let* ((`(,label . ,data) - (aio-await (aio-await (aio-select fetches)))) - (q (plist-get label :query)) - (feed (plist-get label :feed))) - (pcase (plist-get label :type) - ('channel - (if-let ((author (plist-get data :author)) - (author-url (plist-get data :authorUrl))) - (push (elfeed-tube-channel-create - :query q :author author - :url q - :feed feed) - channels) - (push (elfeed-tube-channel-create :query q :feed feed) - channels))) - - ('playlist - (if-let ((title (plist-get data :title)) - (author (plist-get data :author))) - (push (elfeed-tube-channel-create - :query q :author title :url q - :feed feed) - channels) - (push (elfeed-tube-channel-create - :query q :url q - :feed feed) - channels))) - ('video - (if-let* ((author (plist-get data :author)) - (author-id (plist-get data :authorId)) - (author-url (plist-get data :authorUrl)) - (feed (concat channel-base-url author-id))) - (push (elfeed-tube-channel-create - :query q :author author - :url (concat "https://www.youtube.com" author-url) - :feed feed) - channels) - (push (elfeed-tube-channel-create :query (plist-get label :query)) - channels))) - ('search - (if-let* ((chan-1 (and (> (length data) 0) - (aref data 0))) - (author (plist-get chan-1 :author)) - (author-id (plist-get chan-1 :authorId)) - (author-url (plist-get chan-1 :authorUrl)) - (feed (concat channel-base-url author-id))) - (push (elfeed-tube-channel-create - :query q :author author - :url (concat "https://www.youtube.com" author-url) - :feed feed) - channels) - (push (elfeed-tube-channel-create :query q) - channels)))))) - - (nreverse channels))) - -(defun elfeed-tube-add--display-channels (channels) - "Summarize found Youtube channel feeds CHANNELS." - (let ((buffer (get-buffer-create "*Elfeed-Tube Channels*")) - (notfound (propertize "Not found!" 'face 'error))) - (with-current-buffer buffer - (let ((inhibit-read-only t)) (erase-buffer)) - (elfeed-tube-channels-mode) - (setq - tabulated-list-entries - (cl-loop for channel in channels - for n upfrom 1 - for author = (if-let ((url (elfeed-tube-channel-url channel))) - (list (elfeed-tube-channel-author channel) - 'mouse-face 'highlight - 'action - #'elfeed-tube-add--visit-channel - 'follow-link t - 'help-echo (elfeed-tube-channel-url channel)) - notfound) - for feed = (or (elfeed-tube-channel-feed channel) notfound) - collect - `(,n - [,author - ,(replace-regexp-in-string - elfeed-tube-youtube-regexp "" - (elfeed-tube-channel-query channel)) - ,feed]))) - (setq tabulated-list-format - '[("Channel" 22 t) - ("Query" 32 t) - ("Feed URL" 30 nil)]) - - (tabulated-list-init-header) - (tabulated-list-print) - (goto-address-mode 1) - - (goto-char (point-max)) - - (let ((inhibit-read-only t) - (fails (cl-reduce - (lambda (sum ch) - (+ sum - (or (and (elfeed-tube-channel-feed ch) 0) 1))) - channels :initial-value 0)) - (continue (propertize "C-c C-c" 'face 'help-key-binding)) - (continue-extra (propertize "C-u C-c C-c" 'face 'help-key-binding)) - (cancel-q (propertize "q" 'face 'help-key-binding)) - (cancel (propertize "C-c C-k" 'face 'help-key-binding)) - (copy (propertize "C-c C-w" 'face 'help-key-binding))) - - (let ((inhibit-message t)) - (toggle-truncate-lines 1)) - (insert "\n") - (when (> fails 0) - (insert (propertize - (format "%d queries could not be resolved.\n\n" fails) - 'face 'error) - " " continue ": Add found feeds to the Elfeed database, ignoring the failures.\n" - " " continue-extra ": Add found feeds, fetch entries from them and open Elfeed.\n")) - (when (= fails 0) - (insert - (propertize - "All queries resolved successfully.\n\n" - 'face 'success) - " " continue ": Add all feeds to the Elfeed database.\n" - " " continue-extra ": Add all feeds, fetch entries from them and open Elfeed.\n" - " " copy ": Copy the list of feed URLs as a list\n")) - (insert "\n" cancel-q " or " cancel ": Quit and cancel this operation.")) - - (goto-char (point-min)) - - (use-local-map (copy-keymap elfeed-tube-channels-mode-map)) - (local-set-key (kbd "C-c C-c") #'elfeed-tube-add--confirm) - (local-set-key (kbd "C-c C-w") #'elfeed-tube-add--copy) - - (funcall - (if (bound-and-true-p demo-mode) - #'switch-to-buffer - #'display-buffer) - buffer)))) - -(defun elfeed-tube-add--visit-channel (button) - "Activate BUTTON." - (browse-url (button-get button 'help-echo))) - -;; (elfeed-tube-add--display-channels my-channels) - -(defun elfeed-tube-add--confirm (&optional arg) - "Confirm the addition of visible Youtube feeds to the Elfeed database. - -With optional prefix argument ARG, update these feeds and open Elfeed -afterwards." - (interactive "P") - (cl-assert (derived-mode-p 'elfeed-tube-channels-mode)) - (let* ((channels tabulated-list-entries)) - (let ((inhibit-message t)) - (cl-loop for channel in channels - for (_ _ feed) = (append (cadr channel) nil) - do (elfeed-add-feed feed :save t))) - (message "Added to elfeed-feeds.") - (when arg (elfeed)))) - -(defvar elfeed-tube-channels-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-k") (lambda () (interactive) (quit-window 'kill-buffer))) - map)) - -(define-derived-mode elfeed-tube-channels-mode tabulated-list-mode - "Elfeed Tube Channels" - (setq tabulated-list-use-header-line t ; default to no header - ;; tabulated-list--header-string nil - header-line-format nil)) - -(defun elfeed-tube-add--copy () - "Copy visible Youtube feeds to the kill ring as a list. - -With optional prefix argument ARG, update these feeds and open Elfeed -afterwards." - (interactive) - (cl-assert (derived-mode-p 'elfeed-tube-channels-mode)) - (let* ((channels tabulated-list-entries)) - (cl-loop for channel in channels - for (_ _ feed) = (append (cadr channel) nil) - collect feed into feeds - finally (kill-new (prin1-to-string feeds))) - (message "Feed URLs saved to kill-ring."))) - -(aio-defun elfeed-tube--aio-fetch (url &optional next desc attempts) - "Fetch URL asynchronously using `elfeed-curl-retrieve'. - -If successful (HTTP 200), return the JSON-parsed result as a -plist. - -Otherwise, call the function NEXT (with no arguments) and try -ATTEMPTS more times. Return nil if all attempts fail. DESC is a -description string to print to the elfeed-tube log allong with -any other error messages. - -This function returns a promise." - (let ((attempts (or attempts (1+ elfeed-tube--max-retries)))) - (when (> attempts 0) - (let* ((response - (aio-await (elfeed-tube-curl-enqueue url :method "GET"))) - (content (plist-get response :content)) - (status (plist-get response :status-code)) - (error-msg (plist-get response :error-message))) - (cond - ((= status 200) - (condition-case nil - (json-parse-string content :object-type 'plist) - ((json-parse-error error) - (elfeed-tube-log 'error "[Search] JSON malformed (%s)" - (elfeed-tube--attempt-log attempts)) - (and (functionp next) (funcall next)) - (aio-await - (elfeed-tube--aio-fetch url next desc (1- attempts)))))) - (t (elfeed-tube-log 'error "[Search][%s]: %s (%s)" error-msg url - (elfeed-tube--attempt-log attempts)) - (and (functionp next) (funcall next)) - (aio-await - (elfeed-tube--aio-fetch url next desc (1- attempts))))))))) - -(defun elfeed-tube--entry-create (feed-id entry-data) - "Create an Elfeed entry from ENTRY-DATA for feed with id FEED-ID. - -FEED-ID is the id of the feed in the Elfeed database. ENTRY-DATA -is a plist of video metadata." - (cl-assert (listp entry-data)) - (cl-assert (plist-get entry-data :videoId)) - - (let* ((video-id (plist-get entry-data :videoId)) - (link (format "https://www.youtube.com/watch?v=%s" video-id)) - (title (plist-get entry-data :title)) - (published (plist-get entry-data :published)) - (author `((:name ,(plist-get entry-data :author) - :uri ,feed-id)))) - (elfeed-entry--create - :link link - :title title - :id `("www.youtube.com" . ,(concat "yt:video:" video-id)) - :date published - :tags '(unread) - :content-type 'html - :meta `(:authors ,author) - :feed-id feed-id))) - -(aio-defun elfeed-tube--fake-entry (url &optional force-fetch) - (string-match (concat elfeed-tube-youtube-regexp - (rx (zero-or-one "watch?v=") - (group (1+ (not (or "&" "?")))))) - url) - (if-let ((video-id (match-string 1 url))) - (progn - (message "Creating a video summary...") - (cl-letf* ((elfeed-show-unique-buffers t) - (elfeed-show-entry-switch #'display-buffer) - (elfeed-tube-save-indicator nil) - (elfeed-tube-auto-save-p nil) - (api-data (aio-await - (elfeed-tube--aio-fetch - (concat (aio-await (elfeed-tube--get-invidious-url)) - elfeed-tube--api-videos-path - video-id - "?fields=" - ;; "videoThumbnails,descriptionHtml,lengthSeconds," - "title,author,authorUrl,published,videoId") - #'elfeed-tube--nrotate-invidious-servers))) - (feed-id (concat "https://www.youtube.com/feeds/videos.xml?channel_id=" - (nth 1 (split-string (plist-get api-data :authorUrl) - "/" t)))) - (author `((:name ,(plist-get api-data :author) - :uri ,feed-id))) - (entry (elfeed-tube--entry-create feed-id api-data)) - ((symbol-function 'elfeed-entry-feed) - (lambda (_) - (elfeed-feed--create - :id feed-id - :url feed-id - :title (plist-get api-data :author) - :author author)))) - (aio-await (elfeed-tube--fetch-1 entry force-fetch)) - (with-selected-window (elfeed-show-entry entry) - (message "Summary created for video: \"%s\"" - (elfeed-entry-title entry)) - (setq-local elfeed-show-refresh-function - (lambda () (interactive) - (elfeed-tube-show)) - elfeed-tube-save-indicator nil)))) - (message "Not a youtube video URL, aborting."))) - -(defsubst elfeed-tube--line-at-point () - "Get line around point." - (buffer-substring (line-beginning-position) (line-end-position))) - -(defun elfeed-tube-next-heading (&optional arg) - "Jump to the next heading in an Elfeed entry. - -With numeric prefix argument ARG, jump forward that many times. -If ARG is negative, jump backwards instead." - (interactive "p") - (unless arg (setq arg 1)) - (catch 'return - (dotimes (_ (abs arg)) - (when (> arg 0) (end-of-line)) - (if-let ((match - (funcall (if (> arg 0) - #'text-property-search-forward - #'text-property-search-backward) - 'face `(shr-h1 shr-h2 shr-h3 - message-header-name elfeed-tube-chapter-face) - (lambda (tags face) - (cl-loop for x in (if (consp face) face (list face)) - thereis (memq x tags))) - t))) - (goto-char - (if (> arg 0) (prop-match-beginning match) (prop-match-end match))) - (throw 'return nil)) - (when (< arg 0) (beginning-of-line))) - (beginning-of-line) - (point))) - -(defun elfeed-tube-prev-heading (&optional arg) - "Jump to the previous heading in an Elfeed entry. - -With numeric prefix argument ARG, jump backward that many times. -If ARG is negative, jump forward instead." - (interactive "p") - (elfeed-tube-next-heading (- (or arg 1)))) - -(provide 'elfeed-tube-utils) -;;; elfeed-tube-utils.el ends here blob - 0ec63091a27b9c74969d4ed1a2cce7eb8035ff58 (mode 644) blob + /dev/null --- elpa/elfeed-tube-0.15/elfeed-tube.el +++ /dev/null @@ -1,1175 +0,0 @@ -;;; elfeed-tube.el --- YouTube integration for Elfeed -*- lexical-binding: t; -*- - -;; Copyright (C) 2022 Karthik Chikmagalur - -;; Author: Karthik Chikmagalur -;; Version: 0.15 -;; Package-Requires: ((emacs "27.1") (elfeed "3.4.1") (aio "1.0")) -;; Keywords: news, hypermedia, convenience -;; URL: https://github.com/karthink/elfeed-tube - -;; SPDX-License-Identifier: UNLICENSE - -;; This file is NOT part of GNU Emacs. - -;;; Commentary: -;; -;; Elfeed Tube is an extension for Elfeed, the feed reader for Emacs, that -;; enhances your Youtube RSS feed subscriptions. -;; -;; Typically Youtube RSS feeds contain only the title and author of each video. -;; Elfeed Tube adds video descriptions, thumbnails, durations, chapters and -;; "live" transcrips to video entries. See -;; https://github.com/karthink/elfeed-tube for demos. This information can -;; optionally be added to your entry in your Elfeed database. -;; -;; The displayed transcripts and chapter headings are time-aware, so you can -;; click on any transcript segment to visit the video at that time (in a browser -;; or your video player if you also have youtube-dl). A companion package, -;; `elfeed-tube-mpv', provides complete mpv (video player) integration with the -;; transcript, including video seeking through the transcript and following -;; along with the video in Emacs. -;; -;; To use this package, -;; -;; (i) Subscribe to Youtube channel or playlist feeds in Elfeed. You can use the -;; helper function `elfeed-tube-add-feeds' provided by this package to search for -;; Youtube channels by URLs or search queries. -;; -;; (ii) Place in your init file the following: -;; -;; (require 'elfeed-tube) -;; (elfeed-tube-setup) -;; -;; (iii) Use Elfeed as normal, typically with `elfeed'. Your Youtube feed -;; entries should be fully populated. -;; -;; You can also call `elfeed-tube-fetch' in an Elfeed buffer to manually -;; populate an entry, or obtain an Elfeed entry-like summary for ANY youtube -;; video (no subscription needed) by manually calling `elfeed-tube-fetch' from -;; outside Elfeed. -;; -;; User options: -;; -;; There are three options of note: -;; -;; `elfeed-tube-fields': Customize this to set the kinds of metadata you want -;; added to Elfeed's Youtube entries. You can selectively turn on/off -;; thumbnails, transcripts etc. -;; -;; `elfeed-tube-auto-save-p': Set this boolean to save fetched Youtube metadata -;; to your Elfeed database, i.e. to persist the data on disk for all entries. -;; -;; `elfeed-tube-auto-fetch-p': Unset this boolean to turn off fetching metadata. -;; You can then call `elfeed-tube-fetch' to manually fetch data for specific -;; feed entries. -;; -;; See the customization group `elfeed-tube' for more options. See the README -;; for more information. -;; -;;; Code: -(require 'elfeed) -(eval-when-compile - (require 'cl-lib)) -(require 'subr-x) -(require 'rx) -(require 'aio) - -(require 'elfeed-tube-utils) - -;; Customizatiion options -(defgroup elfeed-tube nil - "Elfeed-tube: View youtube details in Elfeed." - :group 'elfeed - :prefix "elfeed-tube-") - -(defcustom elfeed-tube-fields - '(duration thumbnail description captions chapters) - "Metadata fields to fetch for youtube entries in Elfeed. - -This is a list of symbols. The ordering is not relevant. - -The choices are -- duration for video length, -- thumbnail for video thumbnail, -- description for video description, -- captions for video transcript, -- comments for top video comments. (NOT YET IMPLEMENTED) - -Other symbols are ignored. - -To set the thumbnail size, see `elfeed-tube-thumbnail-size'. -To set caption language(s), see `elfeed-tube-captions-languages'." - :group 'elfeed-tube - :type '(repeat (choice (const duration :tag "Duration") - (const thumbnail :tag "Thumbnail") - (const description :tag "Description") - (const captions :tag "Transcript")))) ;TODO - -(defcustom elfeed-tube-thumbnail-size 'small - "Video thumbnail size to show in the Elfeed buffer. - -This is a symbol. Choices are large, medium and small. Setting -this to nil to disable showing thumbnails, but customize -`elfeed-tube-fields' for that instead." - :group 'elfeed-tube - :type '(choice (const :tag "No thumbnails" nil) - (const :tag "Large thumbnails" large) - (const :tag "Medium thumbnails" medium) - (const :tag "Small thumbnails" small))) - -(defcustom elfeed-tube-invidious-url nil - "Invidious URL to use for retrieving data. - -Setting this is optional: If left unset, elfeed-tube will locate -and use an Invidious URL at random. This should be set to a -string, for example \"https://invidio.us\"." - :group 'elfeed-tube - :type '(choice (string :tag "Custom URL") - (const :tag "Disabled (Auto)" nil))) - -(defcustom elfeed-tube-youtube-regexp - (rx bol - (zero-or-one (or "http://" "https://")) - (zero-or-one "www.") - (or "youtube.com/" "youtu.be/")) - "Regular expression to match Elfeed entry URLss against. - -Only entries that match this regexp will be handled by -elfeed-tube when fetching information." - :group 'elfeed-tube - :type 'string) - -(defcustom elfeed-tube-captions-languages - '("en" "english" "english (auto generated)") - "Caption language priority for elfeed-tube captions. - -Captions in the first available langauge in this list will be -fetched. Each entry (string) in the list can be a language code -or a language name (case-insensitive, \"english\"): - -- \"en\" for English -- \"tr\" for Turkish -- \"ar\" for Arabic -- \"de\" for German -- \"pt-BR\" for Portugese (Brazil), etc - -Example: - (\"tr\" \"es\" \"arabic\" \"english\" \"english (auto generated)\") - -NOTE: Language codes are safer to use. Language full names differ -across regions. For example, \"english\" would be spelled -\"englisch\" if you are in Germany." - :group 'elfeed-tube - :type '(repeat string)) - -(defcustom elfeed-tube-save-indicator "[*NOT SAVED*]" - "Indicator to show in Elfeed entry buffers that have unsaved metadata. - -This can be set to a string, which will be displayed below the -headers as a button. Activating this button saves the metadata to -the Elfeed database. - -If set to any symbol except nil, it displays a minimal indicator -at the top of the buffer instead. - -If set to nil, the indicator is disabled." - :group 'elfeed-tube - :type '(choice (const :tag "Disabled" nil) - (symbol :tag "Minimal" :value t) - (string :tag "Any string"))) - -(defcustom elfeed-tube-auto-save-p nil - "Save information fetched by elfeed-tube to the Elfeed databse. - -This is a boolean. Fetched information is automatically saved -when this is set to true." - :group 'elfeed-tube - :type 'boolean) - -(defcustom elfeed-tube-auto-fetch-p t - "Fetch infor automatically when updating Elfeed or opening entries. - -This is a boolean. When set to t, video information will be -fetched automatically when updating Elfeed or opening video -entries that don't have metadata." - :group 'elfeed-tube - :type 'boolean) - -(defcustom elfeed-tube-captions-sblock-p t - "Whether sponsored segments should be de-emphasized in transcripts." - :group 'elfeed-tube - :type 'boolean) - -(defcustom elfeed-tube-captions-chunk-time 30 - "Chunk size used when displaying video transcripts. - -This is the number of seconds of the transcript to chunk into -paragraphs or sections. It must be a positive integer." - :group 'elfeed-tube - :type 'integer) - -;; Internal variables -(defvar elfeed-tube--api-videos-path "/api/v1/videos/") -(defvar elfeed-tube--info-table (make-hash-table :test #'equal)) -(defvar elfeed-tube--invidious-servers nil) -(defvar elfeed-tube--sblock-url "https://sponsor.ajay.app") -(defvar elfeed-tube--sblock-api-path "/api/skipSegments") -(defvar elfeed-tube-captions-puntcuate-p t) -(defvar elfeed-tube--api-video-fields - '("videoThumbnails" "descriptionHtml" "lengthSeconds")) -(defvar elfeed-tube--max-retries 2) -(defvar elfeed-tube--captions-db-dir - ;; `file-name-concat' is 28.1+ only - (mapconcat #'file-name-as-directory - `(,elfeed-db-directory "elfeed-tube" "captions") - "")) -(defvar elfeed-tube--comments-db-dir - (mapconcat #'file-name-as-directory - `(,elfeed-db-directory "elfeed-tube" "comments") - "")) - -(defun elfeed-tube-captions-browse-with (follow-fun) - "Return a command to browse thing at point with FOLLOW-FUN." - (lambda (event) - "Translate mouse event to point based button action." - (interactive "e") - (let ((pos (posn-point (event-end event)))) - (funcall follow-fun pos)))) - -(defvar elfeed-tube-captions-map - (let ((map (make-sparse-keymap))) - (define-key map [mouse-2] (elfeed-tube-captions-browse-with - #'elfeed-tube--browse-at-time)) - map)) - -(defface elfeed-tube-chapter-face - '((t :inherit (variable-pitch message-header-other) :weight bold)) - "Face used for chapter headings displayed by Elfeed Tube.") - -(defface elfeed-tube-timestamp-face - '((t :inherit (variable-pitch message-header-other) :weight semi-bold)) - "Face used for transcript timestamps displayed by Elfeed Tube.") - -(defvar elfeed-tube-captions-faces - '((text . variable-pitch) - (timestamp . elfeed-tube-timestamp-face) - (intro . (variable-pitch :inherit shadow)) - (outro . (variable-pitch :inherit shadow)) - (sponsor . (variable-pitch :inherit shadow - :strike-through t)) - (selfpromo . (variable-pitch :inherit shadow - :strike-through t)) - (chapter . elfeed-tube-chapter-face))) - -;; Helpers -(defsubst elfeed-tube-include-p (field) - "Check if FIELD should be fetched." - (memq field elfeed-tube-fields)) - -(defsubst elfeed-tube--get-entries () - "Get elfeed entry at point or in active region." - (pcase major-mode - ('elfeed-search-mode - (elfeed-search-selected)) - ('elfeed-show-mode - (list elfeed-show-entry)))) - -(defsubst elfeed-tube--youtube-p (entry) - "Check if ENTRY is a Youtube video entry." - (string-match-p elfeed-tube-youtube-regexp - (elfeed-entry-link entry))) - -(defsubst elfeed-tube--entry-video-id (entry) - "Get Youtube video ENTRY's video-id." - (when-let* (((elfeed-tube--youtube-p entry)) - (link (elfeed-entry-link entry))) - (elfeed-tube--url-video-id link))) - -(defsubst elfeed-tube--url-video-id (url) - "Get YouTube video URL's video-id." - (and (string-match - (concat - elfeed-tube-youtube-regexp - "\\(?:watch\\?v=\\)?" - "\\([^?&]+\\)") - url) - (match-string 1 url))) - -(defsubst elfeed-tube--random-elt (collection) - "Random element from COLLECTION." - (and collection - (elt collection (cl-random (length collection))))) - -(defsubst elfeed-tube-log (level fmt &rest objects) - "Log OBJECTS with FMT at LEVEL using `elfeed-log'." - (let ((elfeed-log-buffer-name "*elfeed-tube-log*")) - (apply #'elfeed-log level fmt objects) - nil)) - -(defsubst elfeed-tube--attempt-log (attempts) - "Format ATTEMPTS as a string." - (format "(attempt %d/%d)" - (1+ (- elfeed-tube--max-retries - attempts)) - elfeed-tube--max-retries)) - -(defsubst elfeed-tube--thumbnail-html (thumb) - "HTML for inserting THUMB." - (when (and (elfeed-tube-include-p 'thumbnail) thumb) - (concat "


"))) - -(defsubst elfeed-tube--timestamp (time) - "Format for TIME as timestamp." - (format "%d:%02d" (floor time 60) (mod time 60))) - -(defsubst elfeed-tube--same-entry-p (entry1 entry2) - "Test if elfeed ENTRY1 and ENTRY2 are the same." - (equal (elfeed-entry-id entry1) - (elfeed-entry-id entry2))) - -(defsubst elfeed-tube--match-captions-langs (lang el) - "Find caption track matching LANG in plist EL." - (and (or (string-match-p - lang - (plist-get el :languageCode)) - (string-match-p - lang - (thread-first (plist-get el :name) - (plist-get :simpleText)))) - el)) - -(defsubst elfeed-tube--truncate (str) - "Truncate STR." - (truncate-string-to-width str 20)) - -(defmacro elfeed-tube--with-db (db-dir &rest body) - "Execute BODY with DB-DIR set as the `elfeed-db-directory'." - (declare (indent defun)) - `(let ((elfeed-db-directory ,db-dir)) - ,@body)) - -(defsubst elfeed-tube--caption-get-face (type) - "Get caption face for TYPE." - (or (alist-get type elfeed-tube-captions-faces) - 'variable-pitch)) - -;; Data structure -(cl-defstruct - (elfeed-tube-item (:constructor elfeed-tube-item--create) - (:copier nil)) - "Struct to hold elfeed-tube metadata." - len thumb desc caps error) - -;; Persistence -(defun elfeed-tube--write-db (entry &optional data-item) - "Write struct DATA-ITEM to Elfeed ENTRY in `elfeed-db'." - (cl-assert (elfeed-entry-p entry)) - (when-let* ((data-item (or data-item (elfeed-tube--gethash entry)))) - (when (elfeed-tube-include-p 'description) - (setf (elfeed-entry-content-type entry) 'html) - (setf (elfeed-entry-content entry) - (when-let ((desc (elfeed-tube-item-desc data-item))) - (elfeed-ref desc)))) - (when (elfeed-tube-include-p 'duration) - (setf (elfeed-meta entry :duration) - (elfeed-tube-item-len data-item))) - (when (elfeed-tube-include-p 'thumbnail) - (setf (elfeed-meta entry :thumb) - (elfeed-tube-item-thumb data-item))) - (when (elfeed-tube-include-p 'captions) - (elfeed-tube--with-db elfeed-tube--captions-db-dir - (setf (elfeed-meta entry :caps) - (when-let ((caption (elfeed-tube-item-caps data-item))) - (elfeed-ref (prin1-to-string caption)))))) - (elfeed-tube-log 'info "[DB][Wrote to DB][video:%s]" - (elfeed-tube--truncate (elfeed-entry-title entry))) - t)) - -(defun elfeed-tube--gethash (entry) - "Get hashed elfeed-tube data for ENTRY." - (cl-assert (elfeed-entry-p entry)) - (let ((video-id (elfeed-tube--entry-video-id entry))) - (gethash video-id elfeed-tube--info-table))) - -(defun elfeed-tube--puthash (entry data-item &optional force) - "Cache elfeed-dube-item DATA-ITEM for ENTRY." - (cl-assert (elfeed-entry-p entry)) - (cl-assert (elfeed-tube-item-p data-item)) - (when-let* ((video-id (elfeed-tube--entry-video-id entry)) - (f (or force - (not (gethash video-id elfeed-tube--info-table))))) - ;; (elfeed-tube--message - ;; (format "putting %s with data %S" video-id data-item)) - (puthash video-id data-item elfeed-tube--info-table))) - -;; Data munging -(defun elfeed-tube--get-chapters (desc) - "Get chapter timestamps from video DESC." - (with-temp-buffer - (let ((chapters)) - (save-excursion (insert desc)) - (while (re-search-forward - "\\(?:\\s-\\|\\s)\\|-\\)+\\(.*\\)$" nil t) - (push (cons (match-string 1) - (thread-last (match-string 2) - (replace-regexp-in-string - """ "\"") - (replace-regexp-in-string - "'" "'") - (replace-regexp-in-string - "&" "&") - (string-trim))) - chapters)) - (nreverse chapters)))) - -(defun elfeed-tube--parse-desc (api-data) - "Parse API-DATA for video description." - (let* ((length-seconds (plist-get api-data :lengthSeconds)) - (desc-html (plist-get api-data :descriptionHtml)) - (chapters (elfeed-tube--get-chapters desc-html)) - (desc-html (replace-regexp-in-string - "\n" "
" - desc-html)) - (thumb-alist '((large . 2) - (medium . 3) - (small . 4))) - (thumb-size (cdr-safe (assoc elfeed-tube-thumbnail-size - thumb-alist))) - (thumb)) - (when (and (elfeed-tube-include-p 'thumbnail) - thumb-size) - (setq thumb (thread-first - (plist-get api-data :videoThumbnails) - (aref thumb-size) - (plist-get :url)))) - `(:length ,length-seconds :thumb ,thumb :desc ,desc-html - :chaps ,chapters))) - -(defun elfeed-tube--extract-captions-urls () - "Extract captionn URLs from Youtube HTML." - (catch 'parse-error - (if (not (search-forward "\"captions\":" nil t)) - (throw 'parse-error "captions section not found") - (delete-region (point-min) (point)) - (if (not (search-forward ",\"videoDetails" nil t)) - (throw 'parse-error "video details not found") - (goto-char (match-beginning 0)) - (delete-region (point) (point-max)) - (goto-char (point-min)) - (save-excursion - (while (search-forward "\n" nil t) - (delete-region (match-beginning 0) (match-end 0)))) - (condition-case nil - (json-parse-buffer :object-type 'plist - :array-type 'list) - (json-parse-error (throw 'parse-error "json-parse-error"))))))) - -(defun elfeed-tube--postprocess-captions (text) - "Tweak TEXT for display in the transcript." - (thread-last - ;; (string-replace "\n" " " text) - (replace-regexp-in-string "\n" " " text) - (replace-regexp-in-string "\\bi\\b" "I") - (replace-regexp-in-string - (rx (group (syntax open-parenthesis)) - (one-or-more (or space punct))) - "\\1") - (replace-regexp-in-string - (rx (one-or-more (or space punct)) - (group (syntax close-parenthesis))) - "\\1"))) - -;; Content display -(defvar elfeed-tube--save-state-map - (let ((map (make-sparse-keymap))) - (define-key map [mouse-2] #'elfeed-tube-save) - ;; (define-key map (kbd "RET") #'elfeed-tube--browse-at-time) - (define-key map [follow-link] 'mouse-face) - map)) - -(defun elfeed-tube-show (&optional intended-entry) - "Show extra video information in an Elfeed entry buffer. - -INTENDED-ENTRY is the Elfeed entry being shown. If it is not -specified use the entry (if any) being displayed in the current -buffer." - (when-let* ((show-buf - (if intended-entry - (get-buffer (elfeed-show--buffer-name intended-entry)) - (and (elfeed-tube--youtube-p elfeed-show-entry) - (current-buffer)))) - (entry (buffer-local-value 'elfeed-show-entry show-buf)) - (intended-entry (or intended-entry entry))) - (when (elfeed-tube--same-entry-p entry intended-entry) - (with-current-buffer show-buf - (let* ((inhibit-read-only t) - (feed (elfeed-entry-feed elfeed-show-entry)) - (base (and feed (elfeed-compute-base (elfeed-feed-url feed)))) - (data-item (elfeed-tube--gethash entry)) - insertions) - - (goto-char (point-max)) - (when (text-property-search-backward - 'face 'message-header-name) - (beginning-of-line) - (when (looking-at "Transcript:") - (text-property-search-backward - 'face 'message-header-name) - (beginning-of-line))) - - ;; Duration - (if-let ((d (elfeed-tube-include-p 'duration)) - (duration - (or (and data-item (elfeed-tube-item-len data-item)) - (elfeed-meta entry :duration)))) - (elfeed-tube--insert-duration entry duration) - (forward-line 1)) - - ;; DB Status - (when (and - elfeed-tube-save-indicator - (or (and data-item (elfeed-tube-item-desc data-item) - (not (elfeed-entry-content entry))) - (and data-item (elfeed-tube-item-thumb data-item) - (not (elfeed-meta entry :thumb))) - (and data-item (elfeed-tube-item-caps data-item) - (not (elfeed-meta entry :caps))))) - (let ((prop-list - `(face (:inherit warning :weight bold) mouse-face highlight - help-echo "mouse-1: save this entry to the elfeed-db" - keymap ,elfeed-tube--save-state-map))) - (if (stringp elfeed-tube-save-indicator) - (insert (apply #'propertize - elfeed-tube-save-indicator - prop-list) - "\n") - (save-excursion - (goto-char (point-min)) - (end-of-line) - (insert " " (apply #'propertize "[∗]" prop-list)))))) - - ;; Thumbnail - (when-let ((th (elfeed-tube-include-p 'thumbnail)) - (thumb (or (and data-item (elfeed-tube-item-thumb data-item)) - (elfeed-meta entry :thumb)))) - (elfeed-insert-html (elfeed-tube--thumbnail-html thumb)) - (push 'thumb insertions)) - - ;; Description - (delete-region (point) (point-max)) - (when (elfeed-tube-include-p 'description) - (if-let ((desc (or (and data-item (elfeed-tube-item-desc data-item)) - (elfeed-deref (elfeed-entry-content entry))))) - (progn (elfeed-insert-html (concat desc "") base) - (push 'desc insertions)))) - - ;; Captions - (elfeed-tube--with-db elfeed-tube--captions-db-dir - (when-let* ((c (elfeed-tube-include-p 'captions)) - (caption - (or (and data-item (elfeed-tube-item-caps data-item)) - (and (when-let - ((capstr (elfeed-deref - (elfeed-meta entry :caps)))) - (condition-case nil - (read capstr) - ('error - (elfeed-tube-log - 'error "[Show][Captions] DB parse error: %S" - (elfeed-meta entry :caps))))))))) - (when (not (elfeed-entry-content entry)) - (kill-region (point) (point-max))) - (elfeed-tube--insert-captions caption) - (push 'caps insertions))) - - (if insertions - (delete-region (point) (point-max)) - (insert (propertize "\n(empty)\n" 'face 'italic)))) - - (setq-local - imenu-prev-index-position-function #'elfeed-tube-prev-heading - imenu-extract-index-name-function #'elfeed-tube--line-at-point) - - (goto-char (point-min)))))) - -(defun elfeed-tube--insert-duration (entry duration) - "Insert the video DURATION for ENTRY into an Elfeed entry buffer." - (if (not (integerp duration)) - (elfeed-tube-log - 'warn "[Duration][video:%s][Not available]" - (elfeed-tube--truncate (elfeed-entry-title entry))) - (let ((inhibit-read-only t)) - (beginning-of-line) - (if (looking-at "Duration:") - (delete-region (point) - (save-excursion (end-of-line) - (point))) - (end-of-line) - (insert "\n")) - (insert (propertize "Duration: " 'face 'message-header-name) - (propertize (elfeed-tube--timestamp duration) - 'face 'message-header-other) - "\n") - t))) - -(defun elfeed-tube--insert-captions (caption) - "Insert the video CAPTION for ENTRY into an Elfeed entry buffer." - (if (and (listp caption) - (eq (car-safe caption) 'transcript)) - (let ((caption-ordered - (cl-loop for (type (start _) text) in (cddr caption) - with chapters = (car-safe (cdr caption)) - with pstart = 0 - for chapter = (car-safe chapters) - for oldtime = 0 then time - for time = (string-to-number (cdr start)) - for chap-begin-p = - (and chapter - (>= (floor time) (string-to-number (car chapter)))) - - if (and - (or chap-begin-p - (< (mod (floor time) - elfeed-tube-captions-chunk-time) - (mod (floor oldtime) - elfeed-tube-captions-chunk-time))) - (> (abs (- time pstart)) 3)) - collect (list pstart time para) into result and - do (setq para nil pstart time) - - if chap-begin-p - do (setq chapters (cdr-safe chapters)) - - collect (cons time - (propertize - ;; (elfeed-tube--postprocess-captions text) - (replace-regexp-in-string "\n" " " text) - 'face (elfeed-tube--caption-get-face type) - 'type type)) - into para - finally return (nconc result (list (list pstart time para))))) - (inhibit-read-only t)) - (goto-char (point-max)) - (insert "\n" - (propertize "Transcript:" 'face 'message-header-name) - "\n\n") - (cl-loop for (start end para) in caption-ordered - with chapters = (car-safe (cdr caption)) - with vspace = (propertize " " 'face 'variable-pitch) - for chapter = (car-safe chapters) - with beg = (point) do - (progn - (when (and chapter (>= start (string-to-number (car chapter)))) - (insert (propertize (cdr chapter) - 'face - (elfeed-tube--caption-get-face 'chapter) - 'timestamp (string-to-number (car chapter)) - 'mouse-face 'highlight - 'help-echo #'elfeed-tube--caption-echo - 'keymap elfeed-tube-captions-map - 'type 'chapter) - (propertize "\n\n" 'hard t)) - (setq chapters (cdr chapters))) - (insert - (propertize (format "[%s] - [%s]:" - (elfeed-tube--timestamp start) - (elfeed-tube--timestamp end)) - 'face (elfeed-tube--caption-get-face - 'timestamp)) - (propertize "\n" 'hard t) - (string-join - (mapcar (lambda (tx-cons) - (propertize (cdr tx-cons) - 'timestamp - (car tx-cons) - 'mouse-face - 'highlight - 'help-echo - #'elfeed-tube--caption-echo - 'keymap - elfeed-tube-captions-map)) - para) - vspace) - (propertize "\n\n" 'hard t))) - finally (when-let* ((w shr-width) - (fill-column w) - (use-hard-newlines t)) - (fill-region beg (point) nil t)))) - (elfeed-tube-log 'debug - "[Captions][video:%s][Not available]" - (or (and elfeed-show-entry (truncate-string-to-width - elfeed-show-entry 20)) - "")))) - -(defvar elfeed-tube--captions-echo-message - (lambda (time) (format "mouse-2: open at %s (web browser)" time))) - -(defun elfeed-tube--caption-echo (_ _ pos) - "Caption echo text at position POS." - (concat - (when-let ((type (get-text-property pos 'type))) - (when (not (eq type 'text)) - (format " segment: %s\n\n" (symbol-name type)))) - (let ((time (elfeed-tube--timestamp - (get-text-property pos 'timestamp)))) - (funcall elfeed-tube--captions-echo-message time)))) - -;; Setup -(defun elfeed-tube--auto-fetch (&optional entry) - "Fetch video information for Elfeed ENTRY and display it if possible. - -If ENTRY is not specified, use the entry (if any) corresponding -to the current buffer." - (when elfeed-tube-auto-fetch-p - (aio-listen - (elfeed-tube--fetch-1 (or entry elfeed-show-entry)) - (lambda (fetched-p) - (when (funcall fetched-p) - (elfeed-tube-show (or entry elfeed-show-entry))))))) - -(defun elfeed-tube-setup () - "Set up elfeed-tube. - -This does the following: -- Enable fetching video metadata when running `elfeed-update'. -- Enable showing video metadata in `elfeed-show' buffers if available." - (add-hook 'elfeed-new-entry-hook #'elfeed-tube--auto-fetch) - (advice-add 'elfeed-show-entry - :after #'elfeed-tube--auto-fetch) - (advice-add elfeed-show-refresh-function - :after #'elfeed-tube-show) - t) - -(defun elfeed-tube-teardown () - "Undo the effects of `elfeed-tube-setup'." - (advice-remove elfeed-show-refresh-function #'elfeed-tube-show) - (advice-remove 'elfeed-show-entry #'elfeed-tube--auto-fetch) - (remove-hook 'elfeed-new-entry-hook #'elfeed-tube--auto-fetch) - t) - -;; From aio-contrib.el: the workhorse -(defun elfeed-tube-curl-enqueue (url &rest args) - "Fetch URL with ARGS using Curl. - -Like `elfeed-curl-enqueue' but delivered by a promise. - -The result is a plist with the following keys: -:success -- the callback argument (t or nil) -:headers -- `elfeed-curl-headers' -:status-code -- `elfeed-curl-status-code' -:error-message -- `elfeed-curl-error-message' -:location -- `elfeed-curl-location' -:content -- (buffer-string)" - (let* ((promise (aio-promise)) - (cb (lambda (success) - (let ((result (list :success success - :headers elfeed-curl-headers - :status-code elfeed-curl-status-code - :error-message elfeed-curl-error-message - :location elfeed-curl-location - :content (buffer-string)))) - (aio-resolve promise (lambda () result)))))) - (prog1 promise - (apply #'elfeed-curl-enqueue url cb args)))) - -;; Fetchers -(aio-defun elfeed-tube--get-invidious-servers () - (let* ((instances-url (concat "https://api.invidious.io/instances.json" - "?pretty=1&sort_by=type,users")) - (result (aio-await (elfeed-tube-curl-enqueue instances-url :method "GET"))) - (status-code (plist-get result :status-code)) - (servers (plist-get result :content))) - (when (= status-code 200) - (thread-last - (json-parse-string servers :object-type 'plist :array-type 'list) - (cl-remove-if-not (lambda (s) (eq t (plist-get (cadr s) :api)))) - (mapcar #'car))))) - -(aio-defun elfeed-tube--get-invidious-url () - (or elfeed-tube-invidious-url - (let ((servers - (or elfeed-tube--invidious-servers - (setq elfeed-tube--invidious-servers - (elfeed--shuffle - (aio-await (elfeed-tube--get-invidious-servers))))))) - (car servers)))) - -(defsubst elfeed-tube--nrotate-invidious-servers () - "Rotate the list of Invidious servers in place." - (setq elfeed-tube--invidious-servers - (nconc (cdr elfeed-tube--invidious-servers) - (list (car elfeed-tube--invidious-servers))))) - -(aio-defun elfeed-tube--fetch-captions-tracks (entry) - (let* ((video-id (elfeed-tube--entry-video-id entry)) - (url (format "https://youtube.com/watch?v=%s" video-id)) - (response (aio-await (elfeed-tube-curl-enqueue url :method "GET"))) - (status-code (plist-get response :status-code))) - (if-let* - ((s (= status-code 200)) - (data (with-temp-buffer - (save-excursion (insert (plist-get response :content))) - (elfeed-tube--extract-captions-urls)))) - ;; (message "%S" data) - (thread-first - data - (plist-get :playerCaptionsTracklistRenderer) - (plist-get :captionTracks)) - (elfeed-tube-log 'debug "[%s][Caption tracks]: %s" - url (plist-get response :error-message)) - (elfeed-tube-log 'warn "[Captions][video:%s]: Not available" - (elfeed-tube--truncate (elfeed-entry-title entry)))))) - -(aio-defun elfeed-tube--fetch-captions-url (caption-plist entry) - (let* ((case-fold-search t) - (chosen-caption - (cl-loop - for lang in elfeed-tube-captions-languages - for pick = (cl-some - (lambda (el) (elfeed-tube--match-captions-langs lang el)) - caption-plist) - until pick - finally return pick)) - base-url language) - (cond - ((not caption-plist) - (elfeed-tube-log - 'warn "[Captions][video:%s][No languages]" - (elfeed-tube--truncate (elfeed-entry-title entry)))) - ((not chosen-caption) - (elfeed-tube-log - 'warn - "[Captions][video:%s][Not available in %s]" - (elfeed-tube--truncate (elfeed-entry-title entry)) - (string-join elfeed-tube-captions-languages ", "))) - (t (setq base-url (plist-get chosen-caption :baseUrl) - language (thread-first (plist-get chosen-caption :name) - (plist-get :simpleText))) - (let* ((response (aio-await (elfeed-tube-curl-enqueue base-url :method "GET"))) - (captions (plist-get response :content)) - (status-code (plist-get response :status-code))) - (if (= status-code 200) - (cons language captions) - (elfeed-tube-log - 'error - "[Caption][video:%s][lang:%s]: %s" - (elfeed-tube--truncate (elfeed-entry-title entry)) - language - (plist-get response :error-message)))))))) - -(defvar elfeed-tube--sblock-categories - '("sponsor" "intro" "outro" "selfpromo" "interaction")) - -(aio-defun elfeed-tube--fetch-captions-sblock (entry) - (when-let* ((categories - (json-serialize (vconcat elfeed-tube--sblock-categories))) - (api-url (url-encode-url - (concat elfeed-tube--sblock-url - elfeed-tube--sblock-api-path - "?videoID=" (elfeed-tube--entry-video-id entry) - "&categories=" categories))) - (response (aio-await (elfeed-tube-curl-enqueue - api-url :method "GET"))) - (status-code (plist-get response :status-code)) - (content-json (plist-get response :content))) - (if (= status-code 200) - (condition-case nil - (json-parse-string content-json - :object-type 'plist - :array-type 'list) - (json-parse-error - (elfeed-tube-log - 'error - "[Sponsorblock][video:%s]: JSON malformed" - (elfeed-tube--truncate (elfeed-entry-title entry))))) - (elfeed-tube-log - 'error - "[Sponsorblock][video:%s]: %s" - (elfeed-tube--truncate (elfeed-entry-title entry)) - (plist-get response :error-message))))) - -(aio-defun elfeed-tube--fetch-captions (entry) - (pcase-let* ((urls (aio-await (elfeed-tube--fetch-captions-tracks entry))) - (`(,language . ,xmlcaps) (aio-await (elfeed-tube--fetch-captions-url urls entry))) - (sblock (and elfeed-tube-captions-sblock-p - (aio-await (elfeed-tube--fetch-captions-sblock entry)))) - (parsed-caps)) - ;; (print (elfeed-entry-title entry) (get-buffer "*scratch*")) - ;; (print language (get-buffer "*scratch*")) - (when xmlcaps - (setq parsed-caps (with-temp-buffer - (insert xmlcaps) - (goto-char (point-min)) - (dolist (reps '(("&#39;" . "'") - ("&quot;" . "\"") - ("\n" . " ") - (" " . ""))) - (save-excursion - (while (search-forward (car reps) nil t) - (replace-match (cdr reps) nil t)))) - (libxml-parse-xml-region (point-min) (point-max))))) - (when parsed-caps - (when (and elfeed-tube-captions-sblock-p sblock) - (setq parsed-caps (elfeed-tube--sblock-captions sblock parsed-caps))) - (when (and elfeed-tube-captions-puntcuate-p - (string-match-p "auto-generated" language)) - (elfeed-tube--npreprocess-captions parsed-caps)) - parsed-caps))) - -(defun elfeed-tube--npreprocess-captions (captions) - "Preprocess CAPTIONS." - (cl-loop for text-element in (cddr captions) - for (_ _ text) in (cddr captions) - do (setf (nth 2 text-element) - (cl-reduce - (lambda (accum reps) - (replace-regexp-in-string (car reps) (cdr reps) accum)) - `(("\\bi\\b" . "I") - (,(rx (group (syntax open-parenthesis)) - (one-or-more (or space punct))) - . "\\1") - (,(rx (one-or-more (or space punct)) - (group (syntax close-parenthesis))) - . "\\1")) - :initial-value text)) - finally return captions)) - -(defun elfeed-tube--sblock-captions (sblock captions) - "Add sponsor data from SBLOCK into CAPTIONS." - (let ((sblock-filtered - (cl-loop for skip in sblock - for cat = (plist-get skip :category) - when (member cat elfeed-tube--sblock-categories) - collect `(:category ,cat :segment ,(plist-get skip :segment))))) - (cl-loop for telm in (cddr captions) - do (when-let - ((cat - (cl-some - (lambda (skip) - (pcase-let ((cat (intern (plist-get skip :category))) - (`(,beg ,end) (plist-get skip :segment)) - (sn (string-to-number (cdaadr telm)))) - (and (> sn beg) (< sn end) cat))) - sblock-filtered))) - (setf (car telm) cat)) - finally return captions))) - -(aio-defun elfeed-tube--fetch-desc (entry &optional attempts) - (let* ((attempts (or attempts (1+ elfeed-tube--max-retries))) - (video-id (elfeed-tube--entry-video-id entry))) - (when (> attempts 0) - (if-let ((invidious-url (aio-await (elfeed-tube--get-invidious-url)))) - (let* ((api-url (concat - invidious-url - elfeed-tube--api-videos-path - video-id - "?fields=" - (string-join elfeed-tube--api-video-fields ","))) - (desc-log (elfeed-tube-log - 'debug - "[Description][video:%s][Fetch:%s]" - (elfeed-tube--truncate (elfeed-entry-title entry)) - api-url)) - (api-response (aio-await (elfeed-tube-curl-enqueue - api-url - :method "GET"))) - (api-status (plist-get api-response :status-code)) - (api-data (plist-get api-response :content)) - (json-object-type (quote plist))) - (if (= api-status 200) - ;; Return data - (condition-case error - (prog1 - (elfeed-tube--parse-desc - (json-parse-string api-data :object-type 'plist))) - (json-parse-error - (elfeed-tube-log - 'error - "[Description][video:%s]: JSON malformed %s" - (elfeed-tube--truncate (elfeed-entry-title entry)) - (elfeed-tube--attempt-log attempts)) - (elfeed-tube--nrotate-invidious-servers) - (aio-await - (elfeed-tube--fetch-desc entry (- attempts 1))))) - ;; Retry #attempts times - (elfeed-tube-log 'error - "[Description][video:%s][%s]: %s %s" - (elfeed-tube--truncate (elfeed-entry-title entry)) - api-url - (plist-get api-response :error-message) - (elfeed-tube--attempt-log attempts)) - (elfeed-tube--nrotate-invidious-servers) - (aio-await - (elfeed-tube--fetch-desc entry (- attempts 1))))) - - (message - "Could not find a valid Invidious url. Please customize `elfeed-tube-invidious-url'.") - nil)))) - -(aio-defun elfeed-tube--with-label (label func &rest args) - (cons label (aio-await (apply func args)))) - -(aio-defun elfeed-tube--fetch-1 (entry &optional force-fetch) - (when (elfeed-tube--youtube-p entry) - (let* ((fields (aio-make-select)) - (cached (elfeed-tube--gethash entry)) - desc thumb duration caps sblock chaps error) - - ;; When to fetch a field: - ;; - force-fetch is true: always fetch - ;; - entry not cached, field not saved: fetch - ;; - entry not cached but saved: don't fetch - ;; - entry is cached with errors: don't fetch - ;; - entry is cached without errors, field not empty: don't fetch - ;; - entry is saved and field not empty: don't fetch - - ;; Fetch description? - (when (and (cl-some #'elfeed-tube-include-p - '(description duration thumbnail chapters)) - (or force-fetch - (not (or (and cached - (or (cl-intersection - '(desc duration thumb chapters) - (elfeed-tube-item-error cached)) - (elfeed-tube-item-len cached) - (elfeed-tube-item-desc cached) - (elfeed-tube-item-thumb cached))) - (or (elfeed-entry-content entry) - (elfeed-meta entry :thumb) - (elfeed-meta entry :duration)))))) - (aio-select-add fields - (elfeed-tube--with-label - 'desc #'elfeed-tube--fetch-desc entry))) - - ;; Fetch captions? - (when (and (elfeed-tube-include-p 'captions) - (or force-fetch - (not (or (and cached - (or (elfeed-tube-item-caps cached) - (memq 'caps (elfeed-tube-item-error cached)))) - (elfeed-ref-p - (elfeed-meta entry :caps)))))) - (aio-select-add fields - (elfeed-tube--with-label - 'caps #'elfeed-tube--fetch-captions entry)) - ;; Fetch caption sblocks? - (when (and nil elfeed-tube-captions-sblock-p) - (aio-select-add fields - (elfeed-tube--with-label - 'sblock #'elfeed-tube--fetch-captions-sblock entry)))) - - ;; Record fields? - (while (aio-select-promises fields) - (pcase-let ((`(,label . ,data) - (aio-await (aio-await (aio-select fields))))) - (pcase label - ('desc - (if data - (progn - (when (elfeed-tube-include-p 'thumbnail) - (setf thumb - (plist-get data :thumb))) - (when (elfeed-tube-include-p 'description) - (setf desc - (plist-get data :desc))) - (when (elfeed-tube-include-p 'duration) - (setf duration - (plist-get data :length))) - (when (elfeed-tube-include-p 'chapters) - (setf chaps - (plist-get data :chaps)))) - (setq error (append error '(desc duration thumb))))) - ('caps - (if data - (setf caps data) - (push 'caps error))) - ('sblock - (and data (setf sblock data)))))) - - ;; Add (optional) sblock and chapter info to caps - (when caps - (when sblock - (setf caps (elfeed-tube--sblock-captions sblock caps))) - (when chaps - (setf (cadr caps) chaps))) - - (if (and elfeed-tube-auto-save-p - (or duration caps desc thumb)) - ;; Store in db - (progn (elfeed-tube--write-db - entry - (elfeed-tube-item--create - :len duration :desc desc :thumb thumb - :caps caps)) - (elfeed-tube-log - 'info "Saved to elfeed-db: %s" - (elfeed-entry-title entry))) - ;; Store in session cache - (when (or duration caps desc thumb error) - (elfeed-tube--puthash - entry - (elfeed-tube-item--create - :len duration :desc desc :thumb thumb - :caps caps :error error) - force-fetch))) - ;; Return t if something was fetched - (and (or duration caps desc thumb) t)))) - -;; Interaction -(defun elfeed-tube--browse-at-time (pos) - "Browse video URL at POS at current time." - (interactive "d") - (when-let ((time (get-text-property pos 'timestamp))) - (browse-url (concat "https://youtube.com/watch?v=" - (elfeed-tube--entry-video-id elfeed-show-entry) - "&t=" - (number-to-string (floor time)))))) - -;; Entry points -;;;###autoload (autoload 'elfeed-tube-fetch "elfeed-tube" "Fetch youtube metadata for Youtube video or Elfeed entry ENTRIES." t nil) -(aio-defun elfeed-tube-fetch (entries &optional force-fetch) - "Fetch youtube metadata for Elfeed ENTRIES. - -In elfeed-show buffers, ENTRIES is the entry being displayed. - -In elfeed-search buffers, ENTRIES is the entry at point, or all -entries in the region when the region is active. - -Outside of Elfeed, prompt the user for any Youtube video URL and -generate an Elfeed-like summary buffer for it. - -With optional prefix argument FORCE-FETCH, force refetching of -the metadata for ENTRIES. - -If you want to always add this metadata to the database, consider -setting `elfeed-tube-auto-save-p'. To customize what kinds of -metadata are fetched, customize TODO -`elfeed-tube-fields'." - (interactive (list (or (elfeed-tube--ensure-list (elfeed-tube--get-entries)) - (read-from-minibuffer "Youtube video URL: ")) - current-prefix-arg)) - (if (not (listp entries)) - (elfeed-tube--fake-entry entries force-fetch) - (if (not elfeed-tube-fields) - (message "Nothing to fetch! Customize `elfeed-tube-fields'.") - (dolist (entry (elfeed-tube--ensure-list entries)) - (aio-await (elfeed-tube--fetch-1 entry force-fetch)) - (elfeed-tube-show entry))))) - -(defun elfeed-tube-save (entries) - "Save elfeed-tube youtube metadata for ENTRIES to the elfeed database. - -ENTRIES is the current elfeed entry in elfeed-show buffers. In -elfeed-search buffers it's the entry at point or the selected -entries when the region is active." - (interactive (list (elfeed-tube--get-entries))) - (dolist (entry entries) - (if (elfeed-tube--write-db entry) - (progn (message "Wrote to elfeed-db: \"%s\"" (elfeed-entry-title entry)) - (when (derived-mode-p 'elfeed-show-mode) - (elfeed-show-refresh))) - (message "elfeed-db already contains: \"%s\"" (elfeed-entry-title entry))))) - -(provide 'elfeed-tube) -;;; elfeed-tube.el ends here blob - 55d045f20bb0c233127e0ea75f02a082c33fa5a0 (mode 644) blob + /dev/null --- elpa/elfeed-tube-mpv-0.15/elfeed-tube-mpv-autoloads.el +++ /dev/null @@ -1,28 +0,0 @@ -;;; elfeed-tube-mpv-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from elfeed-tube-mpv.el - -(register-definition-prefixes "elfeed-tube-mpv" '("elfeed-tube-mpv")) - -;;; End of scraped data - -(provide 'elfeed-tube-mpv-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; elfeed-tube-mpv-autoloads.el ends here blob - 78d73de2379956b791beb03aab19b61d1571ed87 (mode 644) blob + /dev/null --- elpa/elfeed-tube-mpv-0.15/elfeed-tube-mpv-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; Generated package description from elfeed-tube-mpv.el -*- no-byte-compile: t -*- -(define-package "elfeed-tube-mpv" "0.15" "Control mpv from Elfeed" '((emacs "27.1") (elfeed-tube "0.10") (mpv "0.2.0")) :commit "7e1409e41628d61d8197ca248d910182ae4fc520" :authors '(("Karthik Chikmagalur" . "karthikchikmagalur@gmail.com")) :maintainers '(("Karthik Chikmagalur" . "karthikchikmagalur@gmail.com")) :maintainer '("Karthik Chikmagalur" . "karthikchikmagalur@gmail.com") :keywords '("news" "hypermedia") :url "https://github.com/karthink/elfeed-tube") blob - 1c1b86fc285c54fe2d2773d8f326809a86a702c5 (mode 644) blob + /dev/null --- elpa/elfeed-tube-mpv-0.15/elfeed-tube-mpv.el +++ /dev/null @@ -1,321 +0,0 @@ -;;; elfeed-tube-mpv.el --- Control mpv from Elfeed -*- lexical-binding: t; -*- - -;; Copyright (C) 2022 Karthik Chikmagalur - -;; Author: Karthik Chikmagalur -;; version: 0.10 -;; Package-Version: 0.15 -;; Package-Commit: 7e1409e41628d61d8197ca248d910182ae4fc520 -;; Keywords: news, hypermedia -;; Package-Requires: ((emacs "27.1") (elfeed-tube "0.10") (mpv "0.2.0")) -;; URL: https://github.com/karthink/elfeed-tube - -;; SPDX-License-Identifier: UNLICENSE - -;; This file is NOT part of GNU Emacs. - -;;; Commentary: -;; -;; This package provides integration with the mpv video player for `elfeed-tube' -;; entries, which see. -;; -;; With `elfeed-tube-mpv' loaded, clicking on a transcript segment in an Elfeed -;; Youtube video feed entry will launch mpv at that time, or seek to that point -;; if already playing. -;; -;; It defines two commands and a minor mode: -;; -;; - `elfeed-tube-mpv': Start an mpv session that is "connected" to an Elfeed -;; entry corresponding to a Youtube video. You can use this command to start -;; playback, or seek in mpv to a transcript segment, or enqueue a video in mpv -;; if one is already playing. Call with a prefix argument to spawn a new -;; instance of mpv instead. -;; -;; - `elfeed-tube-mpv-where': Jump in Emacs to the transcript position -;; corresponding to the current playback time in mpv. -;; -;; - `elfeed-tube-mpv-follow-mode': Follow along in the transcript in Emacs to -;; the video playback. -;; -;;; Code: -(require 'pulse) -(require 'elfeed-tube) -(require 'mpv) - -(defcustom elfeed-tube-mpv-options - '(;; "--ytdl-format=bestvideo[height<=?480]+bestaudio/best" - "--cache=yes" - ;; "--script-opts=osc-scalewindowed=2,osc-visibility=always" - ) - "List of command line arguments to pass to mpv. - -If the mpv library is available, these are appended to -`mpv-default-options'. Otherwise mpv is started with these options. - -Each element in this list is a string. Examples: -- \"--cache=yes\" -- \"--osc=no\"" - :group 'elfeed-tube - :type '(repeat string)) -(defvar elfeed-tube-mpv--available-p - (and (executable-find "mpv") - (or (executable-find "youtube-dl") - (executable-find "yt-dlp")))) -(defvar-local elfeed-tube-mpv--follow-p nil) -(defvar elfeed-tube-mpv--follow-timer nil) -(defvar-local elfeed-tube-mpv--overlay nil) -(defvar elfeed-tube-mpv-hook nil - "Hook run before starting mpv playback in an elfeed-show buffer. - -Each function must accept one argument, the current Elfeed -entry.") - -(let ((map elfeed-tube-captions-map)) - (define-key map (kbd "RET") #'elfeed-tube-mpv) - (define-key map [mouse-1] (elfeed-tube-captions-browse-with - #'elfeed-tube-mpv)) - (define-key map (kbd "C-") - (elfeed-tube-captions-browse-with - (lambda (pos) (elfeed-tube-mpv pos t))))) - -(setq-default - elfeed-tube--captions-echo-message - (defsubst elfeed-tube-mpv--echo-message (time) - (format - " mouse-1: open at %s (mpv) -C-mouse-1: open at %s (mpv, new instance) - mouse-2: open at %s (web browser)" - time time time))) - -(defsubst elfeed-tube-mpv--check-path (video-url) - "Check if currently playing mpv video matches VIDEO-URL." - (condition-case nil - (apply #'string= - (mapcar - (lambda (s) - (replace-regexp-in-string - "&t=[0-9.]*" "" s)) - (list (mpv-get-property "path") - video-url))) - ('error nil))) - -(defsubst elfeed-tube-mpv--set-timer (entry) - "Start mpv position update timer for ENTRY." - (setq elfeed-tube-mpv--follow-timer - (run-with-timer - 4 1.5 #'elfeed-tube-mpv--follow entry))) - -(defsubst elfeed-tube-mpv--overlay-clear () - "Clear mpv position overlay." - (progn (when (timerp elfeed-tube-mpv--follow-timer) - (cancel-timer elfeed-tube-mpv--follow-timer)) - (when (overlayp elfeed-tube-mpv--overlay) - (delete-overlay elfeed-tube-mpv--overlay)))) - -(defun elfeed-tube-mpv (pos &optional arg) - "Start or seek an mpv session connected to an Elfeed entry. - -Call this command with point POS on an Elfeed entry in an Elfeed -Search buffer, or anywhere in an Elfeed Entry, to play the -corresponding video. When called with point in a transcript -segment, seek here or start a new session as appropriate. If a -connected mpv session for a different video is already running -enqueue this video instead. - -With prefix argument ARG always start a new, unnconnected mpv -session." - (interactive (list (point) - current-prefix-arg)) - (if (not elfeed-tube-mpv--available-p) - (message "Could not find mpv + youtube-dl/yt-dlp in PATH.") - (when-let* ((time (or (get-text-property pos 'timestamp) 0)) - (entry (or elfeed-show-entry - (elfeed-search-selected 'ignore-region))) - (video-id (elfeed-tube--entry-video-id entry)) - (video-url (concat "https://youtube.com/watch?v=" - video-id - "&t=" - (number-to-string (floor time)))) - (args (append elfeed-tube-mpv-options (list video-url)))) - (run-hook-with-args 'elfeed-tube-mpv-hook entry) - ;; (pulse-momentary-highlight-one-line) - (if (and (not arg) (require 'mpv nil t)) - (if (mpv-live-p) - (if (elfeed-tube-mpv--check-path video-url) - (unless (= 0 time) - (mpv-seek time)) - (mpv--enqueue `("loadfile" ,video-url "append") - #'ignore) - (message "Added to playlist: %s" - (elfeed-entry-title entry))) - (apply #'mpv-start args) - (message - (concat "Starting mpv: " - (propertize "Connected to Elfeed ✓" - 'face 'success))) - (when elfeed-tube-mpv--follow-p - (elfeed-tube-mpv--set-timer entry))) - (apply #'start-process - (concat "elfeed-tube-mpv-" - (elfeed-tube--entry-video-id elfeed-show-entry)) - nil "mpv" args) - (message (concat "Starting new mpv instance: " - (propertize "Not connected to Elfeed ❌" - 'face 'error))))))) - -(defun elfeed-tube-mpv--follow (entry-playing) - "Folllow the ENTRY-PLAYING in mpv in Emacs. - -This function is intended to be run on a timer when -`elfeed-tube-mpv-follow-mode' is active." - (if (not (mpv-live-p)) - (elfeed-tube-mpv--overlay-clear) - (when-let ((entry-buf (get-buffer - (elfeed-show--buffer-name - entry-playing)))) - (when (and (or (derived-mode-p 'elfeed-show-mode) - (window-live-p (get-buffer-window entry-buf))) - (elfeed-tube--same-entry-p - (buffer-local-value 'elfeed-show-entry entry-buf) - entry-playing) - (eq (mpv-get-property "pause") - json-false)) - (condition-case nil - (when-let ((mpv-time (mpv-get-property "time-pos"))) - (with-current-buffer entry-buf - - ;; Create overlay - (unless (overlayp elfeed-tube-mpv--overlay) - (save-excursion - (goto-char (point-min)) - (text-property-search-forward - 'timestamp) - (setq elfeed-tube-mpv--overlay - (make-overlay (point) (point))) - (overlay-put elfeed-tube-mpv--overlay - 'face '(:inverse-video t)))) - - ;; Handle narrowed buffers - (when (buffer-narrowed-p) - (save-excursion - (let ((min (point-min)) - (max (point-max)) - beg end) - (goto-char min) - (setq beg (prop-match-value - (text-property-search-forward - 'timestamp))) - (goto-char max) - (widen) - (setq end (prop-match-value - (text-property-search-forward - 'timestamp))) - (narrow-to-region min max) - (cond - ((and beg (< mpv-time beg)) - (mpv-set-property "time-pos" (1- beg))) - ((and end (> mpv-time end)) - (mpv-set-property "time-pos" (1+ end)) - (mpv-set-property "pause" t)))))) - - ;; Update overlay - (when-let ((next (elfeed-tube-mpv--where-internal mpv-time))) - (goto-char next) - (move-overlay elfeed-tube-mpv--overlay - (save-excursion (beginning-of-visual-line) (point)) - (save-excursion (end-of-visual-line) (point)))))) - ('error nil)))))) - -(defun elfeed-tube-mpv--where-internal (mpv-time) - "Return the point in the Elfeed buffer that corresponds to time MPV-TIME." - (save-excursion - (while (not (get-text-property (point) 'timestamp)) - (goto-char (or (previous-single-property-change - (point) 'timestamp) - (next-single-property-change - (point) 'timestamp)))) - - (if (> (get-text-property (point) 'timestamp) - mpv-time) - (let ((match (text-property-search-backward - 'timestamp mpv-time - (lambda (mpv cur) - (< (or cur - (get-text-property - (1+ (point)) - 'timestamp)) - (- mpv 1)))))) - (goto-char (prop-match-end match)) - (text-property-search-forward 'timestamp) - (min (1+ (point)) (point-max))) - (let ((match (text-property-search-forward - 'timestamp mpv-time - (lambda (mpv cur) (if cur (> cur (- mpv 1))))))) - (prop-match-beginning match))))) - -(defun elfeed-tube-mpv-where () - "Jump to the current mpv position in a video transcript." - (interactive) - (cond - ((not (featurep 'mpv)) - (message "mpv-where requires the mpv package. You can install it with M-x `package-install' RET mpv RET.")) - ((not (and (derived-mode-p 'elfeed-show-mode) - (elfeed-tube--youtube-p elfeed-show-entry))) - (message "Not in an elfeed-show buffer for a Youtube video!")) - ((not (mpv-live-p)) - (message "No running instance of mpv is connected to Emacs.")) - ((or (previous-single-property-change - (point) 'timestamp) - (next-single-property-change - (point) 'timestamp)) - (goto-char (elfeed-tube-mpv--where-internal - (mpv-get-property "time-pos"))) - (let ((pulse-delay 0.08) - (pulse-iterations 16)) - (pulse-momentary-highlight-one-line))) - (t (message "Transcript location not found in buffer.")))) - -(define-minor-mode elfeed-tube-mpv-follow-mode - "Follow along with mpv in elfeed-show buffers. - -This appliies to Youtube feed entries in Elfeed. When the video -player mpv is started from this buffer (from any location in the -transcript), turning on this minor-mode will cause the cursor to -track the currently playing segment in mpv. You can still click -anywhere in the transcript to seek to that point in the video." - :global nil - :version "0.10" - :lighter " (-->)" - :keymap (let ((map (make-sparse-keymap))) - (prog1 map - (define-key map " " #'mpv-pause))) - :group 'elfeed-tube - (if elfeed-tube-mpv-follow-mode - (cond - - ((not (require 'mpv nil t)) - (message "mpv-follow-mode requires the mpv package. You can install it with M-x `package-install' RET mpv RET.") - (elfeed-tube-mpv-follow-mode -1)) - - ((not (derived-mode-p 'elfeed-show-mode)) - (message "mpv-follow-mode only works in elfeed-show buffers.") - (elfeed-tube-mpv-follow-mode -1)) - - (t (if-let* ((entry elfeed-show-entry) - (video-id (elfeed-tube--entry-video-id entry)) - (video-url - (concat "https://youtube.com/watch?v=" - video-id))) - (if (and (mpv-live-p) (elfeed-tube-mpv--check-path video-url)) - (elfeed-tube-mpv--set-timer entry) - (setq-local elfeed-tube-mpv--follow-p t)) - (message "Not a youtube video buffer!") - (elfeed-tube-mpv-follow-mode -1)))) - - (setq-local elfeed-tube-mpv--follow-p nil) - (when (timerp elfeed-tube-mpv--follow-timer) - (cancel-timer elfeed-tube-mpv--follow-timer)) - (elfeed-tube-mpv--overlay-clear))) - -(provide 'elfeed-tube-mpv) -;;; elfeed-tube-mpv.el ends here blob - 1d11df608482bc31f44d055009c0875fc3901a20 (mode 644) blob + /dev/null --- elpa/elisp-refs-1.5/elisp-refs-autoloads.el +++ /dev/null @@ -1,69 +0,0 @@ -;;; elisp-refs-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from elisp-refs.el - -(autoload 'elisp-refs-function "elisp-refs" "\ -Display all the references to function SYMBOL, in all loaded -elisp files. - -If called with a prefix, prompt for a directory to limit the search. - -This searches for functions, not macros. For that, see -`elisp-refs-macro'. - -(fn SYMBOL &optional PATH-PREFIX)" t) -(autoload 'elisp-refs-macro "elisp-refs" "\ -Display all the references to macro SYMBOL, in all loaded -elisp files. - -If called with a prefix, prompt for a directory to limit the search. - -This searches for macros, not functions. For that, see -`elisp-refs-function'. - -(fn SYMBOL &optional PATH-PREFIX)" t) -(autoload 'elisp-refs-special "elisp-refs" "\ -Display all the references to special form SYMBOL, in all loaded -elisp files. - -If called with a prefix, prompt for a directory to limit the search. - -(fn SYMBOL &optional PATH-PREFIX)" t) -(autoload 'elisp-refs-variable "elisp-refs" "\ -Display all the references to variable SYMBOL, in all loaded -elisp files. - -If called with a prefix, prompt for a directory to limit the search. - -(fn SYMBOL &optional PATH-PREFIX)" t) -(autoload 'elisp-refs-symbol "elisp-refs" "\ -Display all the references to SYMBOL in all loaded elisp files. - -If called with a prefix, prompt for a directory to limit the -search. - -(fn SYMBOL &optional PATH-PREFIX)" t) -(register-definition-prefixes "elisp-refs" '("elisp-")) - -;;; End of scraped data - -(provide 'elisp-refs-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; elisp-refs-autoloads.el ends here blob - a900a2c77c7c3805b559d1b500a87ac75dd950cc (mode 644) blob + /dev/null --- elpa/elisp-refs-1.5/elisp-refs-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; Generated package description from elisp-refs.el -*- no-byte-compile: t -*- -(define-package "elisp-refs" "1.5" "find callers of elisp functions or macros" '((dash "2.12.0") (s "1.11.0")) :commit "afc82c235feb228dbc860587e607599f5e67aa20" :authors '(("Wilfred Hughes" . "me@wilfred.me.uk")) :maintainers '(("Wilfred Hughes" . "me@wilfred.me.uk")) :maintainer '("Wilfred Hughes" . "me@wilfred.me.uk") :keywords '("lisp")) blob - ffb3a5b1775660d229b12da400ddc12f5a1a0b33 (mode 644) blob + /dev/null --- elpa/elisp-refs-1.5/elisp-refs.el +++ /dev/null @@ -1,911 +0,0 @@ -;;; elisp-refs.el --- find callers of elisp functions or macros -*- lexical-binding: t; -*- - -;; Copyright (C) 2016-2020 Wilfred Hughes - -;; Author: Wilfred Hughes -;; Version: 1.5 -;; Package-Version: 1.5 -;; Package-Commit: afc82c235feb228dbc860587e607599f5e67aa20 -;; Keywords: lisp -;; Package-Requires: ((dash "2.12.0") (s "1.11.0")) - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; elisp-refs.el is an Emacs package for finding references to -;; functions, macros or variables. Unlike a dumb text search, -;; elisp-refs.el actually parses the code, so it's never confused by -;; comments or `foo-bar' matching `foo'. -;; -;; See https://github.com/Wilfred/refs.el/blob/master/README.md for -;; more information. - -;;; Code: - -(require 'dash) -(require 's) -(require 'format) -(eval-when-compile (require 'cl-lib)) - -;;; Internal - -(defvar elisp-refs-verbose t) - -(defun elisp-refs--format-int (integer) - "Format INTEGER as a string, with , separating thousands." - (let ((number (abs integer)) - (parts nil)) - (while (> number 999) - (push (format "%03d" (mod number 1000)) - parts) - (setq number (/ number 1000))) - (push (format "%d" number) parts) - (concat - (if (< integer 0) "-" "") - (s-join "," parts)))) - -(defsubst elisp-refs--start-pos (end-pos) - "Find the start position of form ending at END-POS -in the current buffer." - (let ((parse-sexp-ignore-comments t)) - (scan-sexps end-pos -1))) - -(defun elisp-refs--sexp-positions (buffer start-pos end-pos) - "Return a list of start and end positions of all the sexps -between START-POS and END-POS (inclusive) in BUFFER. - -Positions exclude quote characters, so given 'foo or `foo, we -report the position of the symbol foo. - -Not recursive, so we don't consider subelements of nested sexps." - (let ((positions nil)) - (with-current-buffer buffer - (condition-case _err - (catch 'done - (while t - (let* ((sexp-end-pos (let ((parse-sexp-ignore-comments t)) - (scan-sexps start-pos 1)))) - ;; If we've reached a sexp beyond the range requested, - ;; or if there are no sexps left, we're done. - (when (or (null sexp-end-pos) (> sexp-end-pos end-pos)) - (throw 'done nil)) - ;; Otherwise, this sexp is in the range requested. - (push (list (elisp-refs--start-pos sexp-end-pos) sexp-end-pos) - positions) - (setq start-pos sexp-end-pos)))) - ;; Terminate when we see "Containing expression ends prematurely" - (scan-error nil))) - (nreverse positions))) - -(defun elisp-refs--read-buffer-form (symbols-with-pos) - "Read a form from the current buffer, starting at point. -Returns a list: -\(form form-start-pos form-end-pos symbol-positions read-start-pos) - -In Emacs 28 and earlier, SYMBOL-POSITIONS is a list of 0-indexed -symbol positions relative to READ-START-POS, according to -`read-symbol-positions-list'. - -In Emacs 29+, SYMBOL-POSITIONS is nil. If SYMBOLS-WITH-POS is -non-nil, forms are read with `read-positioning-symbols'." - (let* ((read-with-symbol-positions t) - (read-start-pos (point)) - (form (if (and symbols-with-pos (fboundp 'read-positioning-symbols)) - (read-positioning-symbols (current-buffer)) - (read (current-buffer)))) - (symbols (if (boundp 'read-symbol-positions-list) - read-symbol-positions-list - nil)) - (end-pos (point)) - (start-pos (elisp-refs--start-pos end-pos))) - (list form start-pos end-pos symbols read-start-pos))) - -(defvar elisp-refs--path nil - "A buffer-local variable used by `elisp-refs--contents-buffer'. -Internal implementation detail.") - -(defun elisp-refs--read-all-buffer-forms (buffer symbols-with-pos) - "Read all the forms in BUFFER, along with their positions." - (with-current-buffer buffer - (goto-char (point-min)) - (let ((forms nil)) - (condition-case err - (while t - (push (elisp-refs--read-buffer-form symbols-with-pos) forms)) - (error - (if (or (equal (car err) 'end-of-file) - ;; TODO: this shouldn't occur in valid elisp files, - ;; but it's happening in helm-utils.el. - (equal (car err) 'scan-error)) - ;; Reached end of file, we're done. - (nreverse forms) - ;; Some unexpected error, propagate. - (error "Unexpected error whilst reading %s position %s: %s" - (abbreviate-file-name elisp-refs--path) (point) err))))))) - -(defun elisp-refs--proper-list-p (val) - "Is VAL a proper list?" - (if (fboundp 'proper-list-p) - ;; `proper-list-p' was added in Emacs 27.1. - ;; http://git.savannah.gnu.org/cgit/emacs.git/commit/?id=2fde6275b69fd113e78243790bf112bbdd2fe2bf - (with-no-warnings (proper-list-p val)) - ;; Earlier Emacs versions only had format-proper-list-p. - (with-no-warnings (format-proper-list-p val)))) - -(defun elisp-refs--walk (buffer form start-pos end-pos symbol match-p &optional path) - "Walk FORM, a nested list, and return a list of sublists (with -their positions) where MATCH-P returns t. FORM is traversed -depth-first (pre-order traversal, left-to-right). - -MATCH-P is called with three arguments: -\(SYMBOL CURRENT-FORM PATH). - -PATH is the first element of all the enclosing forms of -CURRENT-FORM, innermost first, along with the index of the -current form. - -For example if we are looking at h in (e f (g h)), PATH takes the -value ((g . 1) (e . 2)). - -START-POS and END-POS should be the position of FORM within BUFFER." - (cond - ((funcall match-p symbol form path) - ;; If this form matches, just return it, along with the position. - (list (list form start-pos end-pos))) - ;; Otherwise, recurse on the subforms. - ((consp form) - (let ((matches nil) - ;; Find the positions of the subforms. - (subforms-positions - (if (eq (car-safe form) '\`) - ;; Kludge: `elisp-refs--sexp-positions' excludes the ` when - ;; calculating positions. So, to find the inner - ;; positions when walking from `(...) to (...), we - ;; don't need to increment the start position. - (cons nil (elisp-refs--sexp-positions buffer start-pos end-pos)) - ;; Calculate the positions after the opening paren. - (elisp-refs--sexp-positions buffer (1+ start-pos) end-pos)))) - ;; For each subform, recurse if it's a list, or a matching symbol. - (--each (-zip form subforms-positions) - (-let [(subform subform-start subform-end) it] - (when (or - (and (consp subform) (elisp-refs--proper-list-p subform)) - (and (symbolp subform) (eq subform symbol))) - (-when-let (subform-matches - (elisp-refs--walk - buffer subform - subform-start subform-end - symbol match-p - (cons (cons (car-safe form) it-index) path))) - (push subform-matches matches))))) - - ;; Concat the results from all the subforms. - (apply #'append (nreverse matches)))))) - -;; TODO: condition-case (condition-case ... (error ...)) is not a call -;; TODO: (cl-destructuring-bind (foo &rest bar) ...) is not a call -;; TODO: letf, cl-letf, -let, -let* -(defun elisp-refs--function-p (symbol form path) - "Return t if FORM looks like a function call to SYMBOL." - (cond - ((not (consp form)) - nil) - ;; Ignore (defun _ (SYMBOL ...) ...) - ((or (equal (car path) '(defsubst . 2)) - (equal (car path) '(defun . 2)) - (equal (car path) '(defmacro . 2)) - (equal (car path) '(cl-defun . 2))) - nil) - ;; Ignore (lambda (SYMBOL ...) ...) - ((equal (car path) '(lambda . 1)) - nil) - ;; Ignore (let (SYMBOL ...) ...) - ;; and (let* (SYMBOL ...) ...) - ((or - (equal (car path) '(let . 1)) - (equal (car path) '(let* . 1))) - nil) - ;; Ignore (let ((SYMBOL ...)) ...) - ((or - (equal (cl-second path) '(let . 1)) - (equal (cl-second path) '(let* . 1))) - nil) - ;; Ignore (declare-function NAME (ARGS...)) - ((equal (car path) '(declare-function . 3)) - nil) - ;; (SYMBOL ...) - ((eq (car form) symbol) - t) - ;; (foo ... #'SYMBOL ...) - ((--any-p (equal it (list 'function symbol)) form) - t) - ;; (funcall 'SYMBOL ...) - ((and (eq (car form) 'funcall) - (equal `',symbol (cl-second form))) - t) - ;; (apply 'SYMBOL ...) - ((and (eq (car form) 'apply) - (equal `',symbol (cl-second form))) - t))) - -(defun elisp-refs--macro-p (symbol form path) - "Return t if FORM looks like a macro call to SYMBOL." - (cond - ((not (consp form)) - nil) - ;; Ignore (defun _ (SYMBOL ...) ...) - ((or (equal (car path) '(defsubst . 2)) - (equal (car path) '(defun . 2)) - (equal (car path) '(defmacro . 2))) - nil) - ;; Ignore (lambda (SYMBOL ...) ...) - ((equal (car path) '(lambda . 1)) - nil) - ;; Ignore (let (SYMBOL ...) ...) - ;; and (let* (SYMBOL ...) ...) - ((or - (equal (car path) '(let . 1)) - (equal (car path) '(let* . 1))) - nil) - ;; Ignore (let ((SYMBOL ...)) ...) - ((or - (equal (cl-second path) '(let . 1)) - (equal (cl-second path) '(let* . 1))) - nil) - ;; (SYMBOL ...) - ((eq (car form) symbol) - t))) - -;; Looking for a special form is exactly the same as looking for a -;; macro. -(defalias 'elisp-refs--special-p 'elisp-refs--macro-p) - -(defun elisp-refs--variable-p (symbol form path) - "Return t if this looks like a variable reference to SYMBOL. -We consider parameters to be variables too." - (cond - ((consp form) - nil) - ;; Ignore (defun _ (SYMBOL ...) ...) - ((or (equal (car path) '(defsubst . 1)) - (equal (car path) '(defun . 1)) - (equal (car path) '(defmacro . 1)) - (equal (car path) '(cl-defun . 1))) - nil) - ;; (let (SYMBOL ...) ...) is a variable, not a function call. - ((or - (equal (cl-second path) '(let . 1)) - (equal (cl-second path) '(let* . 1))) - t) - ;; (lambda (SYMBOL ...) ...) is a variable - ((equal (cl-second path) '(lambda . 1)) - t) - ;; (let ((SYMBOL ...)) ...) is also a variable. - ((or - (equal (cl-third path) '(let . 1)) - (equal (cl-third path) '(let* . 1))) - t) - ;; Ignore (SYMBOL ...) otherwise, we assume it's a function/macro - ;; call. - ((equal (car path) (cons symbol 0)) - nil) - ((eq form symbol) - t))) - -;; TODO: benchmark building a list with `push' rather than using -;; mapcat. -(defun elisp-refs--read-and-find (buffer symbol match-p) - "Read all the forms in BUFFER, and return a list of all forms that -contain SYMBOL where MATCH-P returns t. - -For every matching form found, we return the form itself along -with its start and end position." - (-non-nil - (--mapcat - (-let [(form start-pos end-pos symbol-positions _read-start-pos) it] - ;; Optimisation: if we have a list of positions for the current - ;; form (Emacs 28 and earlier), and it doesn't contain the - ;; symbol we're looking for, don't bother walking the form. - (when (or (null symbol-positions) (assq symbol symbol-positions)) - (elisp-refs--walk buffer form start-pos end-pos symbol match-p))) - (elisp-refs--read-all-buffer-forms buffer nil)))) - -(defun elisp-refs--walk-positioned-symbols (forms symbol) - "Given a nested list of FORMS, return a list of all positions of SYMBOL. -Assumes `symbol-with-pos-pos' is defined (Emacs 29+)." - (cond - ((symbol-with-pos-p forms) - (let ((symbols-with-pos-enabled t)) - (if (eq forms symbol) - (list (list symbol - (symbol-with-pos-pos forms) - (+ (symbol-with-pos-pos forms) (length (symbol-name symbol)))))))) - ((elisp-refs--proper-list-p forms) - ;; Proper list, use `--mapcat` to reduce how much we recurse. - (--mapcat (elisp-refs--walk-positioned-symbols it symbol) forms)) - ((consp forms) - ;; Improper list, we have to recurse on head and tail. - (append (elisp-refs--walk-positioned-symbols (car forms) symbol) - (elisp-refs--walk-positioned-symbols (cdr forms) symbol))) - ((vectorp forms) - (--mapcat (elisp-refs--walk-positioned-symbols it symbol) forms)))) - -(defun elisp-refs--read-and-find-symbol (buffer symbol) - "Read all the forms in BUFFER, and return a list of all -positions of SYMBOL." - (let* ((symbols-with-pos (fboundp 'symbol-with-pos-pos)) - (forms (elisp-refs--read-all-buffer-forms buffer symbols-with-pos))) - - (if symbols-with-pos - (elisp-refs--walk-positioned-symbols forms symbol) - (-non-nil - (--mapcat - (-let [(_ _ _ symbol-positions read-start-pos) it] - (--map - (-let [(sym . offset) it] - (when (eq sym symbol) - (-let* ((start-pos (+ read-start-pos offset)) - (end-pos (+ start-pos (length (symbol-name sym))))) - (list sym start-pos end-pos)))) - symbol-positions)) - forms))))) - -(defun elisp-refs--filter-obarray (pred) - "Return a list of all the items in `obarray' where PRED returns t." - (let (symbols) - (mapatoms (lambda (symbol) - (when (and (funcall pred symbol) - (not (equal (symbol-name symbol) ""))) - (push symbol symbols)))) - symbols)) - -(defun elisp-refs--loaded-paths () - "Return a list of all files that have been loaded in Emacs. -Where the file was a .elc, return the path to the .el file instead." - (let ((elc-paths (-non-nil (mapcar #'-first-item load-history)))) - (-non-nil - (--map - (let ((el-name (format "%s.el" (file-name-sans-extension it))) - (el-gz-name (format "%s.el.gz" (file-name-sans-extension it)))) - (cond ((file-exists-p el-name) el-name) - ((file-exists-p el-gz-name) el-gz-name) - ;; Ignore files where we can't find a .el file. - (t nil))) - elc-paths)))) - -(defun elisp-refs--contents-buffer (path) - "Read PATH into a disposable buffer, and return it. -Works around the fact that Emacs won't allow multiple buffers -visiting the same file." - (let ((fresh-buffer (generate-new-buffer (format " *refs-%s*" path))) - ;; Be defensive against users overriding encoding - ;; configurations (Helpful bugs #75 and #147). - (coding-system-for-read nil) - (file-name-handler-alist - '(("\\(?:\\.dz\\|\\.txz\\|\\.xz\\|\\.lzma\\|\\.lz\\|\\.g?z\\|\\.\\(?:tgz\\|svgz\\|sifz\\)\\|\\.tbz2?\\|\\.bz2\\|\\.Z\\)\\(?:~\\|\\.~[-[:alnum:]:#@^._]+\\(?:~[[:digit:]]+\\)?~\\)?\\'" . - jka-compr-handler) - ("\\`/:" . file-name-non-special)))) - (with-current-buffer fresh-buffer - (setq-local elisp-refs--path path) - (insert-file-contents path) - ;; We don't enable emacs-lisp-mode because it slows down this - ;; function significantly. We just need the syntax table for - ;; scan-sexps to do the right thing with comments. - (set-syntax-table emacs-lisp-mode-syntax-table)) - fresh-buffer)) - -(defvar elisp-refs--highlighting-buffer - nil - "A temporary buffer used for highlighting. -Since `elisp-refs--syntax-highlight' is a hot function, we -don't want to create lots of temporary buffers.") - -(defun elisp-refs--syntax-highlight (str) - "Apply font-lock properties to a string STR of Emacs lisp code." - ;; Ensure we have a highlighting buffer to work with. - (unless (and elisp-refs--highlighting-buffer - (buffer-live-p elisp-refs--highlighting-buffer)) - (setq elisp-refs--highlighting-buffer - (generate-new-buffer " *refs-highlighting*")) - (with-current-buffer elisp-refs--highlighting-buffer - (delay-mode-hooks (emacs-lisp-mode)))) - - (with-current-buffer elisp-refs--highlighting-buffer - (erase-buffer) - (insert str) - (if (fboundp 'font-lock-ensure) - (font-lock-ensure) - (with-no-warnings - (font-lock-fontify-buffer))) - (buffer-string))) - -(defun elisp-refs--replace-tabs (string) - "Replace tabs in STRING with spaces." - ;; This is important for unindenting, as we may unindent by less - ;; than one whole tab. - (s-replace "\t" (s-repeat tab-width " ") string)) - -(defun elisp-refs--lines (string) - "Return a list of all the lines in STRING. -'a\nb' -> ('a\n' 'b')" - (let ((lines nil)) - (while (> (length string) 0) - (let ((index (s-index-of "\n" string))) - (if index - (progn - (push (substring string 0 (1+ index)) lines) - (setq string (substring string (1+ index)))) - (push string lines) - (setq string "")))) - (nreverse lines))) - -(defun elisp-refs--map-lines (string fn) - "Execute FN for each line in string, and join the result together." - (let ((result nil)) - (dolist (line (elisp-refs--lines string)) - (push (funcall fn line) result)) - (apply #'concat (nreverse result)))) - -(defun elisp-refs--unindent-rigidly (string) - "Given an indented STRING, unindent rigidly until -at least one line has no indent. - -STRING should have a 'elisp-refs-start-pos property. The returned -string will have this property updated to reflect the unindent." - (let* ((lines (s-lines string)) - ;; Get the leading whitespace for each line. - (indents (--map (car (s-match (rx bos (+ whitespace)) it)) - lines)) - (min-indent (-min (--map (length it) indents)))) - (propertize - (elisp-refs--map-lines - string - (lambda (line) (substring line min-indent))) - 'elisp-refs-unindented min-indent))) - -(defun elisp-refs--containing-lines (buffer start-pos end-pos) - "Return a string, all the lines in BUFFER that are between -START-POS and END-POS (inclusive). - -For the characters that are between START-POS and END-POS, -propertize them." - (let (expanded-start-pos expanded-end-pos) - (with-current-buffer buffer - ;; Expand START-POS and END-POS to line boundaries. - (goto-char start-pos) - (beginning-of-line) - (setq expanded-start-pos (point)) - (goto-char end-pos) - (end-of-line) - (setq expanded-end-pos (point)) - - ;; Extract the rest of the line before and after the section we're interested in. - (let* ((before-match (buffer-substring expanded-start-pos start-pos)) - (after-match (buffer-substring end-pos expanded-end-pos)) - ;; Concat the extra text with the actual match, ensuring we - ;; highlight the match as code, but highlight the rest as as - ;; comments. - (text (concat - (propertize before-match - 'face 'font-lock-comment-face) - (elisp-refs--syntax-highlight (buffer-substring start-pos end-pos)) - (propertize after-match - 'face 'font-lock-comment-face)))) - (-> text - (elisp-refs--replace-tabs) - (elisp-refs--unindent-rigidly) - (propertize 'elisp-refs-start-pos expanded-start-pos - 'elisp-refs-path elisp-refs--path)))))) - -(defun elisp-refs--find-file (button) - "Open the file referenced by BUTTON." - (find-file (button-get button 'path)) - (goto-char (point-min))) - -(define-button-type 'elisp-refs-path-button - 'action 'elisp-refs--find-file - 'follow-link t - 'help-echo "Open file") - -(defun elisp-refs--path-button (path) - "Return a button that navigates to PATH." - (with-temp-buffer - (insert-text-button - (abbreviate-file-name path) - :type 'elisp-refs-path-button - 'path path) - (buffer-string))) - -(defun elisp-refs--describe (button) - "Show *Help* for the symbol referenced by BUTTON." - (let ((symbol (button-get button 'symbol)) - (kind (button-get button 'kind))) - (cond ((eq kind 'symbol) - (describe-symbol symbol)) - ((eq kind 'variable) - (describe-variable symbol)) - (t - ;; Emacs uses `describe-function' for functions, macros and - ;; special forms. - (describe-function symbol))))) - -(define-button-type 'elisp-refs-describe-button - 'action 'elisp-refs--describe - 'follow-link t - 'help-echo "Describe") - -(defun elisp-refs--describe-button (symbol kind) - "Return a button that shows *Help* for SYMBOL. -KIND should be 'function, 'macro, 'variable, 'special or 'symbol." - (with-temp-buffer - (insert (symbol-name kind) " ") - (insert-text-button - (symbol-name symbol) - :type 'elisp-refs-describe-button - 'symbol symbol - 'kind kind) - (buffer-string))) - -(defun elisp-refs--pluralize (number thing) - "Human-friendly description of NUMBER occurrences of THING." - (format "%s %s%s" - (elisp-refs--format-int number) - thing - (if (equal number 1) "" "s"))) - -(defun elisp-refs--format-count (symbol ref-count file-count - searched-file-count prefix) - (let* ((file-str (if (zerop file-count) - "" - (format " in %s" (elisp-refs--pluralize file-count "file")))) - (found-str (format "Found %s to %s%s." - (elisp-refs--pluralize ref-count "reference") - symbol - file-str)) - (searched-str (if prefix - (format "Searched %s in %s." - (elisp-refs--pluralize searched-file-count "loaded file") - (elisp-refs--path-button (file-name-as-directory prefix))) - (format "Searched all %s loaded in Emacs." - (elisp-refs--pluralize searched-file-count "file"))))) - (s-word-wrap 70 (format "%s %s" found-str searched-str)))) - -;; TODO: if we have multiple matches on one line, we repeatedly show -;; that line. That's slightly confusing. -(defun elisp-refs--show-results (symbol description results - searched-file-count prefix) - "Given a RESULTS list where each element takes the form \(forms . buffer\), -render a friendly results buffer." - (let ((buf (get-buffer-create (format "*refs: %s*" symbol)))) - (switch-to-buffer buf) - (let ((inhibit-read-only t)) - (erase-buffer) - (save-excursion - ;; Insert the header. - (insert - (elisp-refs--format-count - description - (-sum (--map (length (car it)) results)) - (length results) - searched-file-count - prefix) - "\n\n") - ;; Insert the results. - (--each results - (-let* (((forms . buf) it) - (path (with-current-buffer buf elisp-refs--path))) - (insert - (propertize "File: " 'face 'bold) - (elisp-refs--path-button path) "\n") - (--each forms - (-let [(_ start-pos end-pos) it] - (insert (elisp-refs--containing-lines buf start-pos end-pos) - "\n"))) - (insert "\n"))) - ;; Prepare the buffer for the user. - (elisp-refs-mode))) - ;; Cleanup buffers created when highlighting results. - (when elisp-refs--highlighting-buffer - (kill-buffer elisp-refs--highlighting-buffer)))) - -(defun elisp-refs--loaded-bufs () - "Return a list of open buffers, one for each path in `load-path'." - (mapcar #'elisp-refs--contents-buffer (elisp-refs--loaded-paths))) - -(defun elisp-refs--search-1 (bufs match-fn) - "Call MATCH-FN on each buffer in BUFS, reporting progress -and accumulating results. - -BUFS should be disposable: we make no effort to preserve their -state during searching. - -MATCH-FN should return a list where each element takes the form: -\(form start-pos end-pos)." - (let* (;; Our benchmark suggests we spend a lot of time in GC, and - ;; performance improves if we GC less frequently. - (gc-cons-percentage 0.8) - (total-bufs (length bufs))) - (let ((searched 0) - (forms-and-bufs nil)) - (dolist (buf bufs) - (let* ((matching-forms (funcall match-fn buf))) - ;; If there were any matches in this buffer, push the - ;; matches along with the buffer into our results - ;; list. - (when matching-forms - (push (cons matching-forms buf) forms-and-bufs)) - ;; Give feedback to the user on our progress, because - ;; searching takes several seconds. - (when (and (zerop (mod searched 10)) - elisp-refs-verbose) - (message "Searched %s/%s files" searched total-bufs)) - (cl-incf searched))) - (when elisp-refs-verbose - (message "Searched %s/%s files" total-bufs total-bufs)) - forms-and-bufs))) - -(defun elisp-refs--search (symbol description match-fn &optional path-prefix) - "Find references to SYMBOL in all loaded files; call MATCH-FN on each buffer. -When PATH-PREFIX, limit to loaded files whose path starts with that prefix. - -Display the results in a hyperlinked buffer. - -MATCH-FN should return a list where each element takes the form: -\(form start-pos end-pos)." - (let* ((loaded-paths (elisp-refs--loaded-paths)) - (matching-paths (if path-prefix - (--filter (s-starts-with? path-prefix it) loaded-paths) - loaded-paths)) - (loaded-src-bufs (mapcar #'elisp-refs--contents-buffer matching-paths))) - ;; Use unwind-protect to ensure we always cleanup temporary - ;; buffers, even if the user hits C-g. - (unwind-protect - (progn - (let ((forms-and-bufs - (elisp-refs--search-1 loaded-src-bufs match-fn))) - (elisp-refs--show-results symbol description forms-and-bufs - (length loaded-src-bufs) path-prefix))) - ;; Clean up temporary buffers. - (--each loaded-src-bufs (kill-buffer it))))) - -(defun elisp-refs--completing-read-symbol (prompt &optional filter) - "Read an interned symbol from the minibuffer, -defaulting to the symbol at point. PROMPT is the string to prompt -with. - -If FILTER is given, only offer symbols where (FILTER sym) returns -t." - (let ((filter (or filter (lambda (_) t)))) - (read - (completing-read prompt - (elisp-refs--filter-obarray filter) - nil nil nil nil - (-if-let (sym (thing-at-point 'symbol)) - (when (funcall filter (read sym)) - sym)))))) - -;;; Commands - -;;;###autoload -(defun elisp-refs-function (symbol &optional path-prefix) - "Display all the references to function SYMBOL, in all loaded -elisp files. - -If called with a prefix, prompt for a directory to limit the search. - -This searches for functions, not macros. For that, see -`elisp-refs-macro'." - (interactive - (list (elisp-refs--completing-read-symbol "Function: " #'functionp) - (when current-prefix-arg - (read-directory-name "Limit search to loaded files in: ")))) - (when (not (functionp symbol)) - (if (macrop symbol) - (user-error "%s is a macro. Did you mean elisp-refs-macro?" - symbol) - (user-error "%s is not a function. Did you mean elisp-refs-symbol?" - symbol))) - (elisp-refs--search symbol - (elisp-refs--describe-button symbol 'function) - (lambda (buf) - (elisp-refs--read-and-find buf symbol #'elisp-refs--function-p)) - path-prefix)) - -;;;###autoload -(defun elisp-refs-macro (symbol &optional path-prefix) - "Display all the references to macro SYMBOL, in all loaded -elisp files. - -If called with a prefix, prompt for a directory to limit the search. - -This searches for macros, not functions. For that, see -`elisp-refs-function'." - (interactive - (list (elisp-refs--completing-read-symbol "Macro: " #'macrop) - (when current-prefix-arg - (read-directory-name "Limit search to loaded files in: ")))) - (when (not (macrop symbol)) - (if (functionp symbol) - (user-error "%s is a function. Did you mean elisp-refs-function?" - symbol) - (user-error "%s is not a function. Did you mean elisp-refs-symbol?" - symbol))) - (elisp-refs--search symbol - (elisp-refs--describe-button symbol 'macro) - (lambda (buf) - (elisp-refs--read-and-find buf symbol #'elisp-refs--macro-p)) - path-prefix)) - -;;;###autoload -(defun elisp-refs-special (symbol &optional path-prefix) - "Display all the references to special form SYMBOL, in all loaded -elisp files. - -If called with a prefix, prompt for a directory to limit the search." - (interactive - (list (elisp-refs--completing-read-symbol "Special form: " #'special-form-p) - (when current-prefix-arg - (read-directory-name "Limit search to loaded files in: ")))) - (elisp-refs--search symbol - (elisp-refs--describe-button symbol 'special-form) - (lambda (buf) - (elisp-refs--read-and-find buf symbol #'elisp-refs--special-p)) - path-prefix)) - -;;;###autoload -(defun elisp-refs-variable (symbol &optional path-prefix) - "Display all the references to variable SYMBOL, in all loaded -elisp files. - -If called with a prefix, prompt for a directory to limit the search." - (interactive - ;; This is awkward. We don't want to just offer defvar variables, - ;; because then we can't search for code which uses `let' to bind - ;; symbols. There doesn't seem to be a good way to only offer - ;; variables that have been bound at some point. - (list (elisp-refs--completing-read-symbol "Variable: " ) - (when current-prefix-arg - (read-directory-name "Limit search to loaded files in: ")))) - (elisp-refs--search symbol - (elisp-refs--describe-button symbol 'variable) - (lambda (buf) - (elisp-refs--read-and-find buf symbol #'elisp-refs--variable-p)) - path-prefix)) - -;;;###autoload -(defun elisp-refs-symbol (symbol &optional path-prefix) - "Display all the references to SYMBOL in all loaded elisp files. - -If called with a prefix, prompt for a directory to limit the -search." - (interactive - (list (elisp-refs--completing-read-symbol "Symbol: " ) - (when current-prefix-arg - (read-directory-name "Limit search to loaded files in: ")))) - (elisp-refs--search symbol - (elisp-refs--describe-button symbol 'symbol) - (lambda (buf) - (elisp-refs--read-and-find-symbol buf symbol)) - path-prefix)) - -;;; Mode - -(defvar elisp-refs-mode-map - (let ((map (make-sparse-keymap))) - ;; TODO: it would be nice for TAB to navigate to file buttons too, - ;; like *Help* does. - (set-keymap-parent map special-mode-map) - (define-key map (kbd "") #'elisp-refs-next-match) - (define-key map (kbd "") #'elisp-refs-prev-match) - (define-key map (kbd "n") #'elisp-refs-next-match) - (define-key map (kbd "p") #'elisp-refs-prev-match) - (define-key map (kbd "q") #'kill-this-buffer) - (define-key map (kbd "RET") #'elisp-refs-visit-match) - map) - "Keymap for `elisp-refs-mode'.") - -(define-derived-mode elisp-refs-mode special-mode "Refs" - "Major mode for refs results buffers.") - -(defun elisp--refs-visit-match (open-fn) - "Go to the search result at point. -Open file with function OPEN_FN. `find-file` or `find-file-other-window`" - (interactive) - (let* ((path (get-text-property (point) 'elisp-refs-path)) - (pos (get-text-property (point) 'elisp-refs-start-pos)) - (unindent (get-text-property (point) 'elisp-refs-unindented)) - (column-offset (current-column)) - (line-offset -1)) - (when (null path) - (user-error "No match here")) - - ;; If point is not on the first line of the match, work out how - ;; far away the first line is. - (save-excursion - (while (equal pos (get-text-property (point) 'elisp-refs-start-pos)) - (forward-line -1) - (cl-incf line-offset))) - - (funcall open-fn path) - (goto-char pos) - ;; Move point so we're on the same char in the buffer that we were - ;; on in the results buffer. - (forward-line line-offset) - (beginning-of-line) - (let ((target-offset (+ column-offset unindent)) - (i 0)) - (while (< i target-offset) - (if (looking-at "\t") - (cl-incf i tab-width) - (cl-incf i)) - (forward-char 1))))) - -(defun elisp-refs-visit-match () - "Goto the search result at point." - (interactive) - (elisp--refs-visit-match #'find-file)) - -(defun elisp-refs-visit-match-other-window () - "Goto the search result at point, opening in another window." - (interactive) - (elisp--refs-visit-match #'find-file-other-window)) - - -(defun elisp-refs--move-to-match (direction) - "Move point one match forwards. -If DIRECTION is -1, moves backwards instead." - (let* ((start-pos (point)) - (match-pos (get-text-property start-pos 'elisp-refs-start-pos)) - current-match-pos) - (condition-case _err - (progn - ;; Move forward/backwards until we're on the next/previous match. - (catch 'done - (while t - (setq current-match-pos - (get-text-property (point) 'elisp-refs-start-pos)) - (when (and current-match-pos - (not (equal match-pos current-match-pos))) - (throw 'done nil)) - (forward-char direction))) - ;; Move to the beginning of that match. - (while (equal (get-text-property (point) 'elisp-refs-start-pos) - (get-text-property (1- (point)) 'elisp-refs-start-pos)) - (forward-char -1)) - ;; Move forward until we're on the first char of match within that - ;; line. - (while (or - (looking-at " ") - (eq (get-text-property (point) 'face) - 'font-lock-comment-face)) - (forward-char 1))) - ;; If we're at the last result, don't move point. - (end-of-buffer - (progn - (goto-char start-pos) - (signal 'end-of-buffer nil)))))) - -(defun elisp-refs-prev-match () - "Move to the previous search result in the Refs buffer." - (interactive) - (elisp-refs--move-to-match -1)) - -(defun elisp-refs-next-match () - "Move to the next search result in the Refs buffer." - (interactive) - (elisp-refs--move-to-match 1)) - -(provide 'elisp-refs) -;;; elisp-refs.el ends here blob - 6d22de33f570450fc5a630a3b4d331ce4aeccc53 (mode 644) blob + /dev/null --- elpa/embark-1.1/.dir-locals.el +++ /dev/null @@ -1,6 +0,0 @@ -;;; Directory Local Variables -;;; For more information see (info "(emacs) Directory Variables") - -((emacs-lisp-mode - (show-trailing-whitespace . t) - (indent-tabs-mode . nil))) blob - 7a694c9699a986b9adf1f6cb8a18a6e923e47ed9 (mode 644) blob + /dev/null --- elpa/embark-1.1/.elpaignore +++ /dev/null @@ -1 +0,0 @@ -LICENSE \ No newline at end of file blob - 058be1ce20770245b36f435560955810398007cc (mode 644) blob + /dev/null --- elpa/embark-1.1/CHANGELOG.org +++ /dev/null @@ -1,105 +0,0 @@ -#+title: Embark changelog - -* Version 1.1 (2024-04-18) -- The =embark-consult= package contains a new exporter for - =consult-location= targets (produced by several =consult= commands such - as =consult-line=), which exports to a grep mode buffer. Users wishing - to use the new grep mode exporter can use the following - configuration: - #+begin_src emacs-lisp - (setf (alist-get 'consult-location embark-exporters-alist) - #'embark-consult-export-location-grep) - #+end_src - The main reason for adding the new exporter is that users of the - =wgrep= package will be able to make use of a feature that =wgrep= has - and the built-in =occur-edit-mode= lacks: when editing search results - you can add new lines to a result location. There are also some - disadvantages of grep mode compared to occur mode (which is why the - previously existing occur mode exporter continues to be the - default): (1) =wgrep= is a third party package while =occur-edit-mode= - is built-in; (2) occur mode buffers can list lines in any kind of - buffer, but grep mode and =wgrep= are meant for lines of files - exclusively. -* Version 1.0 (2023-12-08) -- You can now use around action hooks with multitarget actions (that - you couldn't previously was an oversight). -- Users of the =embark-consult= package can now use consult async search - commands such as =consult-grep= as multitarget actions (through - =embark-act-all=) to search a list of files. For example, you can use - =consult-find= to search among file /names/ and once you have the - relevant files in the minibuffer, you can use =embark-act-all= to - search for some text in those files. When acting on buffers consult - async search commands will search the associated file if there is - one, or else the =default-directory= of the buffer. -- =embark-bindings= and similar commands now show definition of keyboard - macros. -- =embark-org= now recognizes Org links in non-org buffers. -- Now pressing RET in an =embark-collect= on a selection made by - using =embark-select= in a normal buffer will take you to the location - each target was collected from. -- Some functions renamed for greater consistency (these functions are - unlikely to be referred to in user's configuration): - - =embark-target-completion-at-point= → =embark-target-completion-list-candidate= - - =embark-target-top-minibuffer-completion= → =embark-target-top-minibuffer-candidate= - - =embark-completions-buffer-candidates= → =embark-completion-list-candidates= -* Version 0.23 (2023-09-19) -- Added a mode line indicator showing the number of selected targets in - the current buffer (contributed by @minad, thanks!) -- Now =embark-select= can also be called as a top-level command, from - outside =embark-act=. When called that way, it will select the first - target at point. -- =embark-org= now has support for acting on references to org headings - in other buffers, by jumping to the heading first and then running - the action. One source of references to org headings in other - buffers are agenda views: each agenda item is such a reference. But - this feature also supports some great third party commands which - produce references to org headings, such as =org-ql-find= from the - =org-ql= package or =consult-org-heading= from =consult=. -- Renamed =embark-isearch= to =embark-isearch-forward= and added - =embark-isearch-backward=. -- =embark-become= now removes any invisible text from the minibuffer - input on the grounds that users probably expect the target command - to receive exactly the input they can see. -- The meaning of the prefix argument in =embark-bindings= has flipped: - now by default global key bindings are excluded and you can use =C-u= - to include them. -- If any candidate in an embark-collect buffer contains a newline, - then candidates will be separated by horizontal lines. This is handy - for the kill-ring, which you can browse by calling =embark-collect= - from =yank-pop=. -* Version 0.22.1 (2023-04-20) -** New feature: selections -Now users can select several targets to make an ad hoc collection. The -commands =embark-act-all=, =embark-export= and =embark-collect= will act on -the selection if it is non-empty. To select or deselect a target use -the =embark-select= action (bound to =SPC= in =embark-general-map=). If you -have some targets selected, then using =embark-select= through -=embark-act-all= will deselect them. - -Before this change the Embark Collect buffers had their own -implementation of selections which has been removed. This is how to -translate the old bindings to the new feature (which is available in -all buffers, not just Embark Collect buffers!): - -| Task | Old binding | New binding | -|--------------------+-------------+---------------| -| Mark a candidate | m | a SPC | -| Unmark a candidate | u | a SPC | -| Unmark all | U | A SPC | -| Mark all [1] | t | A SPC | -| Toggle all marks | t | not available | - -[1] Marking all candidates (with either the old =t= or the new =A SPC=) -requires that there are no marked candidates to begin with. - -In order to make room for the binding of =embark-select= to -=SPC=, some other key bindings were moved: - -- =mark= in =embark-general-map= was moved to =C-SPC=. -- =outline-mark-subtree= in =embark-heading-map= was moved to =C-SPC=. -- =whitespace-cleanup-region= in =embark-region-map= was moved to =F=. - -* Version 0.21.1 (2020-01-30) -- Finally started this changelog on 2023-04-20. Known issues with the - changelog: it started very late, the first entry is not very - informative. blob - 0ac8be62137458349c1935f910593e8d6529a826 (mode 644) blob + /dev/null --- elpa/embark-1.1/README-elpa +++ /dev/null @@ -1,1471 +0,0 @@ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - EMBARK: EMACS MINI-BUFFER ACTIONS ROOTED IN - KEYMAPS - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - - - - -1 Overview -══════════ - - Embark makes it easy to choose a command to run based on what is near - point, both during a minibuffer completion session (in a way familiar - to Helm or Counsel users) and in normal buffers. Bind the command - `embark-act' to a key and it acts like prefix-key for a keymap of - /actions/ (commands) relevant to the /target/ around point. With point - on an URL in a buffer you can open the URL in a browser or eww or - download the file it points to. If while switching buffers you spot an - old one, you can kill it right there and continue to select another. - Embark comes preconfigured with over a hundred actions for common - types of targets such as files, buffers, identifiers, s-expressions, - sentences; and it is easy to add more actions and more target types. - Embark can also collect all the candidates in a minibuffer to an - occur-like buffer or export them to a buffer in a major-mode specific - to the type of candidates, such as dired for a set of files, ibuffer - for a set of buffers, or customize for a set of variables. - - -1.1 Acting on targets -───────────────────── - - You can think of `embark-act' as a keyboard-based version of a - right-click contextual menu. The `embark-act' command (which you - should bind to a convenient key), acts as a prefix for a keymap - offering you relevant /actions/ to use on a /target/ determined by the - context: - - • In the minibuffer, the target is the current top completion - candidate. - • In the `*Completions*' buffer the target is the completion at point. - • In a regular buffer, the target is the region if active, or else the - file, symbol, URL, s-expression or defun at point. - - Multiple targets can be present at the same location and you can cycle - between them by repeating the `embark-act' key binding. The type of - actions offered depend on the type of the target. Here is a sample of - a few of the actions offered in the default configuration: - - • For files you get offered actions like deleting, copying, renaming, - visiting in another window, running a shell command on the file, - etc. - • For buffers the actions include switching to or killing the buffer. - • For package names the actions include installing, removing or - visiting the homepage. - • For Emacs Lisp symbols the actions include finding the definition, - looking up documentation, evaluating (which for a variable - immediately shows the value, but for a function lets you pass it - some arguments first). There are some actions specific to variables, - such as setting the value directly or though the customize system, - and some actions specific to commands, such as binding it to a key. - - By default when you use `embark-act' if you don't immediately select - an action, after a short delay Embark will pop up a buffer showing a - list of actions and their corresponding key bindings. If you are using - `embark-act' outside the minibuffer, Embark will also highlight the - current target. These behaviors are configurable via the variable - `embark-indicators'. Instead of selecting an action via its key - binding, you can select it by name with completion by typing `C-h' - after `embark-act'. - - Everything is easily configurable: determining the current target, - classifying it, and deciding which actions are offered for each type - in the classification. The above introduction just mentions part of - the default configuration. - - Configuring which actions are offered for a type is particularly easy - and requires no programming: the variable `embark-keymap-alist' - associates target types with variables containing keymaps, and those - keymaps containing bindings for the actions. (To examine the available - categories and their associated keymaps, you can use `C-h v - embark-keymap-alist' or customize that variable.) For example, in the - default configuration the type `file' is associated with the symbol - `embark-file-map'. That symbol names a keymap with single-letter key - bindings for common Emacs file commands, for instance `c' is bound to - `copy-file'. This means that if you are in the minibuffer after - running a command that prompts for a file, such as `find-file' or - `rename-file', you can copy a file by running `embark-act' and then - pressing `c'. - - These action keymaps are very convenient but not strictly necessary - when using `embark-act': you can use any command that reads from the - minibuffer as an action and the target of the action will be inserted - at the first minibuffer prompt. After running `embark-act' all of your - key bindings and even `execute-extended-command' can be used to run a - command. For example, if you want to replace all occurrences of the - symbol at point, just use `M-%' as the action, there is no need to - bind `query-replace' in one of Embark's keymaps. Also, those action - keymaps are normal Emacs keymaps and you should feel free to bind in - them whatever commands you find useful as actions and want to be - available through convenient bindings. - - The actions in `embark-general-map' are available no matter what type - of completion you are in the middle of. By default this includes - bindings to save the current candidate in the kill ring and to insert - the current candidate in the previously selected buffer (the buffer - that was current when you executed a command that opened up the - minibuffer). - - Emacs's minibuffer completion system includes metadata indicating the - /category/ of what is being completed. For example, `find-file''s - metadata indicates a category of `file' and `switch-to-buffer''s - metadata indicates a category of `buffer'. Embark has the related - notion of the /type/ of a target for actions, and by default when - category metadata is present it is taken to be the type of minibuffer - completion candidates when used as targets. Emacs commands often do - not set useful category metadata so the [Marginalia] package, which - supplies this missing metadata, is highly recommended for use with - Embark. - - Embark's default configuration has actions for the following target - types: files, buffers, symbols, packages, URLs, bookmarks, and as a - somewhat special case, actions for when the region is active. You can - read about the [default actions and their key bindings] on the GitHub - project wiki. - - -[Marginalia] - -[default actions and their key bindings] - - - -1.2 The default action on a target -────────────────────────────────── - - Embark has a notion of default action for a target: - - • If the target is a minibuffer completion candidate, then the default - action is whatever command opened the minibuffer in the first place. - For example if you run `kill-buffer', then the default action will - be to kill buffers. - • If the target comes from a regular buffer (i.e., not a minibuffer), - then the default action is whatever is bound to `RET' in the keymap - of actions for that type of target. For example, in Embark's default - configuration for a URL found at point the default action is - `browse-url', because `RET' is bound to `browse-url' in the - `embark-url-map' keymap. - - To run the default action you can press `RET' after running - `embark-act'. Note that if there are several different targets at a - given location, each has its own default action, so first cycle to the - target you want and then press `RET' to run the corresponding default - action. - - There is also `embark-dwim' which runs the default action for the - first target found. It's pretty handy in non-minibuffer buffers: with - Embark's default configuration it will: - - • Open the file at point. - • Open the URL at point in a web browser (using the `browse-url' - command). - • Compose a new email to the email address at point. - • In an Emacs Lisp buffer, if point is on an opening parenthesis or - right after a closing one, it will evaluate the corresponding - expression. - • Go to the definition of an Emacs Lisp function, variable or macro at - point. - • Find the file corresponding to an Emacs Lisp library at point. - - -1.3 Working with sets of possible targets -───────────────────────────────────────── - - Besides acting individually on targets, Embark lets you work - collectively on a set of target /candidates/. For example, while you - are in the minibuffer the candidates are simply the possible - completions of your input. Embark provides three main commands to work - on candidate sets: - - • The `embark-act-all' command runs the same action on each of the - current candidates. It is just like using `embark-act' on each - candidate in turn. (Because you can easily act on many more - candidates than you meant to, by default Embark asks you to confirm - uses of `embark-act-all'; you can turn this off by setting the user - option `embark-confirm-act-all' to `nil'.) - - • The `embark-collect' command produces a buffer listing all the - current candidates, for you to peruse and run actions on at your - leisure. The candidates are displayed as a list showing additional - annotations. If any of the candidates contain newlines, then - horizontal lines are used to separate candidates. - - The Embark Collect buffer is somewhat "dired-like": you can select - and deselect candidates through `embark-select' (available as an - action in `embark-act', bound to `SPC'; but you could also give it a - global key binding). In an Embark Collect buffer `embark-act' is - bound to `a' and `embark-act-all' is bound to `A'; `embark-act-all' - will act on all currently marked candidates if there any, and will - act on all candidates if none are marked. In particular, this means - that `a SPC' will toggle whether the candidate at point is selected, - and `A SPC' will select all candidates if none are selected, or - deselect all selected candidates if there are some. - - • The `embark-export' command tries to open a buffer in an appropriate - major mode for the set of candidates. If the candidates are files - export produces a Dired buffer; if they are buffers, you get an - Ibuffer buffer; and if they are packages you get a buffer in package - menu mode. - - If you use the grepping commands from the [Consult] package, - `consult-grep', `consult-git-grep' or `consult-ripgrep', then you - should install the `embark-consult' package, which adds support for - exporting a list of grep results to an honest grep-mode buffer, on - which you can even use [wgrep] if you wish. - - When in doubt choosing between exporting and collecting, a good rule - of thumb is to always prefer `embark-export' since when an exporter to - a special major mode is available for a given type of target, it will - be more featureful than an Embark collect buffer, and if no such - exporter is configured the `embark-export' command falls back to the - generic `embark-collect'. - - These commands are always available as "actions" (although they do not - act on just the current target but on all candidates) for `embark-act' - and are bound to `A', `S' (for "snapshot"), and `E', respectively, in - `embark-general-map'. This means that you do not have to bind your own - key bindings for these (although you can, of course!), just a key - binding for `embark-act'. - - In Embark Collect or Embark Export buffers that were obtained by - running `embark-collect' or `embark-export' from within a minibuffer - completion session, `g' is bound to a command that restarts the - completion session, that is, the command that opened the minibuffer is - run again and the minibuffer contents restored. You can then interact - normally with the command, perhaps editing the minibuffer contents, - and, if you wish, you can rerun `embark-collect' or `embark-export' to - get an updated buffer. - - -[Consult] - -[wgrep] - -1.3.1 Selecting some targets to make an ad hoc candidate set -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - The commands for working with sets of candidates just described, - namely `embark-act-all', `embark-export' and `embark-collect' by - default work with all candidates defined in the current context. For - example, in the minibuffer they operate on all currently completion - candidates, or in a dired buffer they work on all marked files (or all - files if none are marked). Embark also has a notion of /selection/, - where you can accumulate an ad hoc list of targets for these commands - to work on. - - The selection is controlled by using the `embark-select' action, bound - to `SPC' in `embark-general-map' so that it is always available (you - can also give `embark-select' a global key binding if you wish; when - called directly, not as an action for `embark-act', it will select the - first target at point). Calling this action on a target toggles its - membership in the current buffer's Embark selection; that is, it adds - it to selection if not selected and removes it from the selection if - it was selected. Whenever the selection for a buffer is non-empty, the - commands `embark-act-all', `embark-export' and `embark-collect' will - act on the selection. - - To deselect all selected targets, you can use the `embark-select' - action through `embark-act-all', since this will run `embark-select' - on each member of the current selection. Similarly if no targets are - selected and you are in a minibuffer completion session, running - `embark-select' from `embark-act-all' will select all the current - completion candidates. - - By default, whenever some targets are selected in the current buffer, - a count of selected targets appears in the mode line. This can be - turned off or customized through the `embark-selection-indicator' user - option. - - The selection functionality is supported in every buffer: - - • In the minibuffer this gives a convenient way to act on several - completion candidates that don't follow any simple pattern: just go - through the completions selecting the ones you want, then use - `embark-act-all'. For example, you could attach several files at - once to an email. - • For Embark Collect buffers this functionality enables a dired-like - workflow, in which you mark various candidates and apply an action - to all at once. (It supersedes a previous ad hoc dired-like - interface that was implemented only in Embark Collect buffers, with - a slightly different interface.) - • In a eww buffer you could use this to select various links you wish - to follow up on, and then collect them into a buffer. Similarly, - while reading Emacs's info manual you could select some symbols you - want to read more about and export them to an `apropos-mode' buffer. - • You can use selections in regular text or programming buffers to do - complex editing operations. For example, if you have three - paragraphs scattered over a file and you want to bring them - together, you can select each one, insert them all somewhere and - finally delete all of them (from their original locations). - - -1.3.2 `embark-live' a live-updating variant of `embark-collect' -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - Finally, there is also an `embark-live' variant of the - `embark-collect' command which automatically updates the collection - after each change in the source buffer. Users of a completion UI that - automatically updates and displays the candidate list (such as - Vertico, Icomplete, Fido-mode, or MCT) will probably not want to use - `embark-live' from the minibuffer as they will then have two live - updating displays of the completion candidates! - - A more likely use of `embark-live' is to be called from a regular - buffer to display a sort of live updating "table of contents" for the - buffer. This depends on having appropriate candidate collectors - configured in `embark-candidate-collectors'. There are not many in - Embark's default configuration, but you can try this experiment: open - a dired buffer in a directory that has very many files, mark a few, - and run `embark-live'. You'll get an Embark Collect buffer containing - only the marked files, which updates as you mark or unmark files in - dired. To make `embark-live' genuinely useful other candidate - collectors are required. The `embark-consult' package (documented - near the end of this manual) contains a few: one for imenu items and - one for outline headings as used by `outline-minor-mode'. Those - collectors really do give `embark-live' a table-of-contents feel. - - -1.4 Switching to a different command without losing what you've typed -───────────────────────────────────────────────────────────────────── - - Embark also has the `embark-become' command which is useful for when - you run a command, start typing at the minibuffer and realize you - meant a different command. The most common case for me is that I run - `switch-to-buffer', start typing a buffer name and realize I haven't - opened the file I had in mind yet! I'll use this situation as a - running example to illustrate `embark-become'. When this happens I - can, of course, press `C-g' and then run `find-file' and open the - file, but this requires retyping the portion of the file name you - already typed. This process can be streamlined with `embark-become': - while still in the `switch-to-buffer' you can run `embark-become' and - effectively make the `switch-to-buffer' command become `find-file' for - this run. - - You can bind `embark-become' to a key in `minibuffer-local-map', but - it is also available as an action under the letter `B' (uppercase), so - you don't need a binding if you already have one for `embark-act'. So, - assuming I have `embark-act' bound to, say, `C-.', once I realize I - haven't open the file I can type `C-. B C-x C-f' to have - `switch-to-buffer' become `find-file' without losing what I have - already typed in the minibuffer. - - But for even more convenience, `embark-become' offers shorter key - bindings for commands you are likely to want the current command to - become. When you use `embark-become' it looks for the current command - in all keymaps named in the list `embark-become-keymaps' and then - activates all keymaps that contain it. For example, the default value - of `embark-become-keymaps' contains a keymap - `embark-become-file+buffer-map' with bindings for several commands - related to files and buffers, in particular, it binds - `switch-to-buffer' to `b' and `find-file' to `f'. So when I - accidentally try to switch to a buffer for a file I haven't opened - yet, `embark-become' finds that the command I ran, `switch-to-buffer', - is in the keymap `embark-become-file+buffer-map', so it activates that - keymap (and any others that also contain a binding for - `switch-to-buffer'). The end result is that I can type `C-. B f' to - switch to `find-file'. - - -2 Quick start -═════════════ - - The easiest way to install Embark is from GNU ELPA, just run `M-x - package-install RET embark RET'. (It is also available on MELPA.) It - is highly recommended to also install [Marginalia] (also available on - GNU ELPA), so that Embark can offer you preconfigured actions in more - contexts. For `use-package' users, the following is a very reasonable - starting configuration: - - ┌──── - │ (use-package marginalia - │ :ensure t - │ :config - │ (marginalia-mode)) - │ - │ (use-package embark - │ :ensure t - │ - │ :bind - │ (("C-." . embark-act) ;; pick some comfortable binding - │ ("C-;" . embark-dwim) ;; good alternative: M-. - │ ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' - │ - │ :init - │ - │ ;; Optionally replace the key help with a completing-read interface - │ (setq prefix-help-command #'embark-prefix-help-command) - │ - │ ;; Show the Embark target at point via Eldoc. You may adjust the - │ ;; Eldoc strategy, if you want to see the documentation from - │ ;; multiple providers. Beware that using this can be a little - │ ;; jarring since the message shown in the minibuffer can be more - │ ;; than one line, causing the modeline to move up and down: - │ - │ ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) - │ ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) - │ - │ :config - │ - │ ;; Hide the mode line of the Embark live/completions buffers - │ (add-to-list 'display-buffer-alist - │ '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" - │ nil - │ (window-parameters (mode-line-format . none))))) - │ - │ ;; Consult users will also want the embark-consult package. - │ (use-package embark-consult - │ :ensure t ; only need to install it, embark loads it after consult if found - │ :hook - │ (embark-collect-mode . consult-preview-at-point-mode)) - └──── - - About the suggested key bindings for `embark-act' and `embark-dwim': - • Those key bindings are unlikely to work in the terminal, but - terminal users are probably well aware of this and will know to - select different bindings. - • The suggested `C-.' binding is used by default in (at least some - installations of) GNOME to input emojis, and Emacs doesn't even get - a chance to respond to the binding. You can select a different key - binding for `embark-act' or use `ibus-setup' to change the shortcut - for emoji insertion (Emacs 29 will likely use `C-x 8 e e', in case - you want to set the same one system-wide). - • The suggested alternative of `M-.' for `embark-dwim' is bound by - default to `xref-find-definitions'. That is a very useful command - but overwriting it with `embark-dwim' is sensible since in Embark's - default configuration, `embark-dwim' will also find the definition - of the identifier at point. (Note that `xref-find-definitions' with - a prefix argument prompts you for an identifier, `embark-dwim' does - not cover this case). - - Other Embark commands such as `embark-act-all', `embark-become', - `embark-collect', and `embark-export' can be run through `embark-act' - as actions bound to `A', `B', `S' (for "snapshot"), and `E' - respectively, and thus don't really need a dedicated key binding, but - feel free to bind them directly if you so wish. If you do choose to - bind them directly, you'll probably want to bind them in - `minibuffer-local-map', since they are most useful in the minibuffer - (in fact, `embark-become' only works in the minibuffer). - - The command `embark-dwim' executes the default action at - point. Another good keybinding for `embark-dwim' is `M-.' since - `embark-dwim' acts like `xref-find-definitions' on the symbol at - point. `C-.' can be seen as a right-click context menu at point and - `M-.' acts like left-click. The keybindings are mnemonic, both act at - the point (`.'). - - Embark needs to know what your minibuffer completion system considers - to be the list of candidates and which one is the current candidate. - Embark works out of the box if you use Emacs's default tab completion, - the built-in `icomplete-mode' or `fido-mode', or the third-party - packages [Vertico] or [Ivy]. - - If you are a [Helm] or [Ivy] user you are unlikely to want Embark - since those packages include comprehensive functionality for acting on - minibuffer completion candidates. (Embark does come with Ivy - integration despite this.) - - -[Marginalia] - -[Vertico] - -[Ivy] - -[Helm] - - -3 Advanced configuration -════════════════════════ - -3.1 Showing information about available targets and actions -─────────────────────────────────────────────────────────── - - By default, if you run `embark-act' and do not immediately select an - action, after a short delay Embark will pop up a buffer called - `*Embark Actions*' containing a list of available actions with their - key bindings. You can scroll that buffer with the mouse of with the - usual commands `scroll-other-window' and `scroll-other-window-down' - (bound by default to `C-M-v' and `C-M-S-v'). - - That functionality is provided by the `embark-mixed-indicator', but - Embark has other indicators that can provide information about the - target and its type, what other targets you can cycle to, and which - actions have key bindings in the action map for the current type of - target. Any number of indicators can be active at once and the user - option `embark-indicators' should be set to a list of the desired - indicators. - - Embark comes with the following indicators: - - • `embark-minimal-indicator': shows a messages in the echo area or - minibuffer prompt showing the current target and the types of all - targets starting with the current one. - - • `embark-highlight-indicator': highlights the target at point; on by - default. - - • `embark-verbose-indicator': displays a table of actions and their - key bindings in a buffer; this is not on by default, in favor of the - mixed indicator described next. - - • `embark-mixed-indicator': starts out by behaving as the minimal - indicator but after a short delay acts as the verbose indicator; - this is on by default. - - • `embark-isearch-highlight-indicator': this only does something when - the current target is the symbol at point, in which case it lazily - highlights all occurrences of that symbol in the current buffer, - like isearch; also on by default. - - Users of the popular [which-key] package may prefer to use the - `embark-which-key-indicator' from the [Embark wiki]. Just copy its - definition from the wiki into your configuration and customize the - `embark-indicators' user option to exclude the mixed and verbose - indicators and to include `embark-which-key-indicator'. - - If you use [Vertico], there is an even easier way to get a - `which-key'-like display that also lets you use completion to narrow - down the list of alternatives, described at the end of the next - section. - - -[which-key] - -[Embark wiki] - - -[Vertico] - - -3.2 Selecting commands via completions instead of key bindings -────────────────────────────────────────────────────────────── - - As an alternative to reading the list of actions in the verbose or - mixed indicators (see the previous section for a description of - these), you can press the `embark-help-key', which is `C-h' by default - (but you may prefer `?' to free up `C-h' for use as a prefix) after - running `embark-act'. Pressing the help key will prompt you for the - name of an action with completion (but feel free to enter a command - that is not among the offered candidates!), and will also remind you - of the key bindings. You can press `embark-keymap-prompter-key', which - is `@' by default, at the prompt and then one of the key bindings to - enter the name of the corresponding action. - - You may think that with the `*Embark Actions*' buffer popping up to - remind you of the key bindings you'd never want to use completion to - select an action by name, but personally I find that typing a small - portion of the action name to narrow down the list of candidates feels - significantly faster than visually scanning the entire list of - actions. - - If you find you prefer selecting actions that way, you can configure - embark to always prompt you for actions by setting the variable - `embark-prompter' to `embark-completing-read-prompter'. - - On the other hand, you may wish to continue using key bindings for the - actions you perform most often, and to use completion only to explore - what further actions are available or when you've forgotten a key - binding. In that case, you may prefer to use the minimal indicator, - which does not pop-up an `*Embark Actions*' buffer at all, and to use - the `embark-help-key' whenever you need help. This unobtrusive setup - is achieved with the following configuration: - - ┌──── - │ (setq embark-indicators - │ '(embark-minimal-indicator ; default is embark-mixed-indicator - │ embark-highlight-indicator - │ embark-isearch-highlight-indicator)) - └──── - - [Vertico] users may wish to configure a grid display for the actions - and key-bindings, reminiscent of the popular package [which-key], but, - of course, enhanced by the use of completion to narrow the list of - commands. In order to get the grid display, put the following in your - Vertico configuration: - - ┌──── - │ (add-to-list 'vertico-multiform-categories '(embark-keybinding grid)) - │ (vertico-multiform-mode) - └──── - - This will make the available keys be shown in a compact grid like in - `which-key'. The `vertico-multiform-mode' also enables keys such as - `M-V', `M-G', `M-B', and `M-U' for manually switching between layouts - in Vertico buffers. - - -[Vertico] - -[which-key] - -3.2.1 Selecting commands via completion outside of Embark -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - If you like this completion interface for exploring key bindings for - Embark actions, you may want to use it elsewhere in Emacs. You can use - Embark's completion-based command prompter to list: - - • key bindings under a prefix, - • local key bindings, or - • all key bindings. - - To use it for key bindings under a prefix (you can use this to replace - the `which-key' package, for example), use this configuration: - - ┌──── - │ (setq prefix-help-command #'embark-prefix-help-command) - └──── - - Now, when you have started on a prefix sequence such as `C-x' or - `C-c', pressing `C-h' will bring up the Embark version of the built-in - `prefix-help-command', which will list the keys under that prefix and - their bindings, and lets you select the one you wanted with - completion, or by key binding if you press - `embark-keymap-prompter-key'. - - To list local or global key bindings, use the command - `embark-bindings'. You can bind that to `C-h b', which is the default - key binding for the built-in `describe-bindings' command, which this - command can replace. By default, `embark-bindings' lists local key - bindings, typically those bound in the major mode keymap; to get - global bindings as well, call it with a `C-u' prefix argument. - - -3.3 Quitting the minibuffer after an action -─────────────────────────────────────────── - - By default, if you call `embark-act' from the minibuffer it quits the - minibuffer after performing the action. You can change this by setting - the user option `embark-quit-after-action' to `nil'. Having - `embark-act' /not/ quit the minibuffer can be useful to turn commands - into little "thing managers". For example, you can use `find-file' as - a little file manager or `describe-package' as a little package - manager: you can run those commands, perform a series of actions, and - then quit the command. - - If you want to control the quitting behavior in a fine-grained manner - depending on the action, you can set `embark-quit-after-action' to an - alist, associating commands to either `t' for quitting or `nil' for - not quitting. When using an alist, you can use the special key `t' to - specify the default behavior. For example, to specify that by default - actions should not quit the minibuffer but that using `kill-buffer' as - an action should quit, you can use the following configuration: - - ┌──── - │ (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) - └──── - - The variable `embark-quit-after-action' only specifies a default, that - is, it only controls whether or not `embark-act' quits the minibuffer - when you call it without a prefix argument, and you can select the - opposite behavior to what the variable says by calling `embark-act' - with `C-u'. Also note that both the variable - `embark-quit-after-action' and `C-u' have no effect when you call - `embark-act' outside the minibuffer. - - If you find yourself using the quitting and non-quitting variants of - `embark-act' about equally often, independently of the action, you may - prefer to simply have separate commands for them instead of a single - command that you call with `C-u' half the time. You could, for - example, keep the default exiting behavior of `embark-act' and define - a non-quitting version as follows: - - ┌──── - │ (defun embark-act-noquit () - │ "Run action but don't quit the minibuffer afterwards." - │ (interactive) - │ (let ((embark-quit-after-action nil)) - │ (embark-act))) - └──── - - -3.4 Running some setup after injecting the target -───────────────────────────────────────────────── - - You can customize what happens after the target is inserted at the - minibuffer prompt of an action. There are - `embark-target-injection-hooks', that are run by default after - injecting the target into the minibuffer. The variable - `embark-target-injection-hooks' is an alist associating commands to - their setup hooks. There are two special keys: if no setup hook is - specified for a given action, the hook associated to `t' is run; and - the hook associated to `:always' is run regardless of the - action. (This variable used to have the less explicit name of - `embark-setup-action-hooks', so please update your configuration.) - - For example, consider using `shell-command' as an action during file - completion. It would be useful to insert a space before the target - file name and to leave the point at the beginning, so you can - immediately type the shell command to run on that file. That's why in - Embark's default configuration there is an entry in - `embark-target-injection-hooks' associating `shell-command' to a hook - that includes `embark--shell-prep', a simple helper function that - quotes all the spaces in the file name, inserts an extra space at the - beginning of the line and leaves point to the left of it. - - Now, the preparation that `embark--shell-prep' does would be useless - if Embark did what it normally does after it inserts the target of the - action at the minibuffer prompt, which is to "press `RET'" for you, - accepting the target as is; if Embark did that for `shell-command' you - wouldn't get a chance to type in the command to execute! That is why - in Embark's default configuration the entry for `shell-command' in - `embark-target-injection-hooks' also contains the function - `embark--allow-edit'. - - Embark used to have a dedicated variable `embark-allow-edit-actions' - to which you could add commands for which Embark should forgo pressing - `RET' for you after inserting the target. Since its effect can also be - achieved via the general `embark-target-injection-hooks' mechanism, - that variable has been removed to simplify Embark. Be sure to update - your configuration; if you had something like: - - ┌──── - │ (add-to-list 'embark-allow-edit-actions 'my-command) - └──── - - you should replace it with: - - ┌──── - │ (push 'embark--allow-edit - │ (alist-get 'my-command embark-target-injection-hooks)) - └──── - - - Also note that while you could abuse `embark--allow-edit' so that you - have to confirm "dangerous" actions such as `delete-file', it is - better to implement confirmation by adding the `embark--confirm' - function to the appropriate entry of a different hook alist, namely, - `embark-pre-action-hooks'. - - Besides `embark--allow-edit', Embark comes with another function that - is of general utility in action setup hooks: - `embark--ignore-target'. Use it for commands that do prompt you in the - minibuffer but for which inserting the target would be - inappropriate. This is not a common situation but does occasionally - arise. For example it is used by default for - `shell-command-on-region': that command is used as an action for - region targets, and it prompts you for a shell command; you typically - do /not/ want the target, that is the contents of the region, to be - entered at that prompt! - - -3.5 Running hooks before, after or around an action -─────────────────────────────────────────────────── - - Embark has three variables, `embark-pre-action-hooks', - `embark-post-action-hooks' and `embark-around-action-hooks', which are - alists associating commands to hooks that should run before or after - or as around advice for the command when used as an action. As with - `embark-target-injection-hooks', there are two special keys for the - alists: `t' designates the default hook to run when no specific hook - is specified for a command; and the hook associated to `:always' runs - regardless. - - The default values of those variables are fairly extensive, adding - creature comforts to make running actions a smooth experience. Embark - comes with several functions intended to be added to these hooks, and - used in the default values of `embark-pre-action-hooks', - `embark-post-action-hooks' and `embark-around-action-hooks'. - - For pre-action hooks: - - `embark--confirm' - Prompt the user for confirmation before executing the - action. This is used be default for commands deemed "dangerous", - or, more accurately, hard to undo, such as `delete-file' and - `kill-buffer'. - - `embark--unmark-target' - Unmark the active region. Use this for commands you want to act - on the region contents but without the region being active. The - default configuration uses this function as a pre-action hook - for `occur' and `query-replace', for example, so that you can - use them as actions with region targets to search the whole - buffer for the text contained in the region. Without this - pre-action hook using `occur' as an action for a region target - would be pointless: it would search for the the region contents - /in the region/, (typically, due to the details of regexps) - finding only one match! - - `embark--beginning-of-target' - Move to the beginning of the target (for targets that report - bounds). This is used by default for backward motion commands - such as `backward-sexp', so that they don't accidentally leave - you on the current target. - - `embark--end-of-target' - Move to the end of the target. This is used similarly to the - previous function, but also for commands that act on the last - s-expression like `eval-last-sexp'. This allow you to act on an - s-expression from anywhere inside it and still use - `eval-last-sexp' as an action. - - `embark--xref-push-markers' - Push the current location on the xref marker stack. Use this for - commands that take you somewhere and for which you'd like to be - able to come back to where you were using - `xref-pop-marker-stack'. This is used by default for - `find-library'. - - For post-action hooks: - - `embark--restart' - Restart the command currently prompting in the minibuffer, so - that the list of completion candidates is updated. This is - useful as a post action hook for commands that delete or rename - a completion candidate; for example the default value of - `embark-post-action-hooks' uses it for `delete-file', - `kill-buffer', `rename-file', `rename-buffer', etc. - - For around-action hooks: - - `embark--mark-target' - Save existing mark and point location, mark the target and run - the action. Most targets at point outside the minibuffer report - which region of the buffer they correspond to (this is the - information used by `embark-highlight-indicator' to know what - portion of the buffer to highlight); this function marks that - region. It is useful as an around action hook for commands that - expect a region to be marked, for example, it is used by default - for `indent-region' so that it works on s-expression targets, or - for `fill-region' so that it works on paragraph targets. - - `embark--cd' - Run the action with `default-directory' set to the directory - associated to the current target. The target should be of type - `file', `buffer', `bookmark' or `library', and the associated - directory is what you'd expect in each case. - - `embark--narrow-to-target' - Run the action with buffer narrowed to current target. Use this - as an around hook to localize the effect of actions that don't - already work on just the region. In the default configuration it - is used for `repunctuate-sentences'. - - `embark--save-excursion' - Run the action restoring point at the end. The current default - configuration doesn't use this but it is available for users. - - -3.6 Creating your own keymaps -───────────────────────────── - - All internal keymaps are defined with the standard helper macro - `defvar-keymap'. For example a simple version of the file action - keymap could be defined as follows: - - ┌──── - │ (defvar-keymap embark-file-map - │ :doc "Example keymap with a few file actions" - │ :parent embark-general-map - │ "d" #'delete-file - │ "r" #'rename-file - │ "c" #'copy-file) - └──── - - These action keymaps are perfectly normal Emacs keymaps. You may want - to inherit from the `embark-general-map' if you want to access the - default Embark actions. Note that `embark-collect' and `embark-export' - are also made available via `embark-general-map'. - - -3.7 Defining actions for new categories of targets -────────────────────────────────────────────────── - - It is easy to configure Embark to provide actions for new types of - targets, either in the minibuffer or outside it. I present below two - very detailed examples of how to do this. At several points I'll - explain more than one way to proceed, typically with the easiest - option first. I include the alternative options since there will be - similar situations where the easiest option is not available. - - -3.7.1 New minibuffer target example - tab-bar tabs -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - As an example, take the new [tab bars] from Emacs 27. I'll explain how - to configure Embark to offer tab-specific actions when you use the - tab-bar-mode commands that mention tabs by name. The configuration - explained here is now built-in to Embark (and Marginalia), but it's - still a good self-contained example. In order to setup up tab actions - you would need to: (1) make sure Embark knows those commands deal with - tabs, (2) define a keymap for tab actions and configure Embark so it - knows that's the keymap you want. - - -[tab bars] - - -◊ 3.7.1.1 Telling Embark about commands that prompt for tabs by name - - For step (1), it would be great if the `tab-bar-mode' commands - reported the completion category `tab' when asking you for a tab with - completion. (All built-in Emacs commands that prompt for file names, - for example, do have metadata indicating that they want a `file'.) - They do not, unfortunately, and I will describe a couple of ways to - deal with this. - - Maybe the easiest thing is to configure [Marginalia] to enhance those - commands. All of the `tab-bar-*-tab-by-name' commands have the words - "tab by name" in the minibuffer prompt, so you can use: - - ┌──── - │ (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) - └──── - - That's it! But in case you are ever in a situation where you don't - already have commands that prompt for the targets you want, I'll - describe how writing your own command with appropriate `category' - metadata looks: - - ┌──── - │ (defun my-select-tab-by-name (tab) - │ (interactive - │ (list - │ (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - │ (tab-bar-tabs)) - │ (user-error "No tabs found")))) - │ (completing-read - │ "Tabs: " - │ (lambda (string predicate action) - │ (if (eq action 'metadata) - │ '(metadata (category . tab)) - │ (complete-with-action - │ action tab-list string predicate))))))) - │ (tab-bar-select-tab-by-name tab)) - └──── - - As you can see, the built-in support for setting the category - meta-datum is not very easy to use or pretty to look at. To help with - this I recommend the `consult--read' function from the excellent - [Consult] package. With that function we can rewrite the command as - follows: - - ┌──── - │ (defun my-select-tab-by-name (tab) - │ (interactive - │ (list - │ (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - │ (tab-bar-tabs)) - │ (user-error "No tabs found")))) - │ (consult--read tab-list - │ :prompt "Tabs: " - │ :category 'tab)))) - │ (tab-bar-select-tab-by-name tab)) - └──── - - Much nicer! No matter how you define the `my-select-tab-by-name' - command, the first approach with Marginalia and prompt detection has - the following advantages: you get the `tab' category for all the - `tab-bar-*-bar-by-name' commands at once, also, you enhance built-in - commands, instead of defining new ones. - - - [Marginalia] - - [Consult] - - -◊ 3.7.1.2 Defining and configuring a keymap for tab actions - - Let's say we want to offer select, rename and close actions for tabs - (in addition to Embark general actions, such as saving the tab name to - the kill-ring, which you get for free). Then this will do: - - ┌──── - │ (defvar-keymap embark-tab-actions - │ :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." - │ :parent embark-general-map - │ "s" #'tab-bar-select-tab-by-name - │ "r" #'tab-bar-rename-tab-by-name - │ "k" #'tab-bar-close-tab-by-name) - │ - │ (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) - └──── - - What if after using this for a while you feel closing the tab without - confirmation is dangerous? You have a couple of options: - - 1. You can keep using the `tab-bar-close-tab-by-name' command, but - have Embark ask you for confirmation: - ┌──── - │ (push #'embark--confirm - │ (alist-get 'tab-bar-close-tab-by-name - │ embark-pre-action-hooks)) - └──── - - 2. You can write your own command that prompts for confirmation and - use that instead of `tab-bar-close-tab-by-name' in the above - keymap: - ┌──── - │ (defun my-confirm-close-tab-by-name (tab) - │ (interactive "sTab to close: ") - │ (when (y-or-n-p (format "Close tab '%s'? " tab)) - │ (tab-bar-close-tab-by-name tab))) - └──── - - Notice that this is a command you can also use directly from `M-x' - independently of Embark. Using it from `M-x' leaves something to be - desired, though, since you don't get completion for the tab names. - You can fix this if you wish as described in the previous section. - - -3.7.2 New target example in regular buffers - short Wikipedia links -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - Say you want to teach Embark to treat text of the form - `wikipedia:Garry_Kasparov' in any regular buffer as a link to - Wikipedia, with actions to open the Wikipedia page in eww or an - external browser or to save the URL of the page in the kill-ring. We - can take advantage of the actions that Embark has preconfigured for - URLs, so all we need to do is teach Embark that - `wikipedia:Garry_Kasparov' stands for the URL - `https://en.wikipedia.org/wiki/Garry_Kasparov'. - - You can be as fancy as you want with the recognized syntax. Here, to - keep the example simple, I'll assume the link matches the regexp - `wikipedia:[[:alnum:]_]+'. We will write a function that looks for a - match surrounding point, and returns a dotted list of the form `'(url - URL-OF-THE-PAGE START . END)' where `START' and `END' are the buffer - positions bounding the target, and are used by Embark to highlight it - if you have `embark-highlight-indicator' included in the list - `embark-indicators'. (There are a couple of other options for the - return value of a target finder: the bounding positions are optional - and a single target finder is allowed to return multiple targets; see - the documentation for `embark-target-finders' for details.) - - ┌──── - │ (defun my-short-wikipedia-link () - │ "Target a link at point of the form wikipedia:Page_Name." - │ (save-excursion - │ (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) - │ (end (progn (skip-chars-forward "[:alnum:]_:") (point))) - │ (str (buffer-substring-no-properties start end))) - │ (save-match-data - │ (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) - │ `(url - │ ,(format "https://en.wikipedia.org/wiki/%s" - │ (match-string 1 str)) - │ ,start . ,end)))))) - │ - │ (add-to-list 'embark-target-finders 'my-short-wikipedia-link) - └──── - - -4 How does Embark call the actions? -═══════════════════════════════════ - - Embark actions are normal Emacs commands, that is, functions with an - interactive specification. In order to execute an action, Embark calls - the command with `call-interactively', so the command reads user input - exactly as if run directly by the user. For example the command may - open a minibuffer and read a string (`read-from-minibuffer') or open a - completion interface (`completing-read'). If this happens, Embark - takes the target string and inserts it automatically into the - minibuffer, simulating user input this way. After inserting the - string, Embark exits the minibuffer, submitting the input. (The - immediate minibuffer exit can be disabled for specific actions in - order to allow editing the input; this is done by adding the - `embark--allow-edit' function to the appropriate entry of - `embark-target-injection-hooks'). Embark inserts the target string at - the first minibuffer opened by the action command, and if the command - happens to prompt the user for input more than once, the user still - interacts with the second and further prompts in the normal - fashion. Note that if a command does not prompt the user for input in - the minibuffer, Embark still allows you to use it as an action, but of - course, never inserts the target anywhere. (There are plenty of - examples in the default configuration of commands that do not prompt - the user bound to keys in the action maps, most of the region actions, - for instance.) - - This is how Embark manages to reuse normal commands as actions. The - mechanism allows you to use as Embark actions commands that were not - written with Embark in mind (and indeed almost all actions that are - bound by default in Embark's action keymaps are standard Emacs - commands). It also allows you to write new custom actions in such a - way that they are useful even without Embark. - - Staring from version 28.1, Emacs has a variable - `y-or-n-p-use-read-key', which when set to `t' causes `y-or-n-p' to - use `read-key' instead of `read-from-minibuffer'. Setting - `y-or-n-p-use-read-key' to `t' is recommended for Embark users because - it keeps Embark from attempting to insert the target at a `y-or-n-p' - prompt, which would almost never be sensible. Also consider this as a - warning to structure your own action commands so that if they use - `y-or-n-p', they do so only after the prompting for the target. - - Here is a simple example illustrating the various ways of reading - input from the user mentioned above. Bind the following commands to - the `embark-symbol-map' to be used as actions, then put the point on - some symbol and run them with `embark-act': - - ┌──── - │ (defun example-action-command1 () - │ (interactive) - │ (message "The input was `%s'." (read-from-minibuffer "Input: "))) - │ - │ (defun example-action-command2 (arg input1 input2) - │ (interactive "P\nsInput 1: \nsInput 2: ") - │ (message "The first input %swas `%s', and the second was `%s'." - │ (if arg "truly " "") - │ input1 - │ input2)) - │ - │ (defun example-action-command3 () - │ (interactive) - │ (message "Your selection was `%s'." - │ (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) - │ - │ (defun example-action-command4 () - │ (interactive) - │ (message "I don't prompt you for input and thus ignore the target!")) - │ - │ (keymap-set embark-symbol-map "X 1" #'example-action-command1) - │ (keymap-set embark-symbol-map "X 2" #'example-action-command2) - │ (keymap-set embark-symbol-map "X 3" #'example-action-command3) - │ (keymap-set embark-symbol-map "X 4" #'example-action-command4) - └──── - - Also note that if you are using the key bindings to call actions, you - can pass prefix arguments to actions in the normal way. For example, - you can use `C-u X2' with the above demonstration actions to make the - message printed by `example-action-command2' more emphatic. This - ability to pass prefix arguments to actions is useful for some actions - in the default configuration, such as - `embark-shell-command-on-buffer'. - - -4.1 Non-interactive functions as actions -──────────────────────────────────────── - - Alternatively, Embark does support one other type of action: a - non-interactive function of a single argument. The target is passed as - argument to the function. For example: - - ┌──── - │ (defun example-action-function (target) - │ (message "The target was `%s'." target)) - │ - │ (keymap-set embark-symbol-map "X 4" #'example-action-function) - └──── - - Note that normally binding non-interactive functions in a keymap is - useless, since when attempting to run them using the key binding you - get an error message similar to "Wrong type argument: commandp, - example-action-function". In general it is more flexible to write any - new Embark actions as commands, that is, as interactive functions, - because that way you can also run them directly, without Embark. But - there are a couple of reasons to use non-interactive functions as - actions: - - 1. You may already have the function lying around, and it is - convenient to simply reuse it. - - 2. For command actions the targets can only be simple string, with no - text properties. For certain advanced uses you may want the action - to receive a string /with/ some text properties, or even a - non-string target. - - -5 Embark, Marginalia and Consult -════════════════════════════════ - - Embark cooperates well with the [Marginalia] and [Consult] packages. - Neither of those packages is a dependency of Embark, but both are - highly recommended companions to Embark, for opposite reasons: - Marginalia greatly enhances Embark's usefulness, while Embark can help - enhance Consult. - - In the remainder of this section I'll explain what exactly Marginalia - does for Embark, and what Embark can do for Consult. - - -[Marginalia] - -[Consult] - -5.1 Marginalia -────────────── - - Embark comes with actions for symbols (commands, functions, variables - with actions such as finding the definition, looking up the - documentation, evaluating, etc.) in the `embark-symbol-map' keymap, - and for packages (actions like install, delete, browse url, etc.) in - the `embark-package-keymap'. - - Unfortunately Embark does not automatically offers you these keymaps - when relevant, because many built-in Emacs commands don't report - accurate category metadata. For example, a command like - `describe-package', which reads a package name from the minibuffer, - does not have metadata indicating this fact. - - In an earlier Embark version, there were functions to supply this - missing metadata, but they have been moved to Marginalia, which - augments many Emacs command to report accurate category metadata. - Simply activating `marginalia-mode' allows Embark to offer you the - package and symbol actions when appropriate again. Candidate - annotations in the Embark collect buffer are also provided by the - Marginalia package: - - • If you install Marginalia and activate `marginalia-mode', Embark - Collect buffers will use the Marginalia annotations automatically. - - • If you don't install Marginalia, you will see only the annotations - that come with Emacs (such as key bindings in `M-x', or the unicode - characters in `C-x 8 RET'). - - -5.2 Consult -─────────── - - The excellent Consult package provides many commands that use - minibuffer completion, via the `completing-read' function; plenty of - its commands can be considered enhanced versions of built-in Emacs - commands, and some are completely new functionality. One common - enhancement provided in all commands for which it makes sense is - preview functionality, for example `consult-buffer' will show you a - quick preview of a buffer before you actually switch to it. - - If you use both Consult and Embark you should install the - `embark-consult' package which provides integration between the - two. It provides exporters for several Consult commands and also - tweaks the behavior of many Consult commands when used as actions with - `embark-act' in subtle ways that you may not even notice, but make for - a smoother experience. You need only install it to get these benefits: - Embark will automatically load it after Consult if found. - - The `embark-consult' package provides the following exporters: - - • You can use `embark-export' from `consult-line', `consult-outline', - or `consult-mark' to obtain an `occur-mode' buffer. As with the - built-in `occur' command you use that buffer to jump to a match and - after that, you can then use `next-error' and `previous-error' to - navigate to other matches. You can also press `e' to activate - `occur-edit-mode' and edit the matches in place! - - • You can export from any of the Consult asynchronous search commands, - `consult-grep', `consult-git-grep', or `consult-ripgrep' to get a - `grep-mode' buffer. Here too you can use `next-error' and - `previous-error' to navigate among matches, and, if you install the - [wgrep] package, you can use it to edit the matches in place. - - In both cases, pressing `g' will rerun the Consult command you had - exported from and re-enter the input you had typed (which is similar - to reverting but a little more flexible). You can then proceed to - re-export if that's what you want, but you can also edit the input - changing the search terms or simply cancel if you see you are done - with that search. - - The `embark-consult' also contains some candidates collectors that - allow you to run `embark-live' to get a live-updating table of - contents for your buffer: - - • `embark-consult-outline-candidates' produces the outline headings of - the current buffer, using `consult-outline'. - • `embark-consult-imenu-candidates' produces the imenu items of the - current buffer, using `consult-imenu'. - • `embark-consult-imenu-or-outline-candidates' is a simple combination - of the two previous functions: it produces imenu items in buffers - deriving from `prog-mode' and otherwise outline headings. - - The way to configure `embark-live' (or `embark-collect' and - `embark-export' for that matter) to use one of these function is to - add it at the end of the `embark-candidate-collectors' list. The - `embark-consult' package by default adds the last one, which seems to - be the most sensible default. - - Besides those exporters and candidate collectors, the `embark-consult' - package provides many subtle tweaks and small integrations between - Embark and Consult. Some examples are: - - • When used as actions, the asynchronous search commands will search - only the files associated to the targets: if the targets /are/ - files, it searches those files; for buffers it will search either - the associated file if there is one, else all files in the buffer's - `default-directory'; for bookmarks it will search the file they - point to, same for Emacs Lisp libraries. This is particularly - powerful when using `embark-act-all' to act on multiple files at - once, for example you can use `consult-find' to search among file - /names/ and then `embark-act-all' and `consult-grep' to search - within the matching files. - - • For all other target types, those that do not have a sensible - notion of associated file, a Consult search command (asynchronous - or not) will search for the text of the target but leave the - minibuffer open so you can interact with the Consult command. - - • `consult-imenu' will search for the target and take you directly to - the location if it matches a unique imenu entry, otherwise it will - leave the minibuffer open so you can navigate among the matches. - - -[wgrep] - - -6 Related Packages -══════════════════ - - There are several packages that offer functionality similar to - Embark's. - - Acting on minibuffer completion candidates - The popular Ivy and Helm packages have support for acting on the - completion candidates of commands written using their APIs, and - there is an extensive ecosystem of packages meant for Helm and - for Ivy (the Ivy ones usually have "counsel" in the name) - providing commands and appropriate actions. - Acting on things at point - The built-in `context-menu-mode' provides a mouse-driven - context-sensitive configurable menu. The `do-at-point' package - by Philip Kaludercic (available on GNU ELPA), on the other hand - is keyboard-driven. - Collecting completion candidates into a buffer - The Ivy package has the command `ivy-occur' which is similar to - `embark-collect'. As with Ivy actions, `ivy-occur' only works - for commands written using the Ivy API. - - -7 Resources -═══════════ - - If you want to learn more about how others have used Embark here are - some links to read: - - • [Fifteen ways to use Embark], a blog post by Karthik Chikmagalur. - • [Protesilaos Stavrou's dotemacs], look for the section called - "Extended minibuffer actions and more (embark.el and - prot-embark.el)" - - And some videos to watch: - - • [Embark and my extras] by Protesilaos Stavrou. - • [Embark – Key features and tweaks] by Raoul Comninos on the - Emacs-Elements YouTube channel. - • [Livestreamed: Adding an Embark context action to send a stream - message] by Sacha Chua. - • [System Crafters Live! - The Many Uses of Embark] by David Wilson. - • [Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark] - by Mike Zamansky. - - -[Fifteen ways to use Embark] - - -[Protesilaos Stavrou's dotemacs] - -[Embark and my extras] - - -[Embark – Key features and tweaks] - -[Livestreamed: Adding an Embark context action to send a stream message] - - -[System Crafters Live! - The Many Uses of Embark] - - -[Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark] - - - -8 Contributions -═══════════════ - - Contributions to Embark are very welcome. There is a [wish list] for - actions, target finders, candidate collectors and exporters. For other - ideas you have for Embark, feel free to open an issue on the [issue - tracker]. Any neat configuration tricks you find might be a good fit - for the [wiki]. - - Code contributions are very welcome too, but since Embark is now on - GNU ELPA, copyright assignment to the FSF is required before you can - contribute code. - - -[wish list] - -[issue tracker] - -[wiki] - - -9 Acknowledgments -═════════════════ - - While I, Omar Antolín Camarena, have written most of the Embark code - and remain very stubborn about some of the design decisions, Embark - has received substantial help from a number of other people which this - document has neglected to mention for far too long. In particular, - Daniel Mendler has been absolutely invaluable, implementing several - important features, and providing a lot of useful advice. - - Code contributions: - - • [Daniel Mendler] - • [Clemens Radermacher] - • [José Antonio Ortega Ruiz] - • [Itai Y. Efrat] - • [a13] - • [jakanakaevangeli] - • [mihakam] - • [Brian Leung] - • [Karthik Chikmagalur] - • [Roshan Shariff] - • [condy0919] - • [Damien Cassou] - • [JimDBh] - - Advice and useful discussions: - - • [Daniel Mendler] - • [Protesilaos Stavrou] - • [Clemens Radermacher] - • [Howard Melman] - • [Augusto Stoffel] - • [Bruce d'Arcus] - • [JD Smith] - • [Karthik Chikmagalur] - • [jakanakaevangeli] - • [Itai Y. Efrat] - • [Mohsin Kaleem] - - -[Daniel Mendler] - -[Clemens Radermacher] - -[José Antonio Ortega Ruiz] - -[Itai Y. Efrat] - -[a13] - -[jakanakaevangeli] - -[mihakam] - -[Brian Leung] - -[Karthik Chikmagalur] - -[Roshan Shariff] - -[condy0919] - -[Damien Cassou] - -[JimDBh] - -[Protesilaos Stavrou] - -[Howard Melman] - -[Augusto Stoffel] - -[Bruce d'Arcus] - -[JD Smith] - -[Mohsin Kaleem] blob - 7af6a2f487aac027b13b6f61f94a81fcf8776255 (mode 644) blob + /dev/null --- elpa/embark-1.1/README.org +++ /dev/null @@ -1,1276 +0,0 @@ -#+TITLE: Embark: Emacs Mini-Buffer Actions Rooted in Keymaps -#+OPTIONS: d:nil -#+EXPORT_FILE_NAME: embark.texi -#+TEXINFO_DIR_CATEGORY: Emacs misc features -#+TEXINFO_DIR_TITLE: Embark: (embark). -#+TEXINFO_DIR_DESC: Emacs Mini-Buffer Actions Rooted in Keymaps - -#+html:
GNU ELPA -#+html: GNU-devel ELPA -#+html: MELPA -#+html: MELPA Stable - -* Overview - -Embark makes it easy to choose a command to run based on what is near -point, both during a minibuffer completion session (in a way familiar -to Helm or Counsel users) and in normal buffers. Bind the command -=embark-act= to a key and it acts like prefix-key for a keymap of -/actions/ (commands) relevant to the /target/ around point. With point on -an URL in a buffer you can open the URL in a browser or eww or -download the file it points to. If while switching buffers you spot an -old one, you can kill it right there and continue to select another. -Embark comes preconfigured with over a hundred actions for common -types of targets such as files, buffers, identifiers, s-expressions, -sentences; and it is easy to add more actions and more target types. -Embark can also collect all the candidates in a minibuffer to an -occur-like buffer or export them to a buffer in a major-mode specific -to the type of candidates, such as dired for a set of files, ibuffer -for a set of buffers, or customize for a set of variables. - -** Acting on targets - -You can think of =embark-act= as a keyboard-based version of a -right-click contextual menu. The =embark-act= command (which you should -bind to a convenient key), acts as a prefix for a keymap offering you -relevant /actions/ to use on a /target/ determined by the context: - -- In the minibuffer, the target is the current top completion - candidate. -- In the =*Completions*= buffer the target is the completion at point. -- In a regular buffer, the target is the region if active, or else the - file, symbol, URL, s-expression or defun at point. - -Multiple targets can be present at the same location and you can cycle -between them by repeating the =embark-act= key binding. The type of -actions offered depend on the type of the target. Here is a sample of -a few of the actions offered in the default configuration: - -- For files you get offered actions like deleting, copying, - renaming, visiting in another window, running a shell command on the - file, etc. -- For buffers the actions include switching to or killing the buffer. -- For package names the actions include installing, removing or - visiting the homepage. -- For Emacs Lisp symbols the actions include finding the definition, - looking up documentation, evaluating (which for a variable - immediately shows the value, but for a function lets you pass it - some arguments first). There are some actions specific to variables, - such as setting the value directly or though the customize system, - and some actions specific to commands, such as binding it to a key. - -By default when you use =embark-act= if you don't immediately select an -action, after a short delay Embark will pop up a buffer showing a list -of actions and their corresponding key bindings. If you are using -=embark-act= outside the minibuffer, Embark will also highlight the -current target. These behaviors are configurable via the variable -=embark-indicators=. Instead of selecting an action via its key binding, -you can select it by name with completion by typing =C-h= after -=embark-act=. - -Everything is easily configurable: determining the current target, -classifying it, and deciding which actions are offered for each type -in the classification. The above introduction just mentions part of -the default configuration. - -Configuring which actions are offered for a type is particularly easy -and requires no programming: the variable =embark-keymap-alist= -associates target types with variables containing keymaps, and those -keymaps containing bindings for the actions. (To examine the available -categories and their associated keymaps, you can use =C-h v -embark-keymap-alist= or customize that variable.) For example, in the -default configuration the type =file= is associated with the symbol -=embark-file-map=. That symbol names a keymap with single-letter key -bindings for common Emacs file commands, for instance =c= is bound to -=copy-file=. This means that if you are in the minibuffer after running -a command that prompts for a file, such as =find-file= or =rename-file=, -you can copy a file by running =embark-act= and then pressing =c=. - -These action keymaps are very convenient but not strictly necessary -when using =embark-act=: you can use any command that reads from the -minibuffer as an action and the target of the action will be inserted -at the first minibuffer prompt. After running =embark-act= all of your -key bindings and even =execute-extended-command= can be used to run a -command. For example, if you want to replace all occurrences of the -symbol at point, just use =M-%= as the action, there is no need to bind -=query-replace= in one of Embark's keymaps. Also, those action keymaps -are normal Emacs keymaps and you should feel free to bind in them -whatever commands you find useful as actions and want to be available -through convenient bindings. - -The actions in =embark-general-map= are available no matter what type -of completion you are in the middle of. By default this includes -bindings to save the current candidate in the kill ring and to insert -the current candidate in the previously selected buffer (the buffer -that was current when you executed a command that opened up the -minibuffer). - -Emacs's minibuffer completion system includes metadata indicating the -/category/ of what is being completed. For example, =find-file='s -metadata indicates a category of =file= and =switch-to-buffer='s metadata -indicates a category of =buffer=. Embark has the related notion of the -/type/ of a target for actions, and by default when category metadata -is present it is taken to be the type of minibuffer completion -candidates when used as targets. Emacs commands often do not set -useful category metadata so the [[https://github.com/minad/marginalia][Marginalia]] package, which supplies -this missing metadata, is highly recommended for use with Embark. - -Embark's default configuration has actions for the following target -types: files, buffers, symbols, packages, URLs, bookmarks, and as a -somewhat special case, actions for when the region is active. You can -read about the [[https://github.com/oantolin/embark/wiki/Default-Actions][default actions and their key bindings]] on the GitHub -project wiki. - -** The default action on a target - -Embark has a notion of default action for a target: - -- If the target is a minibuffer completion candidate, then the default - action is whatever command opened the minibuffer in the first place. - For example if you run =kill-buffer=, then the default action will be - to kill buffers. -- If the target comes from a regular buffer (i.e., not a minibuffer), - then the default action is whatever is bound to =RET= in the keymap of - actions for that type of target. For example, in Embark's default - configuration for a URL found at point the default action is - =browse-url=, because =RET= is bound to =browse-url= in the =embark-url-map= - keymap. - -To run the default action you can press =RET= after running =embark-act=. -Note that if there are several different targets at a given location, -each has its own default action, so first cycle to the target you want -and then press =RET= to run the corresponding default action. - -There is also =embark-dwim= which runs the default action for the first -target found. It's pretty handy in non-minibuffer buffers: with -Embark's default configuration it will: - -- Open the file at point. -- Open the URL at point in a web browser (using the =browse-url= - command). -- Compose a new email to the email address at point. -- In an Emacs Lisp buffer, if point is on an opening parenthesis or - right after a closing one, it will evaluate the corresponding - expression. -- Go to the definition of an Emacs Lisp function, variable or macro at - point. -- Find the file corresponding to an Emacs Lisp library at point. - -** Working with sets of possible targets - -Besides acting individually on targets, Embark lets you work -collectively on a set of target /candidates/. For example, while you are -in the minibuffer the candidates are simply the possible completions -of your input. Embark provides three main commands to work on candidate -sets: - -- The =embark-act-all= command runs the same action on each of the - current candidates. It is just like using =embark-act= on each - candidate in turn. (Because you can easily act on many more - candidates than you meant to, by default Embark asks you to confirm - uses of =embark-act-all=; you can turn this off by setting the user - option =embark-confirm-act-all= to =nil=.) - -- The =embark-collect= command produces a buffer listing all the current - candidates, for you to peruse and run actions on at your leisure. - The candidates are displayed as a list showing additional - annotations. If any of the candidates contain newlines, then - horizontal lines are used to separate candidates. - - The Embark Collect buffer is somewhat "dired-like": you can select - and deselect candidates through =embark-select= (available as an - action in =embark-act=, bound to =SPC=; but you could also give it a - global key binding). In an Embark Collect buffer =embark-act= is bound - to =a= and =embark-act-all= is bound to =A=; =embark-act-all= will act on - all currently marked candidates if there any, and will act on all - candidates if none are marked. In particular, this means that =a SPC= - will toggle whether the candidate at point is selected, and =A SPC= - will select all candidates if none are selected, or deselect all - selected candidates if there are some. - -- The =embark-export= command tries to open a buffer in an appropriate - major mode for the set of candidates. If the candidates are files - export produces a Dired buffer; if they are buffers, you get an - Ibuffer buffer; and if they are packages you get a buffer in - package menu mode. - - If you use the grepping commands from the [[https://github.com/minad/consult/][Consult]] package, - =consult-grep=, =consult-git-grep= or =consult-ripgrep=, then you should - install the =embark-consult= package, which adds support for exporting a - list of grep results to an honest grep-mode buffer, on which you can - even use [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep]] if you wish. - -When in doubt choosing between exporting and collecting, a good rule -of thumb is to always prefer =embark-export= since when an exporter to a -special major mode is available for a given type of target, it will be -more featureful than an Embark collect buffer, and if no such exporter -is configured the =embark-export= command falls back to the generic -=embark-collect=. - -These commands are always available as "actions" (although they do not -act on just the current target but on all candidates) for =embark-act= -and are bound to =A=, =S= (for "snapshot"), and =E=, respectively, in -=embark-general-map=. This means that you do not have to bind your own -key bindings for these (although you can, of course!), just a key -binding for =embark-act=. - -In Embark Collect or Embark Export buffers that were obtained by -running =embark-collect= or =embark-export= from within a minibuffer -completion session, =g= is bound to a command that restarts the -completion session, that is, the command that opened the minibuffer is -run again and the minibuffer contents restored. You can then interact -normally with the command, perhaps editing the minibuffer contents, -and, if you wish, you can rerun =embark-collect= or =embark-export= to get -an updated buffer. - -*** Selecting some targets to make an ad hoc candidate set - -The commands for working with sets of candidates just described, -namely =embark-act-all=, =embark-export= and =embark-collect= by default -work with all candidates defined in the current context. For example, -in the minibuffer they operate on all currently completion candidates, -or in a dired buffer they work on all marked files (or all files if -none are marked). Embark also has a notion of /selection/, where you can -accumulate an ad hoc list of targets for these commands to work on. - -The selection is controlled by using the =embark-select= action, bound -to =SPC= in =embark-general-map= so that it is always available (you can -also give =embark-select= a global key binding if you wish; when called -directly, not as an action for =embark-act=, it will select the first -target at point). Calling this action on a target toggles its -membership in the current buffer's Embark selection; that is, it adds -it to selection if not selected and removes it from the selection if -it was selected. Whenever the selection for a buffer is non-empty, the -commands =embark-act-all=, =embark-export= and =embark-collect= will act on -the selection. - -To deselect all selected targets, you can use the =embark-select= action -through =embark-act-all=, since this will run =embark-select= on each -member of the current selection. Similarly if no targets are selected -and you are in a minibuffer completion session, running =embark-select= -from =embark-act-all= will select all the current completion candidates. - -By default, whenever some targets are selected in the current buffer, -a count of selected targets appears in the mode line. This can be -turned off or customized through the =embark-selection-indicator= user -option. - -The selection functionality is supported in every buffer: - -- In the minibuffer this gives a convenient way to act on several - completion candidates that don't follow any simple pattern: just go - through the completions selecting the ones you want, then use - =embark-act-all=. For example, you could attach several files at once - to an email. -- For Embark Collect buffers this functionality enables a dired-like - workflow, in which you mark various candidates and apply an action - to all at once. (It supersedes a previous ad hoc dired-like - interface that was implemented only in Embark Collect buffers, with - a slightly different interface.) -- In a eww buffer you could use this to select various links you wish - to follow up on, and then collect them into a buffer. Similarly, - while reading Emacs's info manual you could select some symbols you - want to read more about and export them to an =apropos-mode= buffer. -- You can use selections in regular text or programming buffers to do - complex editing operations. For example, if you have three - paragraphs scattered over a file and you want to bring them - together, you can select each one, insert them all somewhere and - finally delete all of them (from their original locations). - -*** =embark-live= a live-updating variant of =embark-collect= - -Finally, there is also an =embark-live= variant of the =embark-collect= -command which automatically updates the collection after each change -in the source buffer. Users of a completion UI that automatically -updates and displays the candidate list (such as Vertico, Icomplete, -Fido-mode, or MCT) will probably not want to use -=embark-live= from the minibuffer as they will then have two live -updating displays of the completion candidates! - -A more likely use of =embark-live= is to be called from a regular buffer -to display a sort of live updating "table of contents" for the buffer. -This depends on having appropriate candidate collectors configured in -=embark-candidate-collectors=. There are not many in Embark's default -configuration, but you can try this experiment: open a dired buffer in -a directory that has very many files, mark a few, and run =embark-live=. -You'll get an Embark Collect buffer containing only the marked files, -which updates as you mark or unmark files in dired. To make -=embark-live= genuinely useful other candidate collectors are required. -The =embark-consult= package (documented near the end of this manual) -contains a few: one for imenu items and one for outline headings as -used by =outline-minor-mode=. Those collectors really do give -=embark-live= a table-of-contents feel. - -** Switching to a different command without losing what you've typed - -Embark also has the =embark-become= command which is useful for when -you run a command, start typing at the minibuffer and realize you -meant a different command. The most common case for me is that I run -=switch-to-buffer=, start typing a buffer name and realize I haven't -opened the file I had in mind yet! I'll use this situation as a -running example to illustrate =embark-become=. When this happens I can, -of course, press =C-g= and then run =find-file= and open the file, but -this requires retyping the portion of the file name you already -typed. This process can be streamlined with =embark-become=: while still -in the =switch-to-buffer= you can run =embark-become= and effectively -make the =switch-to-buffer= command become =find-file= for this run. - -You can bind =embark-become= to a key in =minibuffer-local-map=, but it is -also available as an action under the letter =B= (uppercase), so you -don't need a binding if you already have one for =embark-act=. So, -assuming I have =embark-act= bound to, say, =C-.=, once I realize I -haven't open the file I can type =C-. B C-x C-f= to have -=switch-to-buffer= become =find-file= without losing what I have already -typed in the minibuffer. - -But for even more convenience, =embark-become= offers shorter key -bindings for commands you are likely to want the current command to -become. When you use =embark-become= it looks for the current command in -all keymaps named in the list =embark-become-keymaps= and then activates -all keymaps that contain it. For example, the default value of -=embark-become-keymaps= contains a keymap =embark-become-file+buffer-map= -with bindings for several commands related to files and buffers, in -particular, it binds =switch-to-buffer= to =b= and =find-file= to =f=. So when -I accidentally try to switch to a buffer for a file I haven't opened -yet, =embark-become= finds that the command I ran, =switch-to-buffer=, is -in the keymap =embark-become-file+buffer-map=, so it activates that -keymap (and any others that also contain a binding for -=switch-to-buffer=). The end result is that I can type =C-. B f= to -switch to =find-file=. - -* Quick start - -The easiest way to install Embark is from GNU ELPA, just run =M-x -package-install RET embark RET=. (It is also available on MELPA.) It is -highly recommended to also install [[https://github.com/minad/marginalia][Marginalia]] (also available on GNU -ELPA), so that Embark can offer you preconfigured actions in more -contexts. For =use-package= users, the following is a very reasonable -starting configuration: - -#+begin_src emacs-lisp - (use-package marginalia - :ensure t - :config - (marginalia-mode)) - - (use-package embark - :ensure t - - :bind - (("C-." . embark-act) ;; pick some comfortable binding - ("C-;" . embark-dwim) ;; good alternative: M-. - ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' - - :init - - ;; Optionally replace the key help with a completing-read interface - (setq prefix-help-command #'embark-prefix-help-command) - - ;; Show the Embark target at point via Eldoc. You may adjust the - ;; Eldoc strategy, if you want to see the documentation from - ;; multiple providers. Beware that using this can be a little - ;; jarring since the message shown in the minibuffer can be more - ;; than one line, causing the modeline to move up and down: - - ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) - ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) - - :config - - ;; Hide the mode line of the Embark live/completions buffers - (add-to-list 'display-buffer-alist - '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" - nil - (window-parameters (mode-line-format . none))))) - - ;; Consult users will also want the embark-consult package. - (use-package embark-consult - :ensure t ; only need to install it, embark loads it after consult if found - :hook - (embark-collect-mode . consult-preview-at-point-mode)) -#+end_src - -About the suggested key bindings for =embark-act= and =embark-dwim=: -- Those key bindings are unlikely to work in the terminal, but - terminal users are probably well aware of this and will know to - select different bindings. -- The suggested =C-.= binding is used by default in (at least some - installations of) GNOME to input emojis, and Emacs doesn't even get - a chance to respond to the binding. You can select a different key - binding for =embark-act= or use =ibus-setup= to change the shortcut for - emoji insertion (Emacs 29 will likely use =C-x 8 e e=, in case you - want to set the same one system-wide). -- The suggested alternative of =M-.= for =embark-dwim= is bound by default - to =xref-find-definitions=. That is a very useful command but - overwriting it with =embark-dwim= is sensible since in Embark's - default configuration, =embark-dwim= will also find the definition of - the identifier at point. (Note that =xref-find-definitions= with a - prefix argument prompts you for an identifier, =embark-dwim= does not - cover this case). - -Other Embark commands such as =embark-act-all=, =embark-become=, -=embark-collect=, and =embark-export= can be run through =embark-act= as -actions bound to =A=, =B=, =S= (for "snapshot"), and =E= respectively, and -thus don't really need a dedicated key binding, but feel free to bind -them directly if you so wish. If you do choose to bind them directly, -you'll probably want to bind them in =minibuffer-local-map=, since they -are most useful in the minibuffer (in fact, =embark-become= only works -in the minibuffer). - -The command =embark-dwim= executes the default action at point. Another good -keybinding for =embark-dwim= is =M-.= since =embark-dwim= acts like -=xref-find-definitions= on the symbol at point. =C-.= can be seen as a -right-click context menu at point and =M-.= acts like left-click. The -keybindings are mnemonic, both act at the point (=.=). - -Embark needs to know what your minibuffer completion system considers -to be the list of candidates and which one is the current candidate. -Embark works out of the box if you use Emacs's default tab completion, -the built-in =icomplete-mode= or =fido-mode=, or the third-party packages -[[https://github.com/minad/vertico][Vertico]] or [[https://github.com/abo-abo/swiper][Ivy]]. - -If you are a [[https://emacs-helm.github.io/helm/][Helm]] or [[https://github.com/abo-abo/swiper][Ivy]] user you are unlikely to want Embark since -those packages include comprehensive functionality for acting on -minibuffer completion candidates. (Embark does come with Ivy -integration despite this.) - -* Advanced configuration -** Showing information about available targets and actions - -By default, if you run =embark-act= and do not immediately select an -action, after a short delay Embark will pop up a buffer called =*Embark -Actions*= containing a list of available actions with their key -bindings. You can scroll that buffer with the mouse of with the usual -commands =scroll-other-window= and =scroll-other-window-down= (bound by -default to =C-M-v= and =C-M-S-v=). - -That functionality is provided by the =embark-mixed-indicator=, but -Embark has other indicators that can provide information about the -target and its type, what other targets you can cycle to, and which -actions have key bindings in the action map for the current type of -target. Any number of indicators can be active at once and the user -option =embark-indicators= should be set to a list of the desired -indicators. - -Embark comes with the following indicators: - -- =embark-minimal-indicator=: shows a messages in the echo area or - minibuffer prompt showing the current target and the types of all - targets starting with the current one. - -- =embark-highlight-indicator=: highlights the target at point; on by - default. - -- =embark-verbose-indicator=: displays a table of actions and their key - bindings in a buffer; this is not on by default, in favor of the - mixed indicator described next. - -- =embark-mixed-indicator=: starts out by behaving as the minimal - indicator but after a short delay acts as the verbose indicator; - this is on by default. - -- =embark-isearch-highlight-indicator=: this only does something when - the current target is the symbol at point, in which case it - lazily highlights all occurrences of that symbol in the current - buffer, like isearch; also on by default. - -Users of the popular [[https://github.com/justbur/emacs-which-key][which-key]] package may prefer to use the -=embark-which-key-indicator= from the [[https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt][Embark wiki]]. Just copy its -definition from the wiki into your configuration and customize the -=embark-indicators= user option to exclude the mixed and verbose -indicators and to include =embark-which-key-indicator=. - -If you use [[https://github.com/minad/vertico][Vertico]], there is an even easier way to get a -=which-key=-like display that also lets you use completion to narrow -down the list of alternatives, described at the end of the next -section. - -** Selecting commands via completions instead of key bindings - -As an alternative to reading the list of actions in the verbose or -mixed indicators (see the previous section for a description of -these), you can press the =embark-help-key=, which is =C-h= by default -(but you may prefer =?= to free up =C-h= for use as a prefix) after -running =embark-act=. Pressing the help key will prompt you for the name -of an action with completion (but feel free to enter a command that is -not among the offered candidates!), and will also remind you of the -key bindings. You can press =embark-keymap-prompter-key=, which is =@= by -default, at the prompt and then one of the key bindings to enter the -name of the corresponding action. - -You may think that with the =*Embark Actions*= buffer popping up to -remind you of the key bindings you'd never want to use completion to -select an action by name, but personally I find that typing a small -portion of the action name to narrow down the list of candidates feels -significantly faster than visually scanning the entire list of actions. - -If you find you prefer selecting actions that way, you can configure -embark to always prompt you for actions by setting the variable -=embark-prompter= to =embark-completing-read-prompter=. - -On the other hand, you may wish to continue using key bindings for the -actions you perform most often, and to use completion only to explore -what further actions are available or when you've forgotten a key -binding. In that case, you may prefer to use the minimal indicator, -which does not pop-up an =*Embark Actions*= buffer at all, and to use -the =embark-help-key= whenever you need help. This unobtrusive setup is -achieved with the following configuration: - -#+begin_src emacs-lisp - (setq embark-indicators - '(embark-minimal-indicator ; default is embark-mixed-indicator - embark-highlight-indicator - embark-isearch-highlight-indicator)) -#+end_src - -[[https://github.com/minad/vertico][Vertico]] users may wish to configure a grid display for the actions and -key-bindings, reminiscent of the popular package [[https://github.com/justbur/emacs-which-key][which-key]], but, of -course, enhanced by the use of completion to narrow the list of -commands. In order to get the grid display, put the following in your -Vertico configuration: - -#+begin_src emacs-lisp - (add-to-list 'vertico-multiform-categories '(embark-keybinding grid)) - (vertico-multiform-mode) -#+end_src - -This will make the available keys be shown in a compact grid like in -=which-key=. The =vertico-multiform-mode= also enables keys such as =M-V=, -=M-G=, =M-B=, and =M-U= for manually switching between layouts in Vertico -buffers. - -*** Selecting commands via completion outside of Embark - -If you like this completion interface for exploring key bindings for -Embark actions, you may want to use it elsewhere in Emacs. You can use -Embark's completion-based command prompter to list: - -- key bindings under a prefix, -- local key bindings, or -- all key bindings. - -To use it for key bindings under a prefix (you can use this to replace -the =which-key= package, for example), use this configuration: - -#+begin_src emacs-lisp - (setq prefix-help-command #'embark-prefix-help-command) -#+end_src - -Now, when you have started on a prefix sequence such as =C-x= or =C-c=, -pressing =C-h= will bring up the Embark version of the built-in -=prefix-help-command=, which will list the keys under that prefix and -their bindings, and lets you select the one you wanted with completion, -or by key binding if you press =embark-keymap-prompter-key=. - -To list local or global key bindings, use the command =embark-bindings=. -You can bind that to =C-h b=, which is the default key binding for the -built-in =describe-bindings= command, which this command can replace. By -default, =embark-bindings= lists local key bindings, typically those -bound in the major mode keymap; to get global bindings as well, call -it with a =C-u= prefix argument. - -** Quitting the minibuffer after an action - -By default, if you call =embark-act= from the minibuffer it quits the -minibuffer after performing the action. You can change this by setting -the user option =embark-quit-after-action= to =nil=. Having =embark-act= /not/ -quit the minibuffer can be useful to turn commands into little "thing -managers". For example, you can use =find-file= as a little file manager -or =describe-package= as a little package manager: you can run those -commands, perform a series of actions, and then quit the command. - -If you want to control the quitting behavior in a fine-grained manner -depending on the action, you can set =embark-quit-after-action= to an -alist, associating commands to either =t= for quitting or =nil= for not -quitting. When using an alist, you can use the special key =t= to -specify the default behavior. For example, to specify that by default -actions should not quit the minibuffer but that using =kill-buffer= as -an action should quit, you can use the following configuration: - -#+begin_src emacs-lisp - (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) -#+end_src - -The variable =embark-quit-after-action= only specifies a default, that -is, it only controls whether or not =embark-act= quits the minibuffer -when you call it without a prefix argument, and you can select the -opposite behavior to what the variable says by calling =embark-act= with -=C-u=. Also note that both the variable =embark-quit-after-action= and =C-u= -have no effect when you call =embark-act= outside the minibuffer. - -If you find yourself using the quitting and non-quitting variants of -=embark-act= about equally often, independently of the action, you may -prefer to simply have separate commands for them instead of a single -command that you call with =C-u= half the time. You could, for example, -keep the default exiting behavior of =embark-act= and define a -non-quitting version as follows: - -#+begin_src emacs-lisp - (defun embark-act-noquit () - "Run action but don't quit the minibuffer afterwards." - (interactive) - (let ((embark-quit-after-action nil)) - (embark-act))) -#+end_src - -** Running some setup after injecting the target - -You can customize what happens after the target is inserted at the -minibuffer prompt of an action. There are -=embark-target-injection-hooks=, that are run by default after injecting -the target into the minibuffer. The variable -=embark-target-injection-hooks= is an alist associating commands to -their setup hooks. There are two special keys: if no setup hook is -specified for a given action, the hook associated to =t= is run; and the -hook associated to =:always= is run regardless of the action. (This -variable used to have the less explicit name of -=embark-setup-action-hooks=, so please update your configuration.) - -For example, consider using =shell-command= as an action during file -completion. It would be useful to insert a space before the target -file name and to leave the point at the beginning, so you can -immediately type the shell command to run on that file. That's why in -Embark's default configuration there is an entry in -=embark-target-injection-hooks= associating =shell-command= to a hook that -includes =embark--shell-prep=, a simple helper function that quotes all -the spaces in the file name, inserts an extra space at the beginning -of the line and leaves point to the left of it. - -Now, the preparation that =embark--shell-prep= does would be useless if -Embark did what it normally does after it inserts the target of the -action at the minibuffer prompt, which is to "press =RET=" for you, -accepting the target as is; if Embark did that for =shell-command= you -wouldn't get a chance to type in the command to execute! That is why -in Embark's default configuration the entry for =shell-command= in -=embark-target-injection-hooks= also contains the function -=embark--allow-edit=. - -Embark used to have a dedicated variable =embark-allow-edit-actions= to -which you could add commands for which Embark should forgo pressing -=RET= for you after inserting the target. Since its effect can also be -achieved via the general =embark-target-injection-hooks= mechanism, that -variable has been removed to simplify Embark. Be sure to update your -configuration; if you had something like: - -#+begin_src emacs-lisp - (add-to-list 'embark-allow-edit-actions 'my-command) -#+end_src - -you should replace it with: - -#+begin_src emacs-lisp - (push 'embark--allow-edit - (alist-get 'my-command embark-target-injection-hooks)) -#+end_src - - -Also note that while you could abuse =embark--allow-edit= so that you -have to confirm "dangerous" actions such as =delete-file=, it is better -to implement confirmation by adding the =embark--confirm= function to -the appropriate entry of a different hook alist, namely, -=embark-pre-action-hooks=. - -Besides =embark--allow-edit=, Embark comes with another function that is -of general utility in action setup hooks: =embark--ignore-target=. Use -it for commands that do prompt you in the minibuffer but for which -inserting the target would be inappropriate. This is not a common -situation but does occasionally arise. For example it is used by -default for =shell-command-on-region=: that command is used as an action -for region targets, and it prompts you for a shell command; you -typically do /not/ want the target, that is the contents of the region, -to be entered at that prompt! - -** Running hooks before, after or around an action - -Embark has three variables, =embark-pre-action-hooks=, -=embark-post-action-hooks= and =embark-around-action-hooks=, which are -alists associating commands to hooks that should run before or after -or as around advice for the command when used as an action. As with -=embark-target-injection-hooks=, there are two special keys for the -alists: =t= designates the default hook to run when no specific hook is -specified for a command; and the hook associated to =:always= runs -regardless. - -The default values of those variables are fairly extensive, adding -creature comforts to make running actions a smooth experience. Embark -comes with several functions intended to be added to these hooks, and -used in the default values of =embark-pre-action-hooks=, -=embark-post-action-hooks= and =embark-around-action-hooks=. - -For pre-action hooks: - -- =embark--confirm= :: Prompt the user for confirmation before executing - the action. This is used be default for commands deemed "dangerous", - or, more accurately, hard to undo, such as =delete-file= and - =kill-buffer=. - -- =embark--unmark-target= :: Unmark the active region. Use this for - commands you want to act on the region contents but without the - region being active. The default configuration uses this function as - a pre-action hook for =occur= and =query-replace=, for example, so that - you can use them as actions with region targets to search the whole - buffer for the text contained in the region. Without this pre-action - hook using =occur= as an action for a region target would be - pointless: it would search for the the region contents /in the - region/, (typically, due to the details of regexps) finding only one - match! - -- =embark--beginning-of-target= :: Move to the beginning of the target - (for targets that report bounds). This is used by default for - backward motion commands such as =backward-sexp=, so that they don't - accidentally leave you on the current target. - -- =embark--end-of-target= :: Move to the end of the target. This is used - similarly to the previous function, but also for commands that act - on the last s-expression like =eval-last-sexp=. This allow you to act - on an s-expression from anywhere inside it and still use - =eval-last-sexp= as an action. - -- =embark--xref-push-markers= :: Push the current location on the xref - marker stack. Use this for commands that take you somewhere and for - which you'd like to be able to come back to where you were using - =xref-pop-marker-stack=. This is used by default for =find-library=. - -For post-action hooks: - -- =embark--restart= :: Restart the command currently prompting in the - minibuffer, so that the list of completion candidates is updated. - This is useful as a post action hook for commands that delete or - rename a completion candidate; for example the default value of - =embark-post-action-hooks= uses it for =delete-file=, =kill-buffer=, - =rename-file=, =rename-buffer=, etc. - -For around-action hooks: - -- =embark--mark-target= :: Save existing mark and point location, mark - the target and run the action. Most targets at point outside the - minibuffer report which region of the buffer they correspond to - (this is the information used by =embark-highlight-indicator= to - know what portion of the buffer to highlight); this function marks - that region. It is useful as an around action hook for commands that - expect a region to be marked, for example, it is used by default for - =indent-region= so that it works on s-expression targets, or for - =fill-region= so that it works on paragraph targets. - -- =embark--cd= :: Run the action with =default-directory= set to the - directory associated to the current target. The target should be of - type =file=, =buffer=, =bookmark= or =library=, and the associated directory - is what you'd expect in each case. - -- =embark--narrow-to-target= :: Run the action with buffer narrowed to - current target. Use this as an around hook to localize the effect of - actions that don't already work on just the region. In the default - configuration it is used for =repunctuate-sentences=. - -- =embark--save-excursion= :: Run the action restoring point at the end. - The current default configuration doesn't use this but it is - available for users. - -** Creating your own keymaps - -All internal keymaps are defined with the standard helper macro -=defvar-keymap=. For example a simple version of the file action keymap -could be defined as follows: - -#+BEGIN_SRC emacs-lisp - (defvar-keymap embark-file-map - :doc "Example keymap with a few file actions" - :parent embark-general-map - "d" #'delete-file - "r" #'rename-file - "c" #'copy-file) -#+END_SRC - -These action keymaps are perfectly normal Emacs -keymaps. You may want to inherit from the =embark-general-map= if you -want to access the default Embark actions. Note that =embark-collect= -and =embark-export= are also made available via =embark-general-map=. - -** Defining actions for new categories of targets - -It is easy to configure Embark to provide actions for new types of -targets, either in the minibuffer or outside it. I present below two -very detailed examples of how to do this. At several points I'll -explain more than one way to proceed, typically with the easiest -option first. I include the alternative options since there will be -similar situations where the easiest option is not available. - -*** New minibuffer target example - tab-bar tabs - -As an example, take the new [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html][tab bars]] from Emacs 27. I'll explain how -to configure Embark to offer tab-specific actions when you use the -tab-bar-mode commands that mention tabs by name. The configuration -explained here is now built-in to Embark (and Marginalia), but it's -still a good self-contained example. In order to setup up tab actions -you would need to: (1) make sure Embark knows those commands deal with -tabs, (2) define a keymap for tab actions and configure Embark so it -knows that's the keymap you want. - -**** Telling Embark about commands that prompt for tabs by name - -For step (1), it would be great if the =tab-bar-mode= commands reported -the completion category =tab= when asking you for a tab with -completion. (All built-in Emacs commands that prompt for file names, -for example, do have metadata indicating that they want a =file=.) They -do not, unfortunately, and I will describe a couple of ways to deal -with this. - -Maybe the easiest thing is to configure [[https://github.com/minad/marginalia][Marginalia]] to enhance those -commands. All of the =tab-bar-*-tab-by-name= commands have the words -"tab by name" in the minibuffer prompt, so you can use: - -#+begin_src emacs-lisp - (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) -#+end_src - -That's it! But in case you are ever in a situation where you don't -already have commands that prompt for the targets you want, I'll -describe how writing your own command with appropriate =category= -metadata looks: - -#+begin_src emacs-lisp - (defun my-select-tab-by-name (tab) - (interactive - (list - (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - (tab-bar-tabs)) - (user-error "No tabs found")))) - (completing-read - "Tabs: " - (lambda (string predicate action) - (if (eq action 'metadata) - '(metadata (category . tab)) - (complete-with-action - action tab-list string predicate))))))) - (tab-bar-select-tab-by-name tab)) -#+end_src - -As you can see, the built-in support for setting the category -meta-datum is not very easy to use or pretty to look at. To help with -this I recommend the =consult--read= function from the excellent -[[https://github.com/minad/consult/][Consult]] package. With that function we can rewrite the command as -follows: - -#+begin_src emacs-lisp - (defun my-select-tab-by-name (tab) - (interactive - (list - (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - (tab-bar-tabs)) - (user-error "No tabs found")))) - (consult--read tab-list - :prompt "Tabs: " - :category 'tab)))) - (tab-bar-select-tab-by-name tab)) -#+end_src - -Much nicer! No matter how you define the =my-select-tab-by-name= -command, the first approach with Marginalia and prompt detection has -the following advantages: you get the =tab= category for all the -=tab-bar-*-bar-by-name= commands at once, also, you enhance built-in -commands, instead of defining new ones. - -**** Defining and configuring a keymap for tab actions - - Let's say we want to offer select, rename and close actions for tabs - (in addition to Embark general actions, such as saving the tab name to - the kill-ring, which you get for free). Then this will do: - - #+begin_src emacs-lisp - (defvar-keymap embark-tab-actions - :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." - :parent embark-general-map - "s" #'tab-bar-select-tab-by-name - "r" #'tab-bar-rename-tab-by-name - "k" #'tab-bar-close-tab-by-name) - - (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) - #+end_src - - What if after using this for a while you feel closing the tab - without confirmation is dangerous? You have a couple of options: - - 1. You can keep using the =tab-bar-close-tab-by-name= command, but have - Embark ask you for confirmation: - #+begin_src emacs-lisp - (push #'embark--confirm - (alist-get 'tab-bar-close-tab-by-name - embark-pre-action-hooks)) - #+end_src - - 2. You can write your own command that prompts for confirmation and - use that instead of =tab-bar-close-tab-by-name= in the above keymap: - #+begin_src emacs-lisp - (defun my-confirm-close-tab-by-name (tab) - (interactive "sTab to close: ") - (when (y-or-n-p (format "Close tab '%s'? " tab)) - (tab-bar-close-tab-by-name tab))) - #+end_src - - Notice that this is a command you can also use directly from =M-x= - independently of Embark. Using it from =M-x= leaves something to be - desired, though, since you don't get completion for the tab names. - You can fix this if you wish as described in the previous section. - -*** New target example in regular buffers - short Wikipedia links - -Say you want to teach Embark to treat text of the form -=wikipedia:Garry_Kasparov= in any regular buffer as a link to Wikipedia, -with actions to open the Wikipedia page in eww or an external browser -or to save the URL of the page in the kill-ring. We can take advantage -of the actions that Embark has preconfigured for URLs, so all we need -to do is teach Embark that =wikipedia:Garry_Kasparov= stands for the URL -=https://en.wikipedia.org/wiki/Garry_Kasparov=. - -You can be as fancy as you want with the recognized syntax. Here, to -keep the example simple, I'll assume the link matches the regexp -=wikipedia:[[:alnum:]_]+=. We will write a function that looks for a -match surrounding point, and returns a dotted list of the form ='(url -URL-OF-THE-PAGE START . END)= where =START= and =END= are the buffer -positions bounding the target, and are used by Embark to highlight it -if you have =embark-highlight-indicator= included in the list -=embark-indicators=. (There are a couple of other options for the return -value of a target finder: the bounding positions are optional and a -single target finder is allowed to return multiple targets; see the -documentation for =embark-target-finders= for details.) - -#+begin_src emacs-lisp - (defun my-short-wikipedia-link () - "Target a link at point of the form wikipedia:Page_Name." - (save-excursion - (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) - (end (progn (skip-chars-forward "[:alnum:]_:") (point))) - (str (buffer-substring-no-properties start end))) - (save-match-data - (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) - `(url - ,(format "https://en.wikipedia.org/wiki/%s" - (match-string 1 str)) - ,start . ,end)))))) - - (add-to-list 'embark-target-finders 'my-short-wikipedia-link) -#+end_src - -* How does Embark call the actions? - - Embark actions are normal Emacs commands, that is, functions with an - interactive specification. In order to execute an action, Embark - calls the command with =call-interactively=, so the command reads user - input exactly as if run directly by the user. For example the - command may open a minibuffer and read a string - (=read-from-minibuffer=) or open a completion interface - (=completing-read=). If this happens, Embark takes the target string - and inserts it automatically into the minibuffer, simulating user - input this way. After inserting the string, Embark exits the - minibuffer, submitting the input. (The immediate minibuffer exit can - be disabled for specific actions in order to allow editing the - input; this is done by adding the =embark--allow-edit= function to the - appropriate entry of =embark-target-injection-hooks=). Embark inserts - the target string at the first minibuffer opened by the action - command, and if the command happens to prompt the user for input - more than once, the user still interacts with the second and further - prompts in the normal fashion. Note that if a command does not - prompt the user for input in the minibuffer, Embark still allows you - to use it as an action, but of course, never inserts the target - anywhere. (There are plenty of examples in the default configuration - of commands that do not prompt the user bound to keys in the action - maps, most of the region actions, for instance.) - - This is how Embark manages to reuse normal commands as actions. The - mechanism allows you to use as Embark actions commands that were not - written with Embark in mind (and indeed almost all actions that are - bound by default in Embark's action keymaps are standard Emacs - commands). It also allows you to write new custom actions in such a - way that they are useful even without Embark. - - Staring from version 28.1, Emacs has a variable - =y-or-n-p-use-read-key=, which when set to =t= causes =y-or-n-p= to use - =read-key= instead of =read-from-minibuffer=. Setting - =y-or-n-p-use-read-key= to =t= is recommended for Embark users because - it keeps Embark from attempting to insert the target at a =y-or-n-p= - prompt, which would almost never be sensible. Also consider this as - a warning to structure your own action commands so that if they use - =y-or-n-p=, they do so only after the prompting for the target. - - Here is a simple example illustrating the various ways of reading - input from the user mentioned above. Bind the following commands to - the =embark-symbol-map= to be used as actions, then put the point on - some symbol and run them with =embark-act=: - - #+begin_src emacs-lisp - (defun example-action-command1 () - (interactive) - (message "The input was `%s'." (read-from-minibuffer "Input: "))) - - (defun example-action-command2 (arg input1 input2) - (interactive "P\nsInput 1: \nsInput 2: ") - (message "The first input %swas `%s', and the second was `%s'." - (if arg "truly " "") - input1 - input2)) - - (defun example-action-command3 () - (interactive) - (message "Your selection was `%s'." - (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) - - (defun example-action-command4 () - (interactive) - (message "I don't prompt you for input and thus ignore the target!")) - - (keymap-set embark-symbol-map "X 1" #'example-action-command1) - (keymap-set embark-symbol-map "X 2" #'example-action-command2) - (keymap-set embark-symbol-map "X 3" #'example-action-command3) - (keymap-set embark-symbol-map "X 4" #'example-action-command4) - #+end_src - - Also note that if you are using the key bindings to call actions, - you can pass prefix arguments to actions in the normal way. For - example, you can use =C-u X2= with the above demonstration actions to - make the message printed by =example-action-command2= more emphatic. - This ability to pass prefix arguments to actions is useful for some - actions in the default configuration, such as - =embark-shell-command-on-buffer=. - -** Non-interactive functions as actions - - Alternatively, Embark does support one other type of action: a - non-interactive function of a single argument. The target is passed - as argument to the function. For example: - - #+begin_src emacs-lisp - (defun example-action-function (target) - (message "The target was `%s'." target)) - - (keymap-set embark-symbol-map "X 4" #'example-action-function) - #+end_src - - Note that normally binding non-interactive functions in a keymap is - useless, since when attempting to run them using the key binding you - get an error message similar to "Wrong type argument: commandp, - example-action-function". In general it is more flexible to write - any new Embark actions as commands, that is, as interactive - functions, because that way you can also run them directly, without - Embark. But there are a couple of reasons to use non-interactive - functions as actions: - - 1. You may already have the function lying around, and it is - convenient to simply reuse it. - - 2. For command actions the targets can only be simple string, with - no text properties. For certain advanced uses you may want the - action to receive a string /with/ some text properties, or even a - non-string target. - -* Embark, Marginalia and Consult - -Embark cooperates well with the [[https://github.com/minad/marginalia][Marginalia]] and [[https://github.com/minad/consult][Consult]] packages. -Neither of those packages is a dependency of Embark, but both are -highly recommended companions to Embark, for opposite reasons: -Marginalia greatly enhances Embark's usefulness, while Embark can help -enhance Consult. - -In the remainder of this section I'll explain what exactly Marginalia -does for Embark, and what Embark can do for Consult. - -** Marginalia - -Embark comes with actions for symbols (commands, functions, variables -with actions such as finding the definition, looking up the -documentation, evaluating, etc.) in the =embark-symbol-map= keymap, and -for packages (actions like install, delete, browse url, etc.) in the -=embark-package-keymap=. - -Unfortunately Embark does not automatically offers you these keymaps -when relevant, because many built-in Emacs commands don't report -accurate category metadata. For example, a command like -=describe-package=, which reads a package name from the minibuffer, -does not have metadata indicating this fact. - -In an earlier Embark version, there were functions to supply this -missing metadata, but they have been moved to Marginalia, which -augments many Emacs command to report accurate category metadata. -Simply activating =marginalia-mode= allows Embark to offer you the -package and symbol actions when appropriate again. Candidate -annotations in the Embark collect buffer are also provided by the -Marginalia package: - -- If you install Marginalia and activate =marginalia-mode=, Embark - Collect buffers will use the Marginalia annotations automatically. - -- If you don't install Marginalia, you will see only the annotations - that come with Emacs (such as key bindings in =M-x=, or the unicode - characters in =C-x 8 RET=). - -** Consult - -The excellent Consult package provides many commands that use -minibuffer completion, via the =completing-read= function; plenty of its -commands can be considered enhanced versions of built-in Emacs -commands, and some are completely new functionality. One common -enhancement provided in all commands for which it makes sense is -preview functionality, for example =consult-buffer= will show you a -quick preview of a buffer before you actually switch to it. - -If you use both Consult and Embark you should install the -=embark-consult= package which provides integration between the two. It -provides exporters for several Consult commands and also tweaks the -behavior of many Consult commands when used as actions with =embark-act= -in subtle ways that you may not even notice, but make for a smoother -experience. You need only install it to get these benefits: Embark -will automatically load it after Consult if found. - -The =embark-consult= package provides the following exporters: - -- You can use =embark-export= from =consult-line=, =consult-outline=, or - =consult-mark= to obtain an =occur-mode= buffer. As with the built-in - =occur= command you use that buffer to jump to a match and after that, - you can then use =next-error= and =previous-error= to navigate to other - matches. You can also press =e= to activate =occur-edit-mode= and edit - the matches in place! - -- You can export from any of the Consult asynchronous search commands, - =consult-grep=, =consult-git-grep=, or =consult-ripgrep= to get a - =grep-mode= buffer. Here too you can use =next-error= and =previous-error= - to navigate among matches, and, if you install the [[http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el ][wgrep]] package, - you can use it to edit the matches in place. - -In both cases, pressing =g= will rerun the Consult command you had -exported from and re-enter the input you had typed (which is similar -to reverting but a little more flexible). You can then proceed to -re-export if that's what you want, but you can also edit the input -changing the search terms or simply cancel if you see you are done -with that search. - -The =embark-consult= also contains some candidates collectors that allow -you to run =embark-live= to get a live-updating table of contents for -your buffer: - -- =embark-consult-outline-candidates= produces the outline headings of - the current buffer, using =consult-outline=. -- =embark-consult-imenu-candidates= produces the imenu items of - the current buffer, using =consult-imenu=. -- =embark-consult-imenu-or-outline-candidates= is a simple combination - of the two previous functions: it produces imenu items in buffers - deriving from =prog-mode= and otherwise outline headings. - -The way to configure =embark-live= (or =embark-collect= and =embark-export= -for that matter) to use one of these function is to add it at the end -of the =embark-candidate-collectors= list. The =embark-consult= package by -default adds the last one, which seems to be the most sensible -default. - -Besides those exporters and candidate collectors, the =embark-consult= -package provides many subtle tweaks and small integrations between -Embark and Consult. Some examples are: - -- When used as actions, the asynchronous search commands will search - only the files associated to the targets: if the targets /are/ files, - it searches those files; for buffers it will search either the - associated file if there is one, else all files in the buffer's - =default-directory=; for bookmarks it will search the file they point - to, same for Emacs Lisp libraries. This is particularly powerful - when using =embark-act-all= to act on multiple files at once, for - example you can use =consult-find= to search among file /names/ and then - =embark-act-all= and =consult-grep= to search within the matching files. - - - For all other target types, those that do not have a sensible - notion of associated file, a Consult search command (asynchronous - or not) will search for the text of the target but leave the - minibuffer open so you can interact with the Consult command. - -- =consult-imenu= will search for the target and take you directly to - the location if it matches a unique imenu entry, otherwise it will - leave the minibuffer open so you can navigate among the matches. - -* Related Packages - -There are several packages that offer functionality similar -to Embark's. - -- Acting on minibuffer completion candidates :: The popular Ivy and - Helm packages have support for acting on the completion candidates - of commands written using their APIs, and there is an extensive - ecosystem of packages meant for Helm and for Ivy (the Ivy ones - usually have "counsel" in the name) providing commands and - appropriate actions. -- Acting on things at point :: The built-in =context-menu-mode= provides - a mouse-driven context-sensitive configurable menu. The =do-at-point= - package by Philip Kaludercic (available on GNU ELPA), on the other - hand is keyboard-driven. -- Collecting completion candidates into a buffer :: The Ivy package - has the command =ivy-occur= which is similar to =embark-collect=. As - with Ivy actions, =ivy-occur= only works for commands written using - the Ivy API. - -* Resources - -If you want to learn more about how others have used Embark here are -some links to read: - -- [[https://karthinks.com/software/fifteen-ways-to-use-embark/][Fifteen ways to use Embark]], a blog post by Karthik Chikmagalur. -- [[https://protesilaos.com/dotemacs/][Protesilaos Stavrou's dotemacs]], look for the section called - "Extended minibuffer actions and more (embark.el and - prot-embark.el)" - -And some videos to watch: - -- [[https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/][Embark and my extras]] by Protesilaos Stavrou. -- [[https://youtu.be/qpoQiiinCtY][Embark -- Key features and tweaks]] by Raoul Comninos on the - Emacs-Elements YouTube channel. -- [[https://youtu.be/WsxXr1ncukY][Livestreamed: Adding an Embark context action to send a stream - message]] by Sacha Chua. -- [[https://youtu.be/qk2Is_sC8Lk][System Crafters Live! - The Many Uses of Embark]] by David Wilson. -- [[https://youtu.be/5ffb2at2d7w][Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark]] by - Mike Zamansky. - -* Contributions - -Contributions to Embark are very welcome. There is a [[https://github.com/oantolin/embark/issues/95][wish list]] for -actions, target finders, candidate collectors and exporters. For other -ideas you have for Embark, feel free to open an issue on the [[https://github.com/oantolin/embark/issues][issue -tracker]]. Any neat configuration tricks you find might be a good fit -for the [[https://github.com/oantolin/embark/wiki][wiki]]. - -Code contributions are very welcome too, but since Embark is now on -GNU ELPA, copyright assignment to the FSF is required before you can -contribute code. - -* Acknowledgments - -While I, Omar Antolín Camarena, have written most of the Embark code -and remain very stubborn about some of the design decisions, Embark -has received substantial help from a number of other people which this -document has neglected to mention for far too long. In particular, -Daniel Mendler has been absolutely invaluable, implementing several -important features, and providing a lot of useful advice. - -Code contributions: - -- [[https://github.com/minad][Daniel Mendler]] -- [[https://github.com/clemera/][Clemens Radermacher]] -- [[https://codeberg.org/jao/][José Antonio Ortega Ruiz]] -- [[https://github.com/iyefrat][Itai Y. Efrat]] -- [[https://github.com/a13][a13]] -- [[https://github.com/jakanakaevangeli][jakanakaevangeli]] -- [[https://github.com/mihakam][mihakam]] -- [[https://github.com/leungbk][Brian Leung]] -- [[https://github.com/karthink][Karthik Chikmagalur]] -- [[https://github.com/roshanshariff][Roshan Shariff]] -- [[https://github.com/condy0919][condy0919]] -- [[https://github.com/DamienCassou][Damien Cassou]] -- [[https://github.com/JimDBh][JimDBh]] - -Advice and useful discussions: - -- [[https://github.com/minad][Daniel Mendler]] -- [[https://gitlab.com/protesilaos/][Protesilaos Stavrou]] -- [[https://github.com/clemera/][Clemens Radermacher]] -- [[https://github.com/hmelman/][Howard Melman]] -- [[https://github.com/astoff][Augusto Stoffel]] -- [[https://github.com/bdarcus][Bruce d'Arcus]] -- [[https://github.com/jdtsmith][JD Smith]] -- [[https://github.com/karthink][Karthik Chikmagalur]] -- [[https://github.com/jakanakaevangeli][jakanakaevangeli]] -- [[https://github.com/iyefrat][Itai Y. Efrat]] -- [[https://github.com/mohkale][Mohsin Kaleem]] blob - 818863058651d778ade67038f9bcad5661f59022 (mode 644) blob + /dev/null --- elpa/embark-1.1/dir +++ /dev/null @@ -1,18 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs misc features -* Embark: (embark). Emacs Mini-Buffer Actions Rooted in Keymaps. blob - 98111a655213e6973411d6a0ababf40e760ae5bb (mode 644) blob + /dev/null --- elpa/embark-1.1/embark-autoloads.el +++ /dev/null @@ -1,210 +0,0 @@ -;;; embark-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from embark.el - -(defun embark--record-this-command nil "\ -Record command which opened the minibuffer. -We record this because it will be the default action. -This function is meant to be added to `minibuffer-setup-hook'." (setq-local embark--command this-command)) -(add-hook 'minibuffer-setup-hook #'embark--record-this-command) -(autoload 'embark-eldoc-first-target "embark" "\ -Eldoc function reporting the first Embark target at point. -This function uses the eldoc REPORT callback and is meant to be -added to `eldoc-documentation-functions'. - -(fn REPORT &rest _)") -(autoload 'embark-eldoc-target-types "embark" "\ -Eldoc function reporting the types of all Embark targets at point. -This function uses the eldoc REPORT callback and is meant to be -added to `eldoc-documentation-functions'. - -(fn REPORT &rest _)") -(autoload 'embark-bindings-in-keymap "embark" "\ -Explore command key bindings in KEYMAP with `completing-read'. -The selected command will be executed. Interactively, prompt the -user for a KEYMAP variable. - -(fn KEYMAP)" t) -(autoload 'embark-bindings "embark" "\ -Explore current command key bindings with `completing-read'. -The selected command will be executed. - -This shows key bindings from minor mode maps and the local -map (usually set by the major mode), but also less common keymaps -such as those from a text property or overlay, or the overriding -maps: `overriding-terminal-local-map' and `overriding-local-map'. - -Additionally, if GLOBAL is non-nil (interactively, if called with -a prefix argument), this command includes global key bindings. - -(fn GLOBAL)" t) -(autoload 'embark-bindings-at-point "embark" "\ -Explore all key bindings at point with `completing-read'. -The selected command will be executed. - -This command lists key bindings found in keymaps specified by the -text properties `keymap' or `local-map', from either buffer text -or an overlay. These are not widely used in Emacs, and when they -are used can be somewhat hard to discover. Examples of locations -that have such a keymap are links and images in `eww' buffers, -attachment links in `gnus' article buffers, and the stash line -in a `vc-dir' buffer." t) -(autoload 'embark-prefix-help-command "embark" "\ -Prompt for and run a command bound in the prefix used for this command. -The prefix described consists of all but the last event of the -key sequence that ran this command. This function is intended to -be used as a value for `prefix-help-command'. - -In addition to using completion to select a command, you can also -type @ and the key binding (without the prefix)." t) -(autoload 'embark-act "embark" "\ -Prompt the user for an action and perform it. -The targets of the action are chosen by `embark-target-finders'. -By default, if called from a minibuffer the target is the top -completion candidate. When called from a non-minibuffer buffer -there can multiple targets and you can cycle among them by using -`embark-cycle' (which is bound by default to the same key -binding `embark-act' is, but see `embark-cycle-key'). - -This command uses `embark-prompter' to ask the user to specify an -action, and calls it injecting the target at the first minibuffer -prompt. - -If you call this from the minibuffer, it can optionally quit the -minibuffer. The variable `embark-quit-after-action' controls -whether calling `embark-act' with nil ARG quits the minibuffer, -and if ARG is non-nil it will do the opposite. Interactively, -ARG is the prefix argument. - -If instead you call this from outside the minibuffer, the first -ARG targets are skipped over (if ARG is negative the skipping is -done by cycling backwards) and cycling starts from the following -target. - -(fn &optional ARG)" t) -(autoload 'embark-act-all "embark" "\ -Prompt the user for an action and perform it on each candidate. -The candidates are chosen by `embark-candidate-collectors'. By -default, if `embark-select' has been used to select some -candidates, then `embark-act-all' will act on those candidates; -otherwise, if the selection is empty and `embark-act-all' is -called from a minibuffer, then the candidates are the completion -candidates. - -This command uses `embark-prompter' to ask the user to specify an -action, and calls it injecting the target at the first minibuffer -prompt. - -If you call this from the minibuffer, it can optionally quit the -minibuffer. The variable `embark-quit-after-action' controls -whether calling `embark-act' with nil ARG quits the minibuffer, -and if ARG is non-nil it will do the opposite. Interactively, -ARG is the prefix argument. - -(fn &optional ARG)" t) -(autoload 'embark-dwim "embark" "\ -Run the default action on the current target. -The target of the action is chosen by `embark-target-finders'. - -If the target comes from minibuffer completion, then the default -action is the command that opened the minibuffer in the first -place, unless overridden by `embark-default-action-overrides'. - -For targets that do not come from minibuffer completion -(typically some thing at point in a regular buffer) and whose -type is not listed in `embark-default-action-overrides', the -default action is given by whatever binding RET has in the action -keymap for the target's type. - -See `embark-act' for the meaning of the prefix ARG. - -(fn &optional ARG)" t) -(autoload 'embark-become "embark" "\ -Make current command become a different command. -Take the current minibuffer input as initial input for new -command. The new command can be run normally using key bindings or -\\[execute-extended-command], but if the current command is found in a keymap in -`embark-become-keymaps', that keymap is activated to provide -convenient access to the other commands in it. - -If FULL is non-nil (interactively, if called with a prefix -argument), the entire minibuffer contents are used as the initial -input of the new command. By default only the part of the -minibuffer contents between the current completion boundaries is -taken. What this means is fairly technical, but (1) usually -there is no difference: the completion boundaries include the -entire minibuffer contents, and (2) the most common case where -these notions differ is file completion, in which case the -completion boundaries single out the path component containing -point. - -(fn &optional FULL)" t) -(autoload 'embark-collect "embark" "\ -Create an Embark Collect buffer. - -To control the display, add an entry to `display-buffer-alist' -with key \"Embark Collect\". - -In Embark Collect buffers `revert-buffer' is remapped to -`embark-rerun-collect-or-export', which has slightly unusual -behavior if the buffer was obtained by running `embark-collect' -from within a minibuffer completion session. In that case -rerunning just restarts the completion session, that is, the -command that opened the minibuffer is run again and the -minibuffer contents restored. You can then interact normally with -the command, perhaps editing the minibuffer contents, and, if you -wish, you can rerun `embark-collect' to get an updated buffer." t) -(autoload 'embark-live "embark" "\ -Create a live-updating Embark Collect buffer. - -To control the display, add an entry to `display-buffer-alist' -with key \"Embark Live\"." t) -(autoload 'embark-export "embark" "\ -Create a type-specific buffer to manage current candidates. -The variable `embark-exporters-alist' controls how to make the -buffer for each type of completion. - -In Embark Export buffers `revert-buffer' is remapped to -`embark-rerun-collect-or-export', which has slightly unusual -behavior if the buffer was obtained by running `embark-export' -from within a minibuffer completion session. In that case -reverting just restarts the completion session, that is, the -command that opened the minibuffer is run again and the -minibuffer contents restored. You can then interact normally -with the command, perhaps editing the minibuffer contents, and, -if you wish, you can rerun `embark-export' to get an updated -buffer." t) -(autoload 'embark-select "embark" "\ -Add or remove the target from the current buffer's selection. -You can act on all selected targets at once with `embark-act-all'. -When called from outside `embark-act' this command will select -the first target at point." t) -(register-definition-prefixes "embark" '("embark-")) - - -;;; Generated autoloads from embark-org.el - -(register-definition-prefixes "embark-org" '("embark-org-")) - -;;; End of scraped data - -(provide 'embark-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; embark-autoloads.el ends here blob - f2816b1930297c187141f0c648558957b786dd20 (mode 644) blob + /dev/null --- elpa/embark-1.1/embark-org.el +++ /dev/null @@ -1,716 +0,0 @@ -;;; embark-org.el --- Embark targets and actions for Org Mode -*- lexical-binding: t; -*- - -;; Copyright (C) 2022-2023 Free Software Foundation, Inc. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This package configures the Embark package for use in Org Mode -;; buffers. It teaches Embark a number of Org related targets and -;; appropriate actions. Currently it has table cells, whole tables, -;; source blocks and links. Targets to add: headings (Embark already -;; has generic support for outlines, so we just nee to add Org -;; specific actions), timestamps, etc. - -;;; Code: - -(require 'embark) -(require 'org) -(require 'org-element) - -;;; Basic target finder for Org - -;; There are very many org element and objects types, we'll only -;; recognize those for which there are specific actions we can put in -;; a keymap, or even if there aren't any specific actions, if it's -;; important to be able to kill, delete or duplicate (embark-insert) -;; them conveniently. I'll start conservatively and we can add more -;; later - -(defconst embark-org--types - '( - babel-call - ;; bold - ;; center-block - ;; citation - ;; citation-reference - ;; clock - ;; code - ;; comment - ;; comment-block - ;; diary-sexp - ;; drawer - ;; dynamic-block - ;; entity - ;; example-block - ;; export-block - ;; export-snippet - ;; fixed-width - footnote-definition - footnote-reference - ;; headline ; the bounds include the entire subtree! - ;; horizontal-rule - ;; inline-babel-call - inline-src-block - ;; inlinetask - ;; italic - item - ;; keyword - ;; latex-environment - ;; latex-fragment - ;; line-break - link - ;; macro - ;; node-property - ;; paragraph ; the existing general support seems fine - plain-list - ;; planning - ;; property-drawer - ;; quote-block - ;; radio-target - ;; section - ;; special-block - src-block - ;; statistics-cookie - ;; strike-through - ;; subscript - ;; superscript - table ; supported via a specific target finder - table-cell - ;; table-row ; we'll put row & column actions in the cell map - ;; target ; I think there are no useful actions for radio targets - timestamp - ;; underline - ;; verbatim - ;; verse-block - ) - "Supported Org object and element types.") - -(defun embark-org-target-element-context () - "Target all Org elements or objects around point." - (when (derived-mode-p 'org-mode) - (cl-loop - for elt = (org-element-lineage (org-element-context) embark-org--types t) - then (org-element-lineage elt embark-org--types) - while elt - ;; clip bounds to narrowed portion of buffer - for begin = (max (org-element-property :begin elt) (point-min)) - for end = (min (org-element-property :end elt) (point-max)) - for target = (buffer-substring begin end) - ;; Adjust table-cell to exclude final |. (Why is that there?) - ;; Note: We are not doing this as an embark transformer because we - ;; want to adjust the bounds too. - ;; TODO? If more adjustments like this become necessary, add a - ;; nice mechanism for doing them. - when (and (eq (car elt) 'table-cell) (string-suffix-p "|" target)) - do (setq target (string-trim (string-remove-suffix "|" target)) - end (1- end)) - collect `(,(intern (format "org-%s" (car elt))) ,target ,begin . ,end)))) - -(unless (memq 'embark-org-target-element-context embark-target-finders) - (if-let ((tail (memq 'embark-target-active-region embark-target-finders))) - (push 'embark-org-target-element-context (cdr tail)) - (push 'embark-org-target-element-context embark-target-finders))) - -;;; Custom Org actions - -(defvar org-export-with-toc) - -(defun embark-org-copy-as-markdown (start end) - "Export the region from START to END to markdown and save on the `kill-ring'." - (interactive "r") - (require 'ox) - (kill-new - (let (org-export-with-toc) - (string-trim - (org-export-string-as (buffer-substring-no-properties start end) 'md t)))) - (deactivate-mark)) - -(add-to-list 'embark-pre-action-hooks - '(embark-org-copy-as-markdown embark--mark-target)) - -(keymap-set embark-region-map "M" #'embark-org-copy-as-markdown) ; good idea? - -;;; Tables - -(dolist (motion '(org-table-move-cell-up org-table-move-cell-down - org-table-move-cell-left org-table-move-cell-right - org-table-move-row org-table-move-column - org-table-move-row-up org-table-move-row-down - org-table-move-column-left org-table-move-column-right)) - (add-to-list 'embark-repeat-actions motion)) - -(dolist (cmd '(org-table-eval-formula org-table-edit-field)) - (push 'embark--ignore-target (alist-get cmd embark-target-injection-hooks))) - -(defvar-keymap embark-org-table-cell-map - :doc "Keymap for actions the current cells, column or row of an Org table." - :parent embark-general-map - "RET" #'org-table-align ; harmless default - "" #'org-table-move-cell-up - "" #'org-table-move-cell-down - "" #'org-table-move-cell-left - "" #'org-table-move-cell-right - "d" #'org-table-kill-row - "c" #'org-table-copy-down - "D" #'org-table-delete-column ; capital = column - "^" #'org-table-move-row-up - "v" #'org-table-move-row-down - "<" #'org-table-move-column-left - ">" #'org-table-move-column-right - "o" #'org-table-insert-row - "O" #'org-table-insert-column ; capital = column - "h" #'org-table-insert-hline - "=" #'org-table-eval-formula - "e" #'org-table-edit-field - "g" #'org-table-recalculate) - -(defvar-keymap embark-org-table-map - :doc "Keymap for actions on entire Org table." - :parent embark-general-map - "RET" #'org-table-align ; harmless default - "=" #'org-table-edit-formulas - "s" #'org-table-sort-lines - "t" #'org-table-transpose-table-at-point - "c" #'org-table-convert - "f" #'org-table-follow-field-mode - "y" #'org-table-paste-rectangle - "d" #'org-table-toggle-formula-debugger - "o" #'org-table-toggle-coordinate-overlays - "g" #'org-table-iterate - "e" #'org-table-export) - -(push 'embark--ignore-target ; prompts for file name - (alist-get 'org-table-export embark-target-injection-hooks)) - -(add-to-list 'embark-keymap-alist '(org-table embark-org-table-map)) - -(add-to-list 'embark-keymap-alist '(org-table-cell embark-org-table-cell-map)) - -;;; Links - -;; The link support has a slightly complicated design in order to -;; achieve the following goals: - -;; 1. RET should simply be org-open-at-point - -;; 2. When the link is to a file, URL, email address or elisp -;; expression or command, we want to offer the user actions for -;; that underlying type. - -;; 3. Even in those cases, we still want some actions to apply to the -;; entire link including description: actions to copy the link as -;; markdown, or just the link description or target. - -;; So the strategy is as follows (illustrated with file links): - -;; - The target will be just the file, without the description and -;; also without the "file:" prefix nor the "::line-number or search" -;; suffix. That way, file actions will correctly apply to it. - -;; - The type will not be 'file, but 'org-file-link; that way we can -;; register a keymap for 'org-file-link that inherits from both -;; embark-org-link-map (with RET bound to org-open-at-point and a -;; few other generic link actions) and embark-file-map. - -;; - The commands to copy the link at point in some format will be -;; written as commands that act on the Org link at point. This way -;; they are independently (plausibly) useful, and we circumvent the -;; problem that the whole Org link is not actually the target (just -;; the inner file is!). - -;; Alternative design I considered: separate each target into two, a -;; whole link target which includes the description and brackets and -;; what not; and an "inner target" which is just the file or URL or -;; whatever. Cons of this approach: much target cycling is required! -;; First of all, an unadorned embark-dwim definitely should be -;; org-open-at-point, which means the whole link target would need -;; priority. That means that any file, URL, etc. actions would require -;; you to cycle first. This sounds very inconvenient, the above -;; slightly more complex design allows both whole-link and inner -;; target actions to work without cycling. - -(defun embark-org-target-link () - "Target Org link at point. -This targets Org links in any buffer, not just buffers in -`org-mode' or `org-agenda-mode'. Org links in any buffer can be -opened with `org-open-at-point-global', which is the default -Embark action for Org links." - (pcase (org-in-regexp org-link-any-re) - (`(,start . ,end) - ;; We won't recognize unadorned http(s) or mailto links, as those - ;; already have target finders (but if these links have either a - ;; description, double brackets or angle brackets, then we do - ;; recognize them as org links) - (unless (save-excursion (goto-char start) (looking-at "http\\|mailto")) - `(org-link ,(buffer-substring start end) ,start . ,end))))) - -(let ((tail (memq 'embark-target-active-region embark-target-finders))) - (cl-pushnew 'embark-org-target-link (cdr tail))) - -(autoload 'org-attach-dir "org-attach") - -(defun embark-org--refine-link-type (_type target) - "Refine type of link TARGET if we have more specific actions available." - (when (string-match org-link-any-re target) - (let ((target (or (match-string-no-properties 2 target) - (match-string-no-properties 0 target)))) - (cond - ((string-prefix-p "http" target) - (cons 'org-url-link target)) - ((string-prefix-p "mailto:" target) - (cons 'org-email-link (string-remove-prefix "mailto:" target))) - ((string-prefix-p "file:" target) - (cons 'org-file-link - (replace-regexp-in-string - "::.*" "" (string-remove-prefix "file:" target)))) - ((string-prefix-p "attachment:" target) - (cons 'org-file-link - (expand-file-name - (replace-regexp-in-string - "::.*" "" (string-remove-prefix "attachment:" target)) - (org-attach-dir)))) - ((string-match-p "^[./]" target) - (cons 'org-file-link (abbreviate-file-name (expand-file-name target)))) - ((string-prefix-p "elisp:(" target) - (cons 'org-expression-link (string-remove-prefix "elisp:" target))) - ((string-prefix-p "elisp:" target) - (cons 'command (string-remove-prefix "elisp:" target))) - (t (cons 'org-link target)))))) - -(add-to-list 'embark-transformer-alist - '(org-link . embark-org--refine-link-type)) - -(defmacro embark-org-define-link-copier (name formula description) - "Define a command that copies the Org link at point according to FORMULA. -The command's name is formed by appending NAME to -embark-org-copy-link. The docstring includes the DESCRIPTION of -what part or in what format the link is copied." - `(defun ,(intern (format "embark-org-copy-link-%s" name)) () - ,(format "Copy to the kill-ring the Org link at point%s." description) - (interactive) - (when (org-in-regexp org-link-any-re) - (let* ((full (match-string-no-properties 0)) - (target (or (match-string-no-properties 2) - (match-string-no-properties 0))) - (description (match-string-no-properties 3)) - (kill ,formula)) - (ignore full target description) - (when kill - (message "Saved '%s'" kill) - (kill-new kill)))))) - -(embark-org-define-link-copier in-full full " in full") -(embark-org-define-link-copier description description "'s description") -(embark-org-define-link-copier target target "'s target") - -(defalias 'embark-org-copy-link-inner-target #'kill-new - "Copy inner part of the Org link at point's target. -For mailto and elisp links, the inner part is the portion of the -target after `mailto:' or `elisp:'. - -For file links the inner part is the file name, without the -`file:' prefix and without `::' suffix (used for line numbers, -IDs or search terms). - -For URLs the inner part is the whole target including the `http:' -or `https:' prefix. For any other type of link the inner part is -also the whole target.") - -(defvar-keymap embark-org-link-copy-map - :doc "Keymap for different ways to copy Org links to the kill-ring. - -You should bind w in this map to your most frequently used link -copying function. The default is for w to copy the \"inner -target\" (see `embark-org-copy-link-inner-target'); which is also -bound to i." - :parent nil - "w" #'embark-org-copy-link-inner-target - "f" #'embark-org-copy-link-in-full - "d" #'embark-org-copy-link-description - "t" #'embark-org-copy-link-target - "i" #'embark-org-copy-link-inner-target - "m" #'embark-org-copy-as-markdown) - -(fset 'embark-org-link-copy-map embark-org-link-copy-map) - -(defvar-keymap embark-org-link-map - :doc "Keymap for actions on Org links." - :parent embark-general-map - "RET" #'org-open-at-point-global - "'" #'org-insert-link - "n" #'org-next-link - "p" #'org-previous-link - "w" #'embark-org-link-copy-map) - -(dolist (motion '(org-next-link org-previous-link)) - (cl-pushnew motion embark-repeat-actions)) - -;; The reason for this is left as an exercise to the reader. -;; Solution: Na ryvfc gnetrg znl cebzcg gur hfre sbe fbzrguvat! -(cl-pushnew 'embark--ignore-target - (alist-get 'org-open-at-point embark-target-injection-hooks)) -(cl-pushnew 'embark--ignore-target - (alist-get 'org-insert-link embark-target-injection-hooks)) - -(add-to-list 'embark-keymap-alist - '(org-link embark-org-link-map)) -(add-to-list 'embark-keymap-alist - '(org-url-link embark-org-link-map embark-url-map)) -(add-to-list 'embark-keymap-alist - '(org-email-link embark-org-link-map embark-email-map)) -(add-to-list 'embark-keymap-alist - '(org-file-link embark-org-link-map embark-file-map)) -(add-to-list 'embark-keymap-alist - '(org-expression-link embark-org-link-map embark-expression-map)) - -;;; Org headings - -(defun embark-org--refine-heading (type target) - "Refine TYPE of heading TARGET in Org buffers." - (cons - (if (derived-mode-p 'org-mode) 'org-heading type) - target)) - -(add-to-list 'embark-transformer-alist '(heading . embark-org--refine-heading)) - -(defvar-keymap embark-org-heading-map - :doc "Keymap for actions on Org headings." - :parent embark-heading-map - "RET" #'org-todo - "TAB" #'org-cycle - "t" #'org-todo - "s" #'org-schedule - "d" #'org-deadline - "," #'org-priority - ":" #'org-set-tags-command - "P" #'org-set-property - "D" #'org-delete-property - "k" #'org-cut-subtree - "N" #'org-narrow-to-subtree - "T" #'org-tree-to-indirect-buffer - "" #'org-do-promote - "" #'org-do-demote - "o" #'org-sort - "r" #'org-refile - "R" #'embark-org-refile-here - "I" #'org-clock-in - "O" #'org-clock-out - "a" #'org-archive-subtree-default-with-confirmation - "h" #'org-insert-heading-respect-content - "H" #'org-insert-todo-heading-respect-content - "l" #'org-store-link - "j" #'embark-org-insert-link-to) - -(dolist (cmd '(org-todo org-metaright org-metaleft org-metaup org-metadown - org-shiftmetaleft org-shiftmetaright org-cycle org-shifttab)) - (cl-pushnew cmd embark-repeat-actions)) - -(dolist (cmd '(org-set-tags-command org-set-property - org-delete-property org-refile org-schedule)) - (cl-pushnew 'embark--ignore-target - (alist-get cmd embark-target-injection-hooks))) - -(add-to-list 'embark-keymap-alist '(org-heading embark-org-heading-map)) - -;;; Source blocks - -(defun embark-org-copy-block-contents () - "Save contents of source block at point to the `kill-ring'." - (interactive) - (when (org-in-src-block-p) - (let ((contents (nth 2 (org-src--contents-area (org-element-at-point))))) - (with-temp-buffer - (insert contents) - (org-do-remove-indentation) - (kill-new (buffer-substring (point-min) (point-max))))))) - -(defvar-keymap embark-org-src-block-map - :doc "Keymap for actions on Org source blocks." - :parent embark-general-map - "RET" #'org-babel-execute-src-block - "C-SPC" #'org-babel-mark-block - "TAB" #'org-indent-block - "c" #'embark-org-copy-block-contents - "h" #'org-babel-check-src-block - "k" #'org-babel-remove-result-one-or-many - "p" #'org-babel-previous-src-block - "n" #'org-babel-next-src-block - "t" #'org-babel-tangle - "s" #'org-babel-switch-to-session - "l" #'org-babel-load-in-session - "'" #'org-edit-special - "/" #'org-babel-demarcate-block - "N" #'org-narrow-to-block) - -(cl-defun embark-org--at-block-head - (&rest rest &key run bounds &allow-other-keys) - "Save excursion and RUN the action at the head of the current block. -If BOUNDS are given, use them to locate the block (useful for -when acting on a selection of blocks). Applies RUN to the REST -of the arguments." - (save-excursion - (when bounds (goto-char (car bounds))) - (org-babel-goto-src-block-head) - (apply run rest))) - -(cl-pushnew #'embark-org--at-block-head - (alist-get 'org-indent-block embark-around-action-hooks)) - -(dolist (motion '(org-babel-next-src-block org-babel-previous-src-block)) - (add-to-list 'embark-repeat-actions motion)) - -(dolist (cmd '(org-babel-execute-maybe - org-babel-lob-execute-maybe - org-babel-execute-src-block - org-babel-execute-src-block-maybe - org-babel-execute-buffer - org-babel-execute-subtree)) - (cl-pushnew #'embark--ignore-target - (alist-get cmd embark-target-injection-hooks))) - -(add-to-list 'embark-keymap-alist '(org-src-block embark-org-src-block-map)) - -;;; Inline source blocks - -(defvar-keymap embark-org-inline-src-block-map - :doc "Keymap for actions on Org inline source blocks." - :parent embark-general-map - "RET" #'org-babel-execute-src-block - "'" #'org-edit-inline-src-code - "k" #'org-babel-remove-inline-result) - -(add-to-list 'embark-keymap-alist - '(org-inline-src-block embark-org-inline-src-block-map)) - -;;; Babel calls - -(defvar-keymap embark-org-babel-call-map - :doc "Keymap for actions on Org babel calls." - :parent embark-general-map - "RET" #'org-babel-lob-execute-maybe - "k" #'org-babel-remove-result) - -(add-to-list 'embark-keymap-alist - '(org-babel-call embark-org-babel-call-map)) - -;;; List items - -(defvar-keymap embark-org-item-map - :doc "Keymap for actions on Org list items." - :parent embark-general-map - "RET" #'org-toggle-checkbox - "c" #'org-toggle-checkbox - "t" #'org-toggle-item - "n" #'org-next-item - "p" #'org-previous-item - "" #'org-outdent-item - "" #'org-indent-item - "" #'org-move-item-up - "" #'org-move-item-down - ">" #'org-indent-item-tree - "<" #'org-outdent-item-tree) - -(dolist (cmd '(org-toggle-checkbox - org-toggle-item - org-next-item - org-previous-item - org-outdent-item - org-indent-item - org-move-item-up - org-move-item-down - org-indent-item-tree - org-outdent-item-tree)) - (add-to-list 'embark-repeat-actions cmd)) - -(add-to-list 'embark-keymap-alist '(org-item embark-org-item-map)) - -;;; Org plain lists - -(defvar-keymap embark-org-plain-list-map - :doc "Keymap for actions on plain Org lists." - :parent embark-general-map - "RET" #'org-list-repair - "r" #'org-list-repair - "s" #'org-sort-list - "b" #'org-cycle-list-bullet - "t" #'org-list-make-subtree - "c" #'org-toggle-checkbox) - -(add-to-list 'embark-repeat-actions 'org-cycle-list-bullet) - -(add-to-list 'embark-keymap-alist '(org-plain-list embark-org-plain-list-map)) - -(cl-defun embark-org--toggle-checkboxes - (&rest rest &key run type &allow-other-keys) - "Around action hook for `org-toggle-checkbox'. -See `embark-around-action-hooks' for the keyword arguments RUN and TYPE. -REST are the remaining arguments." - (apply (if (eq type 'org-plain-list) #'embark--mark-target run) - :type type - rest)) - -(cl-pushnew #'embark-org--toggle-checkboxes - (alist-get 'org-toggle-checkbox embark-around-action-hooks)) - -;;; "Encode" region using Org export in place - -(defvar-keymap embark-org-export-in-place-map - :doc "Keymap for actions which replace the region by an exported version." - :parent embark-general-map - "m" #'org-md-convert-region-to-md - "h" #'org-html-convert-region-to-html - "a" #'org-ascii-convert-region-to-ascii - "l" #'org-latex-convert-region-to-latex) - -(fset 'embark-org-export-in-place-map embark-org-export-in-place-map) - -(keymap-set embark-encode-map "o" 'embark-org-export-in-place-map) - -;;; References to Org headings, such as agenda items - -;; These are targets that represent an org heading but not in the -;; current buffer, instead they have a text property named -;; `org-marker' that points to the actual heading. - -(defun embark-org-target-agenda-item () - "Target Org agenda item at point." - (when (and (derived-mode-p 'org-agenda-mode) - (get-text-property (line-beginning-position) 'org-marker)) - (let ((start (+ (line-beginning-position) (current-indentation))) - (end (line-end-position))) - `(org-heading ,(buffer-substring start end) ,start . ,end)))) - -(let ((tail (memq 'embark-org-target-element-context embark-target-finders))) - (cl-pushnew 'embark-org-target-agenda-item (cdr tail))) - -(cl-defun embark-org--at-heading - (&rest rest &key run target &allow-other-keys) - "RUN the action at the location of the heading TARGET refers to. -The location is given by the `org-marker' text property of -target. Applies RUN to the REST of the arguments." - (if-let ((marker (get-text-property 0 'org-marker target))) - (org-with-point-at marker - (apply run :target target rest)) - (apply run :target target rest))) - -(cl-defun embark-org-goto-heading (&key target &allow-other-keys) - "Jump to the org heading TARGET refers to." - (when-let ((marker (get-text-property 0 'org-marker target))) - (pop-to-buffer (marker-buffer marker)) - (widen) - (goto-char marker) - (org-reveal) - (pulse-momentary-highlight-one-line))) - -(defun embark-org-heading-default-action (target) - "Default action for Org headings. -There are two types of heading TARGETs: the heading at point in a -normal org buffer, and references to org headings in some other -buffer (for example, org agenda items). For references the -default action is to jump to the reference, and for the heading -at point, the default action is whatever is bound to RET in -`embark-org-heading-map', or `org-todo' if RET is unbound." - (if (get-text-property 0 'org-marker target) - (embark-org-goto-heading :target target) - (command-execute - (or (keymap-lookup embark-org-heading-map "RET") #'org-todo)))) - -(defconst embark-org--invisible-jump-to-heading - '(org-tree-to-indirect-buffer - org-refile - org-clock-in - org-clock-out - org-archive-subtree-default-with-confirmation - org-store-link) - "Org heading actions which won't display the heading's buffer.") - -(defconst embark-org--no-jump-to-heading - '(embark-org-insert-link-to embark-org-refile-here) - "Org heading actions which shouldn't be executed with point at the heading.") - -(setf (alist-get 'org-heading embark-default-action-overrides) - #'embark-org-heading-default-action) - -(map-keymap - (lambda (_key cmd) - (unless (or (where-is-internal cmd (list embark-general-map)) - (memq cmd embark-org--no-jump-to-heading) - (memq cmd embark-org--invisible-jump-to-heading)) - (cl-pushnew 'embark-org-goto-heading - (alist-get cmd embark-pre-action-hooks)))) - embark-org-heading-map) - -(dolist (cmd embark-org--invisible-jump-to-heading) - (cl-pushnew 'embark-org--at-heading - (alist-get cmd embark-around-action-hooks))) - -(defun embark-org--in-source-window (target function) - "Call FUNCTION, in the source window, on TARGET's `org-marker'. - -If TARGET does not have an `org-marker' property a `user-error' -is signaled. In case the TARGET comes from an org agenda buffer -and the `other-window-for-scrolling' is an org mode buffer, then -the FUNCTION is called with that other window temporarily -selected; otherwise the FUNCTION is called in the selected -window." - (if-let ((marker (get-text-property 0 'org-marker target))) - (with-selected-window - (or (and (derived-mode-p 'org-agenda-mode) - (let ((window (ignore-errors (other-window-for-scrolling)))) - (with-current-buffer (window-buffer window) - (when (derived-mode-p 'org-mode) window)))) - (selected-window)) - (funcall function marker)) - (user-error "The target is an org heading rather than a reference to one"))) - -(defun embark-org-refile-here (target) - "Refile the heading at point in the source window to TARGET. - -If TARGET is an agenda item and `other-window-for-scrolling' is -displaying an org mode buffer, then that is the source window. -If TARGET is a minibuffer completion candidate, then the source -window is the window selected before the command that opened the -minibuffer ran." - (embark-org--in-source-window target - (lambda (marker) - (org-refile nil nil - ;; The RFLOC argument: - (list - ;; Name - (org-with-point-at marker - (nth 4 (org-heading-components))) - ;; File - (buffer-file-name (marker-buffer marker)) - ;; nil - nil - ;; Position - marker))))) - -(defun embark-org-insert-link-to (target) - "Insert a link to the TARGET in the source window. - -If TARGET is an agenda item and `other-window-for-scrolling' is -displaying an org mode buffer, then that is the source window. -If TARGET is a minibuffer completion candidate, then the source -window is the window selected before the command that opened the -minibuffer ran." - (embark-org--in-source-window target - (lambda (marker) - (org-with-point-at marker (org-store-link nil t)) - (org-insert-all-links 1 "" "")))) - -(provide 'embark-org) -;;; embark-org.el ends here blob - 46a156c5664210bface6266e1662ecbc64c1973c (mode 644) blob + /dev/null --- elpa/embark-1.1/embark-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from embark.el -*- no-byte-compile: t -*- -(define-package "embark" "1.1" "Conveniently act on minibuffer completions" '((emacs "27.1") (compat "29.1.4.0")) :commit "195add1f1ccd1059472c9df7334c97c4d155425e" :authors '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) :maintainer '("Omar Antolín Camarena" . "omar@matem.unam.mx") :keywords '("convenience") :url "https://github.com/oantolin/embark") blob - 660d9324f9e84a9e4145d13b65c9bd30ff7fd0ff (mode 644) blob + /dev/null --- elpa/embark-1.1/embark.el +++ /dev/null @@ -1,4604 +0,0 @@ -;;; embark.el --- Conveniently act on minibuffer completions -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2023 Free Software Foundation, Inc. - -;; Author: Omar Antolín Camarena -;; Maintainer: Omar Antolín Camarena -;; Keywords: convenience -;; Version: 1.1 -;; Homepage: https://github.com/oantolin/embark -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.0")) - -;; This file is part of GNU Emacs. - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This package provides a sort of right-click contextual menu for -;; Emacs, accessed through the `embark-act' command (which you should -;; bind to a convenient key), offering you relevant actions to use on -;; a target determined by the context: - -;; - In the minibuffer, the target is the current best completion -;; candidate. -;; - In the `*Completions*' buffer the target is the completion at point. -;; - In a regular buffer, the target is the region if active, or else the -;; file, symbol or url at point. - -;; The type of actions offered depend on the type of the target: - -;; - For files you get offered actions like deleting, copying, -;; renaming, visiting in another window, running a shell command on the -;; file, etc. -;; - For buffers the actions include switching to or killing the buffer. -;; - For package names the actions include installing, removing or -;; visiting the homepage. - -;; Everything is easily configurable: determining the current target, -;; classifying it, and deciding with actions are offered for each type -;; in the classification. The above introduction just mentions part of -;; the default configuration. - -;; Configuring which actions are offered for a type is particularly -;; easy and requires no programming: the `embark-keymap-alist' -;; variable associates target types with variable containing keymaps, -;; and those keymaps containing binds for the actions. For example, -;; in the default configuration the type `file' is associated with the -;; symbol `embark-file-map'. That symbol names a keymap with -;; single-letter key bindings for common Emacs file commands, for -;; instance `c' is bound to `copy-file'. This means that if while you -;; are in the minibuffer after running a command that prompts for a -;; file, such as `find-file' or `rename-file', you can copy a file by -;; running `embark-act' and then pressing `c'. - -;; These action keymaps are very convenient but not strictly necessary -;; when using `embark-act': you can use any command that reads from the -;; minibuffer as an action and the target of the action will be inserted -;; at the first minibuffer prompt. After running `embark-act' all of your -;; key bindings and even `execute-extended-command' can be used to run a -;; command. The action keymaps are normal Emacs keymaps and you should -;; feel free to bind in them whatever commands you find useful as actions. - -;; The actions in `embark-general-map' are available no matter what -;; type of completion you are in the middle of. By default this -;; includes bindings to save the current candidate in the kill ring -;; and to insert the current candidate in the previously selected -;; buffer (the buffer that was current when you executed a command -;; that opened up the minibuffer). - -;; You can read about the Embark GitHub project wiki: -;; https://github.com/oantolin/embark/wiki/Default-Actions - -;; Besides acting individually on targets, Embark lets you work -;; collectively on a set of target candidates. For example, while -;; you are in the minibuffer the candidates are simply the possible -;; completions of your input. Embark provides three commands to work -;; on candidate sets: - -;; - The `embark-act-all' command runs the same action on each of the -;; current candidates. It is just like using `embark-act' on each -;; candidate in turn. - -;; - The `embark-collect' command produces a buffer listing all -;; candidates, for you to peruse and run actions on at your leisure. -;; The candidates are displayed as a list showing additional -;; annotations. - -;; - The `embark-export' command tries to open a buffer in an -;; appropriate major mode for the set of candidates. If the -;; candidates are files export produces a Dired buffer; if they are -;; buffers, you get an Ibuffer buffer; and if they are packages you -;; get a buffer in package menu mode. - -;; These are always available as "actions" (although they do not act -;; on just the current target but on all candidates) for embark-act -;; and are bound to A, S (for "snapshot") and E, respectively, in -;; embark-general-map. This means that you do not have to bind your -;; own key bindings for these (although you can, of course), just a -;; key binding for `embark-act'. - -;;; Code: - -(require 'compat) -(eval-when-compile (require 'subr-x)) - -(require 'ffap) ; used to recognize file and url targets - -;;; User facing options - -(defgroup embark nil - "Emacs Mini-Buffer Actions Rooted in Keymaps." - :link '(info-link :tag "Info Manual" "(embark)") - :link '(url-link :tag "Homepage" "https://github.com/oantolin/embark") - :link '(emacs-library-link :tag "Library Source" "embark.el") - :group 'minibuffer - :prefix "embark-") - -(defcustom embark-keymap-alist - '((file embark-file-map) - (library embark-library-map) - (environment-variables embark-file-map) ; they come up in file completion - (url embark-url-map) - (email embark-email-map) - (buffer embark-buffer-map) - (tab embark-tab-map) - (expression embark-expression-map) - (identifier embark-identifier-map) - (defun embark-defun-map) - (symbol embark-symbol-map) - (face embark-face-map) - (command embark-command-map) - (variable embark-variable-map) - (function embark-function-map) - (minor-mode embark-command-map) - (unicode-name embark-unicode-name-map) - (package embark-package-map) - (bookmark embark-bookmark-map) - (region embark-region-map) - (sentence embark-sentence-map) - (paragraph embark-paragraph-map) - (kill-ring embark-kill-ring-map) - (heading embark-heading-map) - (flymake embark-flymake-map) - (smerge smerge-basic-map embark-general-map) - (t embark-general-map)) - "Alist of action types and corresponding keymaps. -The special key t is associated with the default keymap to use. -Each value can be either a single symbol whose value is a keymap, -or a list of such symbols." - :type '(alist :key-type (symbol :tag "Target type") - :value-type (choice (variable :tag "Keymap") - (repeat :tag "Keymaps" variable)))) - -(defcustom embark-target-finders - '(embark-target-top-minibuffer-candidate - embark-target-active-region - embark-target-collect-candidate - embark-target-completion-list-candidate - embark-target-text-heading-at-point - embark-target-bug-reference-at-point - embark-target-flymake-at-point - embark-target-smerge-at-point - embark-target-package-at-point - embark-target-email-at-point - embark-target-url-at-point - embark-target-file-at-point - embark-target-custom-variable-at-point - embark-target-identifier-at-point - embark-target-guess-file-at-point - embark-target-expression-at-point - embark-target-sentence-at-point - embark-target-paragraph-at-point - embark-target-defun-at-point - embark-target-prog-heading-at-point) - "List of functions to determine the target in current context. -Each function should take no arguments and return one of: - -1. a cons (TYPE . TARGET) where TARGET is a string and TYPE is a - symbol (which is looked up in `embark-keymap-alist' to - determine which additional keybindings for actions to setup); - -2. a dotted list of the form (TYPE TARGET START . END), where - START and END are the buffer positions bounding TARGET, used - for highlighting; or - -3. a possibly empty list of targets, each of type 1 or 2 (in - particular if a target finder does not find any targets, it - should return nil)." - :type 'hook) - -(defcustom embark-transformer-alist - '((minor-mode . embark--lookup-lighter-minor-mode) - (embark-keybinding . embark--keybinding-command) - (project-file . embark--project-file-full-path) - (package . embark--remove-package-version) - (multi-category . embark--refine-multi-category) - (file . embark--simplify-path)) - "Alist associating type to functions for transforming targets. -Each function should take a type and a target string and return a -pair of the form a `cons' of the new type and the new target." - :type '(alist :key-type symbol :value-type function)) - -(defcustom embark-become-keymaps - '(embark-become-help-map - embark-become-file+buffer-map - embark-become-shell-command-map - embark-become-match-map) - "List of keymaps for `embark-become'. -Each keymap groups a set of related commands that can -conveniently become one another." - :type '(repeat variable)) - -(defcustom embark-prompter 'embark-keymap-prompter - "Function used to prompt the user for actions. -This should be set to a function that prompts the use for an -action and returns the symbol naming the action command. The -default value, `embark-keymap-prompter' activates the type -specific action keymap given in `embark-keymap-alist'. -There is also `embark-completing-read-prompter' which -prompts for an action with completion." - :type '(choice (const :tag "Use action keymaps" embark-keymap-prompter) - (const :tag "Read action with completion" - embark-completing-read-prompter) - (function :tag "Other"))) - -(defcustom embark-keymap-prompter-key "@" - "Key to switch to the keymap prompter from `embark-completing-read-prompter'. - -The key must be either nil or a string. The -string must be accepted by `key-valid-p'." - :type '(choice key (const :tag "None" nil))) - -(defcustom embark-cycle-key nil - "Key used for `embark-cycle'. - -If the key is set to nil it defaults to the global binding of -`embark-act'. The key must be a string which is accepted by -`key-valid-p'." - :type '(choice key (const :tag "Use embark-act key" nil))) - -(defcustom embark-help-key "C-h" - "Key used for help. - -The key must be either nil or a string. The -string must be accepted by `key-valid-p'." - :type '(choice (const "C-h") - (const "?") - (const :tag "None" nil) - key)) - -(defcustom embark-keybinding-repeat - (propertize "*" 'face 'embark-keybinding-repeat) - "Indicator string for repeatable keybindings. -Keybindings are formatted by the `completing-read' prompter and -the verbose indicator." - :type 'string) - -(defface embark-keybinding-repeat - '((t :inherit font-lock-builtin-face)) - "Face used to indicate keybindings as repeatable.") - -(defface embark-keybinding '((t :inherit success)) - "Face used to display key bindings. -Used by `embark-completing-read-prompter' and `embark-keymap-help'.") - -(defface embark-keymap '((t :slant italic)) - "Face used to display keymaps. -Used by `embark-completing-read-prompter' and `embark-keymap-help'.") - -(defface embark-target '((t :inherit highlight)) - "Face used to highlight the target at point during `embark-act'.") - -(defcustom embark-quit-after-action t - "Should `embark-act' quit the minibuffer? -This controls whether calling `embark-act' without a prefix -argument quits the minibuffer or not. You can always get the -opposite behavior to that indicated by this variable by calling -`embark-act' with \\[universal-argument]. - -Note that `embark-act' can also be called from outside the -minibuffer and this variable is irrelevant in that case. - -In addition to t or nil this variable can also be set to an -alist to specify the minibuffer quitting behavior per command. -In the alist case one can additionally use the key t to -prescribe a default for commands not used as alist keys." - :type '(choice (const :tag "Always quit" t) - (const :tag "Never quit" nil) - (alist :tag "Configure per action" - :key-type (choice (function :tag "Action") - (const :tag "All other actions" t)) - :value-type (choice (const :tag "Quit" t) - (const :tag "Do not quit" nil))))) - -(defcustom embark-confirm-act-all t - "Should `embark-act-all' prompt the user for confirmation? -Even if this variable is nil you may still be prompted to confirm -some uses of `embark-act-all', namely, for those actions whose -entry in `embark-pre-action-hooks' includes `embark--confirm'." - :type 'boolean) - -(defcustom embark-default-action-overrides nil - "Alist associating target types with overriding default actions. -When the source of a target is minibuffer completion, the default -action for it is usually the command that opened the minibuffer -in the first place but this can be overridden for a given type by -an entry in this list. - -For example, if you run `delete-file' the default action for its -completion candidates is `delete-file' itself. You may prefer to -make `find-file' the default action for all files, even if they -were obtained from a `delete-file' prompt. In that case you can -configure that by adding an entry to this variable pairing `file' -with `find-file'. - -In addition to target types, you can also use as keys in this alist, -pairs of a target type and a command name. Such a pair indicates that -the override only applies if the target was obtained from minibuffer -completion from that command. For example adding an -entry (cons (cons \\='file \\='delete-file) \\='find-file) to this alist would -indicate that for files at the prompt of the `delete-file' command, -`find-file' should be used as the default action." - :type '(alist :key-type (choice (symbol :tag "Type") - (cons (symbol :tag "Type") - (symbol :tag "Command"))) - :value-type (function :tag "Default action"))) - -(defcustom embark-target-injection-hooks - '((async-shell-command embark--allow-edit embark--shell-prep) - (shell-command embark--allow-edit embark--shell-prep) - (pp-eval-expression embark--eval-prep) - (eval-expression embark--eval-prep) - (package-delete embark--force-complete) - ;; commands evaluating code found in the buffer, which may in turn prompt - (embark-pp-eval-defun embark--ignore-target) - (eval-defun embark--ignore-target) - (eval-last-sexp embark--ignore-target) - (embark-eval-replace embark--ignore-target) - ;; commands which prompt for something that is *not* the target - (write-region embark--ignore-target) - (append-to-file embark--ignore-target) - (append-to-buffer embark--ignore-target) - (shell-command-on-region embark--ignore-target) - (format-encode-region embark--ignore-target) - (format-decode-region embark--ignore-target) - (xref-find-definitions embark--ignore-target) - (xref-find-references embark--ignore-target) - (sort-regexp-fields embark--ignore-target) - (align-regexp embark--ignore-target)) - "Alist associating commands with post-injection setup hooks. -For commands appearing as keys in this alist, run the -corresponding value as a setup hook after injecting the target -into in the minibuffer and before acting on it. The hooks must -accept arbitrary keyword arguments. The :action command, the -:target string and target :type are always present. For actions -at point the target :bounds are passed too. The default pre-action -hook is specified by the entry with key t. Furthermore, hooks with -the key :always are executed always." - :type '(alist :key-type - (choice symbol - (const :tag "Default" t) - (const :tag "Always" :always)) - :value-type hook)) - -(defcustom embark-pre-action-hooks - `(;; commands that need to position point at the beginning or end - (eval-last-sexp embark--end-of-target) - (indent-pp-sexp embark--beginning-of-target) - (backward-up-list embark--beginning-of-target) - (backward-list embark--beginning-of-target) - (forward-list embark--end-of-target) - (forward-sexp embark--end-of-target) - (backward-sexp embark--beginning-of-target) - (raise-sexp embark--beginning-of-target) - (kill-sexp embark--beginning-of-target) - (mark-sexp embark--beginning-of-target) - (transpose-sexps embark--end-of-target) - (transpose-sentences embark--end-of-target) - (transpose-paragraphs embark--end-of-target) - (forward-sentence embark--end-of-target) - (backward-sentence embark--beginning-of-target) - (backward-paragraph embark--beginning-of-target) - (embark-insert embark--end-of-target) - ;; commands we want to be able to jump back from - ;; (embark-find-definition achieves this by calling - ;; xref-find-definitions which pushes the markers itself) - (find-library embark--xref-push-marker) - ;; commands which prompt the user for confirmation before running - (delete-file embark--confirm) - (delete-directory embark--confirm) - (kill-buffer embark--confirm) - (embark-kill-buffer-and-window embark--confirm) - (bookmark-delete embark--confirm) - (package-delete embark--confirm) - (,'tab-bar-close-tab-by-name embark--confirm) ;; Avoid package-lint warning - ;; search for region contents outside said region - (embark-isearch-forward embark--unmark-target) - (embark-isearch-backward embark--unmark-target) - (occur embark--unmark-target) - (query-replace embark--beginning-of-target embark--unmark-target) - (query-replace-regexp embark--beginning-of-target embark--unmark-target) - (replace-string embark--beginning-of-target embark--unmark-target) - (replace-regexp embark--beginning-of-target embark--unmark-target) - ;; mark pseudo-action - (mark embark--mark-target) - ;; shells in new buffers - (shell embark--universal-argument) - (eshell embark--universal-argument)) - "Alist associating commands with pre-action hooks. -The hooks are run right before an action is embarked upon. See -`embark-target-injection-hooks' for information about the hook -arguments and more details." - :type '(alist :key-type - (choice symbol - (const :tag "Default" t) - (const :tag "Always" :always)) - :value-type hook)) - -(defcustom embark-post-action-hooks - `((bookmark-delete embark--restart) - (bookmark-rename embark--restart) - (delete-file embark--restart) - (embark-kill-ring-remove embark--restart) - (embark-recentf-remove embark--restart) - (embark-history-remove embark--restart) - (rename-file embark--restart) - (copy-file embark--restart) - (delete-directory embark--restart) - (make-directory embark--restart) - (kill-buffer embark--restart) - (embark-rename-buffer embark--restart) - (,'tab-bar-rename-tab-by-name embark--restart) ;; Avoid package-lint warning - (,'tab-bar-close-tab-by-name embark--restart) - (package-delete embark--restart)) - "Alist associating commands with post-action hooks. -The hooks are run after an embarked upon action concludes. See -`embark-target-injection-hooks' for information about the hook -arguments and more details." - :type '(alist :key-type - (choice symbol - (const :tag "Default" t) - (const :tag "Always" :always)) - :value-type hook)) - -(defcustom embark-around-action-hooks - '(;; use directory of target as default-directory - (shell embark--cd) - (eshell embark--cd) - ;; mark the target preserving point and previous mark - (kill-region embark--mark-target) - (kill-ring-save embark--mark-target) - (indent-region embark--mark-target) - (ispell-region embark--mark-target) - (fill-region embark--mark-target) - (upcase-region embark--mark-target) - (downcase-region embark--mark-target) - (capitalize-region embark--mark-target) - (count-words-region embark--mark-target) - (count-words embark--mark-target) - (delete-duplicate-lines embark--mark-target) - (shell-command-on-region embark--mark-target) - (delete-region embark--mark-target) - (format-encode-region embark--mark-target) - (format-decode-region embark--mark-target) - (write-region embark--mark-target) - (append-to-file embark--mark-target) - (append-to-buffer embark--mark-target) - (shell-command-on-region embark--mark-target) - (embark-eval-replace embark--mark-target) - (delete-indentation embark--mark-target) - (comment-dwim embark--mark-target) - (insert-parentheses embark--mark-target) - (insert-pair embark--mark-target) - (org-emphasize embark--mark-target) - ;; do the actual work of selecting & deselecting targets - (embark-select embark--select)) - "Alist associating commands with post-action hooks. -The hooks are run instead of the embarked upon action. The hook -can decide whether or not to run the action or it can run it -in some special environment, like inside a let-binding or inside -`save-excursion'. Each hook is called with keyword argument :run -providing a function encapsulating the following around hooks and -the action; the hook additionally receives the keyword arguments -used for other types of action hooks, for more details see -`embark-target-injection-hooks'." - :type '(alist :key-type - (choice symbol - (const :tag "Default" t) - (const :tag "Always" :always)) - :value-type hook)) - -(when (version-list-< (version-to-list emacs-version) '(29 1)) - ;; narrow to target for duration of action - (setf (alist-get 'repunctuate-sentences embark-around-action-hooks) - '(embark--narrow-to-target))) - -(defcustom embark-multitarget-actions '(embark-insert embark-copy-as-kill) - "Commands for which `embark-act-all' should pass a list of targets. -Normally `embark-act-all' runs the same action on each candidate -separately, but when a command included in this variable's value -is used as an action, `embark-act-all' will instead call it -non-interactively with a single argument: the list of all -candidates. For commands on this list `embark-act' behaves -similarly: it calls them non-interactively with a single -argument: a one element list containing the target." - :type '(repeat function)) - -(defcustom embark-repeat-actions - '((mark . region) - ;; outline commands - outline-next-visible-heading outline-previous-visible-heading - outline-forward-same-level outline-backward-same-level - outline-demote outline-promote - outline-show-subtree (outline-mark-subtree . region) - outline-move-subtree-up outline-move-subtree-down - outline-up-heading outline-hide-subtree outline-cycle - ;; org commands (remapped outline commands) - org-forward-heading-same-level org-backward-heading-same-level - org-next-visible-heading org-previous-visible-heading - org-demote-subtree org-promote-subtree - org-show-subtree (org-mark-subtree . region) - org-move-subtree-up org-move-subtree-down - ;; transpose commands - transpose-sexps transpose-sentences transpose-paragraphs - ;; navigation commands - flymake-goto-next-error flymake-goto-prev-error - embark-next-symbol embark-previous-symbol - backward-up-list backward-list forward-list forward-sexp - backward-sexp forward-sentence backward-sentence - forward-paragraph backward-paragraph - ;; smerge commands - smerge-refine smerge-combine-with-next smerge-prev smerge-next) - "List of repeatable actions. -When you use a command on this list as an Embark action from -outside the minibuffer, `embark-act' does not exit but instead -lets you act again on the possibly new target you reach. - -By default, after using one of these actions, when `embark-act' -looks for targets again, it will start the target cycle at the -same type as the previously acted upon target; that is, you -\"don't loose your place in the target cycle\". - -Sometimes, however, you'll want to prioritize a different type of -target to continue acting on. The main example of this that if -you use a marking command as an action, you almost always want to -act on the region next. For those cases, in addition to -commands, you can also place on this list a pair of a command and -the desired starting type for the target cycle for the next -action." - :type '(repeat (choice function - (cons function - (symbol :tag "Next target type"))))) - -;;; Overlay properties - -;; high priority to override both bug reference and the lazy -;; isearch highlights in embark-isearch-highlight-indicator -(put 'embark-target-overlay 'face 'embark-target) -(put 'embark-target-overlay 'priority 1001) -(put 'embark-selected-overlay 'face 'embark-selected) -(put 'embark-selected-overlay 'priority 1001) - -;;; Stashing information for actions in buffer local variables - -(defvar-local embark--type nil - "Cache for the completion type, meant to be set buffer-locally.") - -(defvar-local embark--target-buffer nil - "Cache for the previous buffer, meant to be set buffer-locally.") - -(defvar-local embark--target-window nil - "Cache for the previous window, meant to be set buffer-locally. -Since windows can be reused to display different buffers, this -window should only be used if it displays the buffer stored in -the variable `embark--target-buffer'.") - -(defvar-local embark--command nil - "Command that started the completion session.") - -(defvar-local embark--toggle-quit nil - "Should we toggle the default quitting behavior for the next action?") - -(defun embark--minibuffer-point () - "Return length of minibuffer contents." - (max 0 (- (point) (minibuffer-prompt-end)))) - -(defun embark--default-directory () - "Guess a reasonable default directory for the current candidates." - (if (and (minibufferp) minibuffer-completing-file-name) - (let ((end (minibuffer-prompt-end)) - (contents (minibuffer-contents))) - (expand-file-name - (substitute-in-file-name - (buffer-substring - end - (+ end - (or (cdr - (last - (completion-all-completions - contents - minibuffer-completion-table - minibuffer-completion-predicate - (embark--minibuffer-point)))) - (cl-position ?/ contents :from-end t) - 0)))))) - default-directory)) - -(defun embark--target-buffer () - "Return buffer that should be targeted by Embark actions." - (cond - ((and (minibufferp) minibuffer-completion-table (minibuffer-selected-window)) - (window-buffer (minibuffer-selected-window))) - ((and embark--target-buffer (buffer-live-p embark--target-buffer)) - embark--target-buffer) - (t (current-buffer)))) - -(defun embark--target-window (&optional display) - "Return window which should be selected when Embark actions run. -If DISPLAY is non-nil, call `display-buffer' to produce the -window if necessary." - (cond - ((and (minibufferp) minibuffer-completion-table (minibuffer-selected-window)) - (minibuffer-selected-window)) - ((and embark--target-window - (window-live-p embark--target-window) - (or (not (buffer-live-p embark--target-buffer)) - (eq (window-buffer embark--target-window) embark--target-buffer))) - embark--target-window) - ((and embark--target-buffer (buffer-live-p embark--target-buffer)) - (or (get-buffer-window embark--target-buffer) - (when display (display-buffer embark--target-buffer)))) - (display (selected-window)))) - -(defun embark--cache-info (buffer) - "Cache information needed for actions in variables local to BUFFER. -BUFFER defaults to the current buffer." - (let ((cmd embark--command) - (dir (embark--default-directory)) - (target-buffer (embark--target-buffer)) - (target-window (embark--target-window))) - (with-current-buffer buffer - (setq embark--command cmd - default-directory dir - embark--target-buffer target-buffer - embark--target-window target-window)))) - -(defun embark--cache-info--completion-list () - "Cache information needed for actions in a *Completions* buffer. -Meant to be be added to `completion-setup-hook'." - ;; when completion-setup-hook hook runs, the *Completions* buffer is - ;; available in the variable standard-output - (embark--cache-info standard-output) - (with-current-buffer standard-output - (when (minibufferp completion-reference-buffer) - (setq embark--type - (completion-metadata-get - (with-current-buffer completion-reference-buffer - (embark--metadata)) - 'category))))) - -;; We have to add this *after* completion-setup-function because that's -;; when the buffer is put in completion-list-mode and turning the mode -;; on kills all local variables! So we use a depth of 5. -(add-hook 'completion-setup-hook #'embark--cache-info--completion-list 5) - -;;;###autoload -(progn - (defun embark--record-this-command () - "Record command which opened the minibuffer. -We record this because it will be the default action. -This function is meant to be added to `minibuffer-setup-hook'." - (setq-local embark--command this-command)) - (add-hook 'minibuffer-setup-hook #'embark--record-this-command)) - -;;; Internal variables - -(defvar embark--prompter-history nil - "History used by the `embark-completing-read-prompter'.") - -;;; Core functionality - -(defconst embark--verbose-indicator-buffer " *Embark Actions*") - -(defvar embark--minimal-indicator-overlay nil) - -(defun embark--metadata () - "Return current minibuffer completion metadata." - (completion-metadata - (buffer-substring-no-properties - (minibuffer-prompt-end) - (max (minibuffer-prompt-end) (point))) - minibuffer-completion-table - minibuffer-completion-predicate)) - -(defun embark-target-active-region () - "Target the region if active." - (when (use-region-p) - (let ((start (region-beginning)) - (end (region-end))) - `(region ,(buffer-substring start end) . (,start . ,end))))) - -(autoload 'dired-get-filename "dired") -(declare-function image-dired-original-file-name "image-dired") - -(defun embark-target-guess-file-at-point () - "Target the file guessed by `ffap' at point." - (when-let ((tap-file (thing-at-point 'filename)) - ((not (ffap-url-p tap-file))) ; no URLs, those have a target finder - (bounds (bounds-of-thing-at-point 'filename)) - (file (ffap-file-at-point))) - ;; ffap doesn't make bounds available, so we use - ;; thingatpt bounds, which might be a little off - ;; adjust bounds if thingatpt gobbled punctuation around file - (when (or (string-match (regexp-quote file) tap-file) - (string-match (regexp-quote (file-name-base file)) tap-file)) - (setq bounds (cons (+ (car bounds) (match-beginning 0)) - (- (cdr bounds) (- (length tap-file) - (match-end 0)))))) - `(file ,(abbreviate-file-name (expand-file-name file)) ,@bounds))) - -(defun embark-target-file-at-point () - "Target file at point. -This function mostly relies on `ffap-file-at-point', with the -following exceptions: - -- In `dired-mode', it uses `dired-get-filename' instead. - -- In `imaged-dired-thumbnail-mode', it uses - `image-dired-original-file-name' instead." - (let (file bounds) - (or (and (derived-mode-p 'dired-mode) - (setq file (dired-get-filename t 'no-error-if-not-filep)) - (setq bounds - (cons - (save-excursion (dired-move-to-filename) (point)) - (save-excursion (dired-move-to-end-of-filename) (point))))) - (and (derived-mode-p 'image-dired-thumbnail-mode) - (setq file (image-dired-original-file-name)) - (setq bounds (cons (point) (1+ (point))))) - (when-let ((tap-file (thing-at-point 'filename)) - ((not (equal (file-name-base tap-file) tap-file))) - (guess (embark-target-guess-file-at-point))) - (setq file (cadr guess) bounds (cddr guess)))) - (when file - `(file ,(abbreviate-file-name (expand-file-name file)) ,@bounds)))) - -(defun embark-target-package-at-point () - "Target the package on the current line in a packages buffer." - (when (derived-mode-p 'package-menu-mode) - (when-let ((pkg (get-text-property (point) 'tabulated-list-id))) - `(package ,(symbol-name (package-desc-name pkg)) - ,(line-beginning-position) . ,(line-end-position))))) - -(defun embark-target-email-at-point () - "Target the email address at point." - (when-let ((email (thing-at-point 'email))) - (when (string-prefix-p "mailto:" email) - (setq email (string-remove-prefix "mailto:" email))) - `(email ,email . ,(bounds-of-thing-at-point 'email)))) - -(defun embark-target-url-at-point () - "Target the URL at point." - (if-let ((url (or (get-text-property (point) 'shr-url) - (get-text-property (point) 'image-url)))) - `(url ,url - ,(previous-single-property-change - (min (1+ (point)) (point-max)) 'mouse-face nil (point-min)) - . ,(next-single-property-change - (point) 'mouse-face nil (point-max))) - (when-let ((url (thing-at-point 'url))) - `(url ,url . ,(thing-at-point-bounds-of-url-at-point t))))) - -(declare-function widget-at "wid-edit") - -(defun embark-target-custom-variable-at-point () - "Target the variable corresponding to the customize widget at point." - (when (derived-mode-p 'Custom-mode) - (save-excursion - (beginning-of-line) - (when-let* ((widget (widget-at (point))) - (var (and (eq (car widget) 'custom-visibility) - (plist-get (cdr widget) :parent))) - (sym (and (eq (car var) 'custom-variable) - (plist-get (cdr var) :value)))) - `(variable - ,(symbol-name sym) - ,(point) - . ,(progn - (re-search-forward ":" (line-end-position) 'noerror) - (point))))))) - -;; NOTE: There is also (thing-at-point 'list), however it does -;; not work on strings and requires the point to be inside the -;; parentheses. This version here is slightly more general. -(defun embark-target-expression-at-point () - "Target expression at point." - (cl-flet ((syntax-p (class &optional (delta 0)) - (and (<= (point-min) (+ (point) delta) (point-max)) - (eq (pcase class - ('open 4) ('close 5) ('prefix 6) ('string 7)) - (syntax-class (syntax-after (+ (point) delta))))))) - (when-let - ((start - (pcase-let ((`(_ ,open _ ,string _ _ _ _ ,start _ _) (syntax-ppss))) - (ignore-errors ; set start=nil if delimiters are unbalanced - (cond - (string start) - ((or (syntax-p 'open) (syntax-p 'prefix)) - (save-excursion (backward-prefix-chars) (point))) - ((syntax-p 'close -1) - (save-excursion - (backward-sexp) (backward-prefix-chars) (point))) - ((syntax-p 'string) (point)) - ((syntax-p 'string -1) (scan-sexps (point) -1)) - (t open))))) - (end (ignore-errors (scan-sexps start 1)))) - (unless (eq start (car (bounds-of-thing-at-point 'defun))) - `(expression ,(buffer-substring start end) ,start . ,end))))) - -(defmacro embark-define-overlay-target (name prop &optional pred type target) - "Define a target finder for NAME that targets overlays with property PROP. -The function defined is named embark-target-NAME-at-point and it -returns Embark targets based on the overlays around point. An -overlay provides a target if its property named PROP is non-nil. - -If the optional PRED argument is given, it should be an -expression and it further restricts the targets to only those -overlays for which PRED evaluates to non-nil. - -The target finder returns target type NAME or optional symbol -TYPE if given. - -The target finder returns the substring of the buffer covered by -the overlay as the target string or the result of evaluating the -optional TARGET expression if given. - -PRED and TARGET are expressions (not functions) and when evaluated the -symbols `%o' and `%p' are bound to the overlay and the overlay's -property respectively." - `(defun ,(intern (format "embark-target-%s-at-point" name)) () - ,(format "Target %s at point." name) - (when-let ((%o (seq-find - (lambda (%o) - (when-let ((%p (overlay-get %o ',prop))) - (ignore %p) - ,(or pred t))) - (overlays-in (max (point-min) (1- (point))) - (min (point-max) (1+ (point)))))) - (%p (overlay-get %o ',prop))) - (ignore %p) - (cons ',(or type name) - (cons ,(or target `(buffer-substring-no-properties - (overlay-start %o) (overlay-end %o))) - (cons (overlay-start %o) (overlay-end %o))))))) - -(embark-define-overlay-target flymake flymake-diagnostic) -(embark-define-overlay-target bug-reference bug-reference-url nil url %p) -(embark-define-overlay-target smerge smerge (eq %p 'conflict)) - -(defmacro embark-define-thingatpt-target (thing &rest modes) - "Define a target finder for THING using the thingatpt library. -The function defined is named embark-target-NAME-at-point and it -uses (thing-at-point 'THING) to find its targets. - -If any MODES are given, the target finder only applies to buffers -in one of those major modes." - (declare (indent 1)) - `(defun ,(intern (format "embark-target-%s-at-point" thing)) () - ,(format "Target %s at point." thing) - (when ,(if modes `(derived-mode-p ,@(mapcar (lambda (m) `',m) modes)) t) - (when-let (bounds (bounds-of-thing-at-point ',thing)) - (cons ',thing (cons - (buffer-substring (car bounds) (cdr bounds)) - bounds)))))) - -(embark-define-thingatpt-target defun) -(embark-define-thingatpt-target sentence - text-mode help-mode Info-mode man-common) -(embark-define-thingatpt-target paragraph - text-mode help-mode Info-mode man-common) - -(defmacro embark-define-regexp-target - (name regexp &optional type target bounds limit) - "Define a target finder for matches of REGEXP around point. -The function defined is named embark-target-NAME-at-point and it -uses (thing-at-point-looking-at REGEXP) to find its targets. - -The target finder returns target type NAME or optional symbol -TYPE if given. - -The target finder returns the substring of the buffer matched by -REGEXP as the target string or the result of evaluating the -optional TARGET expression if given. In the expression TARGET -you can use `match-string' to recover the match of the REGEXP or -of any sub-expressions it has. - -BOUNDS is an optional expression to compute the bounds of the -target and defaults to (cons (match-beginning 0) (match-end 0)). - -The optional LIMIT is the number of characters before and after -point to limit the search to. If LIMIT is nil, search a little -more than the current line (more precisely, the smallest interval -centered at point that includes the current line)." - `(defun ,(intern (format "embark-target-%s-at-point" name)) () - ,(format "Target %s at point." name) - (save-match-data - (when (thing-at-point-looking-at - ,regexp - ,(or limit '(max (- (pos-eol) (point)) (- (point) (pos-bol))))) - (cons ',(or type name) - (cons ,(or target '(match-string 0)) - ,(or bounds - '(cons (match-beginning 0) (match-end 0))))))))) - -(defun embark--identifier-types (identifier) - "Return list of target types appropriate for IDENTIFIER." - (let ((symbol (intern-soft identifier))) - (if (not - (or (derived-mode-p 'emacs-lisp-mode 'inferior-emacs-lisp-mode) - (and (not (derived-mode-p 'prog-mode)) - symbol - (or (boundp symbol) (fboundp symbol) (symbol-plist symbol))))) - '(identifier) - (let* ((library (ffap-el-mode identifier)) - (types - (append - (and (commandp symbol) '(command)) - (and symbol (boundp symbol) (not (keywordp symbol)) '(variable)) - (and (fboundp symbol) (not (commandp symbol)) '(function)) - (and (facep symbol) '(face)) - (and library '(library)) - (and (featurep 'package) (embark--package-desc symbol) - '(package))))) - (when (and library - (looking-back "\\(?:require\\|use-package\\).*" - (line-beginning-position))) - (setq types (embark--rotate types (cl-position 'library types)))) - (or types '(symbol)))))) - -(defun embark-target-identifier-at-point () - "Target identifier at point. - -In Emacs Lisp and IELM buffers the identifier is promoted to a -symbol, for which more actions are available. Identifiers are -also promoted to symbols if they are interned Emacs Lisp symbols -and found in a buffer in a major mode that is not derived from -`prog-mode' (this is intended for when you might be reading or -writing about Emacs). - -As a convenience, in Org Mode an initial ' or surrounding == or -~~ are removed." - (when-let (bounds (bounds-of-thing-at-point 'symbol)) - (let ((name (buffer-substring (car bounds) (cdr bounds)))) - (when (derived-mode-p 'org-mode) - (cond ((string-prefix-p "'" name) - (setq name (substring name 1)) - (cl-incf (car bounds))) - ((string-match-p "^\\([=~]\\).*\\1$" name) - (setq name (substring name 1 -1)) - (cl-incf (car bounds)) - (cl-decf (cdr bounds))))) - (mapcar (lambda (type) `(,type ,name . ,bounds)) - (embark--identifier-types name))))) - -(defun embark-target-heading-at-point () - "Target the outline heading at point." - (let ((beg (line-beginning-position)) - (end (line-end-position))) - (when (save-excursion - (goto-char beg) - (and (bolp) - (looking-at - ;; default definition from outline.el - (or (bound-and-true-p outline-regexp) "[*\^L]+")))) - (require 'outline) ;; Ensure that outline commands are available - `(heading ,(buffer-substring beg end) ,beg . ,end)))) - -(defun embark-target-text-heading-at-point () - "Target the outline heading at point in text modes." - (when (derived-mode-p 'text-mode) - (embark-target-heading-at-point))) - -(defun embark-target-prog-heading-at-point () - "Target the outline heading at point in programming modes." - (when (derived-mode-p 'prog-mode) - (embark-target-heading-at-point))) - -(defun embark-target-top-minibuffer-candidate () - "Target the top completion candidate in the minibuffer. -Return the category metadatum as the type of the target. - -This target finder is meant for the default completion UI and -completion UI highly compatible with it, like Icomplete. -Many completion UIs can still work with Embark but will need -their own target finder. See for example -`embark--vertico-selected'." - (when (and (minibufferp) minibuffer-completion-table) - (pcase-let* ((`(,category . ,candidates) (embark-minibuffer-candidates)) - (contents (minibuffer-contents)) - (top (if (test-completion contents - minibuffer-completion-table - minibuffer-completion-predicate) - contents - (let ((completions (completion-all-sorted-completions))) - (if (null completions) - contents - (concat - (substring contents - 0 (or (cdr (last completions)) 0)) - (car completions))))))) - (cons category (or (car (member top candidates)) top))))) - -(defun embark-target-collect-candidate () - "Target the collect candidate at point." - (when (derived-mode-p 'embark-collect-mode) - (when-let ((button - (pcase (get-text-property (point) 'tabulated-list-column-name) - ("Candidate" (button-at (point))) - ("Annotation" (previous-button (point))))) - (start (button-start button)) - (end (button-end button)) - (candidate (tabulated-list-get-id))) - `(,embark--type - ,(if (eq embark--type 'file) - (abbreviate-file-name (expand-file-name candidate)) - candidate) - ,start . ,end)))) - -(defun embark-target-completion-list-candidate () - "Return the completion candidate at point in a completions buffer." - (when (derived-mode-p 'completion-list-mode) - (if (not (get-text-property (point) 'mouse-face)) - (user-error "No completion here") - ;; this fairly delicate logic is taken from `choose-completion' - (let (beg end) - (cond - ((and (not (eobp)) (get-text-property (point) 'mouse-face)) - (setq end (point) beg (1+ (point)))) - ((and (not (bobp)) - (get-text-property (1- (point)) 'mouse-face)) - (setq end (1- (point)) beg (point))) - (t (user-error "No completion here"))) - (setq beg (previous-single-property-change beg 'mouse-face)) - (setq end (or (next-single-property-change end 'mouse-face) - (point-max))) - (let ((raw (or (get-text-property beg 'completion--string) - (buffer-substring beg end)))) - `(,embark--type - ,(if (eq embark--type 'file) - (abbreviate-file-name (expand-file-name raw)) - raw) - ,beg . ,end)))))) - -(defun embark--cycle-key () - "Return the key to use for `embark-cycle'." - (if embark-cycle-key - (if (key-valid-p embark-cycle-key) - (key-parse embark-cycle-key) - (error "`embark-cycle-key' is invalid")) - (car (where-is-internal #'embark-act)))) - -(defun embark--raw-action-keymap (type) - "Return raw action map for targets of given TYPE. -This does not take into account the default action, help key or -cycling bindings, just what's registered in -`embark-keymap-alist'." - (make-composed-keymap - (mapcar #'symbol-value - (let ((actions (or (alist-get type embark-keymap-alist) - (alist-get t embark-keymap-alist)))) - (ensure-list actions))))) - -(defun embark--action-keymap (type cycle) - "Return action keymap for targets of given TYPE. -If CYCLE is non-nil bind `embark-cycle'." - (make-composed-keymap - (let ((map (make-sparse-keymap)) - (default-action (embark--default-action type))) - (define-key map [13] default-action) - (when-let ((cycle-key (and cycle (embark--cycle-key)))) - (define-key map cycle-key #'embark-cycle)) - (when embark-help-key - (keymap-set map embark-help-key #'embark-keymap-help)) - map) - (embark--raw-action-keymap type))) - -(defun embark--truncate-target (target) - "Truncate TARGET string." - (unless (stringp target) - (setq target (format "%s" target))) - (if-let (pos (string-match-p "\n" target)) - (concat (car (split-string target "\n" 'omit-nulls "\\s-*")) "…") - target)) - -;;;###autoload -(defun embark-eldoc-first-target (report &rest _) - "Eldoc function reporting the first Embark target at point. -This function uses the eldoc REPORT callback and is meant to be -added to `eldoc-documentation-functions'." - (when-let (((not (minibufferp))) - (target (car (embark--targets)))) - (funcall report - (format "Embark on %s ‘%s’" - (plist-get target :type) - (embark--truncate-target (plist-get target :target)))))) - -;;;###autoload -(defun embark-eldoc-target-types (report &rest _) - "Eldoc function reporting the types of all Embark targets at point. -This function uses the eldoc REPORT callback and is meant to be -added to `eldoc-documentation-functions'." - (when-let (((not (minibufferp))) - (targets (embark--targets))) - (funcall report - (format "Embark target types: %s" - (mapconcat - (lambda (target) (symbol-name (plist-get target :type))) - targets - ", "))))) - -(defun embark--format-targets (target shadowed-targets rep) - "Return a formatted string indicating the TARGET of an action. - -This is used internally by the minimal indicator and for the -targets section of the verbose indicator. The string will also -mention any SHADOWED-TARGETS. A non-nil REP indicates we are in -a repeating sequence of actions." - (let ((act (propertize - (cond - ((plist-get target :multi) "∀ct") - (rep "Rep") - (t "Act")) - 'face 'highlight))) - (cond - ((eq (plist-get target :type) 'embark-become) - (propertize "Become" 'face 'highlight)) - ((and (minibufferp) - (not (eq 'embark-keybinding - (completion-metadata-get - (embark--metadata) - 'category)))) - ;; we are in a minibuffer but not from the - ;; completing-read prompter, use just "Act" - act) - ((plist-get target :multi) - (format "%s on %s %ss" - act - (plist-get target :multi) - (plist-get target :type))) - (t (format - "%s on %s%s ‘%s’" - act - (plist-get target :type) - (if shadowed-targets - (format (propertize "(%s)" 'face 'shadow) - (mapconcat - (lambda (target) (symbol-name (plist-get target :type))) - shadowed-targets - ", ")) - "") - (embark--truncate-target (plist-get target :target))))))) - -(defun embark-minimal-indicator () - "Minimal indicator, appearing in the minibuffer prompt or echo area. -This indicator displays a message showing the types of all -targets, starting with the current target, and the value of the -current target. The message is displayed in the echo area, or if -the minibuffer is open, the message is added to the prompt." - (lambda (&optional keymap targets _prefix) - (if (null keymap) - (when embark--minimal-indicator-overlay - (delete-overlay embark--minimal-indicator-overlay) - (setq-local embark--minimal-indicator-overlay nil)) - (let ((indicator (embark--format-targets - (car targets) (cdr targets) - (eq (lookup-key keymap [13]) #'embark-done)))) - (if (not (minibufferp)) - (message "%s" indicator) - (unless embark--minimal-indicator-overlay - (setq-local embark--minimal-indicator-overlay - (make-overlay (point-min) (point-min) - (current-buffer) t t))) - (overlay-put embark--minimal-indicator-overlay - 'before-string (concat indicator - (if (<= (length indicator) - (* 0.4 (frame-width))) - " " - "\n")))))))) - -(defun embark--read-key-sequence (update) - "Read key sequence, call UPDATE function with prefix keys." - (let (timer prefix) - (unwind-protect - (progn - (when (functionp update) - (setq timer (run-at-time - 0.05 0.05 - (lambda () - (let ((new-prefix (this-single-command-keys))) - (unless (equal prefix new-prefix) - (setq prefix new-prefix) - (when (/= (length prefix) 0) - (funcall update prefix)))))))) - (read-key-sequence-vector nil nil nil t 'cmd-loop)) - (when timer - (cancel-timer timer))))) - -(defvar embark-indicators) ; forward declaration - -(defun embark-keymap-prompter (keymap update) - "Let the user choose an action using the bindings in KEYMAP. -Besides the bindings in KEYMAP, the user is free to use all their -key bindings and even \\[execute-extended-command] to select a command. -UPDATE is the indicator update function." - (let* ((keys (let ((overriding-terminal-local-map keymap)) - (embark--read-key-sequence update))) - (cmd (let ((overriding-terminal-local-map keymap)) - (key-binding keys 'accept-default)))) - ;; Set last-command-event as it would be from the command loop. - ;; Previously we only set it locally for digit-argument and for - ;; the mouse scroll commands handled in this function. But other - ;; commands can need it too! For example, electric-pair-mode users - ;; may wish to bind ( to self-insert-command in embark-region-map. - ;; Also, as described in issue #402, there are circumstances where - ;; you might run consult-narrow through the embark-keymap-prompter. - (setq last-command-event (aref keys (1- (length keys)))) - (pcase cmd - ((or 'embark-keymap-help - (and 'nil ; cmd is nil but last key is help-char - (guard (eq help-char (aref keys (1- (length keys))))))) - (let ((embark-indicators - (cl-set-difference embark-indicators - '(embark-verbose-indicator - embark-mixed-indicator))) - (prefix-map - (if (eq cmd 'embark-keymap-help) - keymap - (let ((overriding-terminal-local-map keymap)) - (key-binding (seq-take keys (1- (length keys))) - 'accept-default)))) - (prefix-arg prefix-arg)) ; preserve prefix arg - (when-let ((win (get-buffer-window embark--verbose-indicator-buffer - 'visible))) - (quit-window 'kill-buffer win)) - (embark-completing-read-prompter prefix-map update))) - ((or 'universal-argument 'universal-argument-more - 'negative-argument 'digit-argument 'embark-toggle-quit) - ;; prevent `digit-argument' from modifying the overriding map - (let ((overriding-terminal-local-map overriding-terminal-local-map)) - (command-execute cmd)) - (embark-keymap-prompter - (make-composed-keymap universal-argument-map keymap) - update)) - ((or 'minibuffer-keyboard-quit 'abort-recursive-edit 'abort-minibuffers) - nil) - ((guard (let ((def (lookup-key keymap keys))) ; if directly - ; bound, then obey - (and def (not (numberp def))))) ; number means "invalid prefix" - cmd) - ((and (pred symbolp) - (guard (string-suffix-p "self-insert-command" (symbol-name cmd)))) - (minibuffer-message "Not an action") - (embark-keymap-prompter keymap update)) - ((or 'scroll-other-window 'scroll-other-window-down) - (let ((minibuffer-scroll-window - ;; NOTE: Here we special case the verbose indicator! - (or (get-buffer-window embark--verbose-indicator-buffer 'visible) - minibuffer-scroll-window))) - (ignore-errors (command-execute cmd))) - (embark-keymap-prompter keymap update)) - ((or 'scroll-bar-toolkit-scroll 'mwheel-scroll - 'mac-mwheel-scroll 'pixel-scroll-precision) - (funcall cmd last-command-event) - (embark-keymap-prompter keymap update)) - ('execute-extended-command - (let ((prefix-arg prefix-arg)) ; preserve prefix arg - (intern-soft (read-extended-command)))) - ((or 'keyboard-quit 'keyboard-escape-quit) - nil) - (_ cmd)))) - -(defun embark--command-name (cmd) - "Return an appropriate name for CMD. -If CMD is a symbol, use its symbol name; for lambdas, use the -first line of the documentation string; for keyboard macros use -`key-description'; otherwise use the word \"unnamed\"." - (concat ; fresh copy, so we can freely add text properties - (cond - ((or (stringp cmd) (vectorp cmd)) (key-description cmd)) - ((stringp (car-safe cmd)) (car cmd)) - ((eq (car-safe cmd) 'menu-item) (eval (cadr cmd))) - ((keymapp cmd) - (propertize (if (symbolp cmd) (format "+%s" cmd) "") - 'face 'embark-keymap)) - ((symbolp cmd) - (let ((name (symbol-name cmd))) - (if (string-prefix-p "embark-action--" name) ; direct action mode - (format "(%s)" (string-remove-prefix "embark-action--" name)) - name))) - ((when-let (doc (and (functionp cmd) (ignore-errors (documentation cmd)))) - (save-match-data - (when (string-match "^\\(.*\\)$" doc) - (match-string 1 doc))))) - (t "")))) - -;; Taken from Marginalia, needed by the verbose indicator. -;; We cannot use the completion annotators in this case. -(defconst embark--advice-regexp - (rx bos - (1+ (seq (? "This function has ") - (or ":before" ":after" ":around" ":override" - ":before-while" ":before-until" ":after-while" - ":after-until" ":filter-args" ":filter-return") - " advice: " (0+ nonl) "\n")) - "\n") - "Regexp to match lines about advice in function documentation strings.") - -;; Taken from Marginalia, needed by the verbose indicator. -;; We cannot use the completion annotators in this case. -(defun embark--function-doc (sym) - "Documentation string of function SYM." - (let ((vstr (and (symbolp sym) (keymapp sym) (boundp sym) - (eq (symbol-function sym) (symbol-value sym)) - (documentation-property sym 'variable-documentation)))) - (when-let (str (or (ignore-errors (documentation sym)) vstr)) - ;; Replace standard description with variable documentation - (when (and vstr (string-match-p "\\`Prefix command" str)) - (setq str vstr)) - (save-match-data - (if (string-match embark--advice-regexp str) - (substring str (match-end 0)) - str))))) - -(defun embark--action-repeatable-p (action) - "Is ACTION repeatable? -When the return value is non-nil it will be the desired starting -point of the next target cycle or t to indicate the default, -namely that the target cycle for the next action should begin at -the type of the current target." - (or (cdr (assq action embark-repeat-actions)) - (and (memq action embark-repeat-actions) t))) - -(defun embark--formatted-bindings (keymap &optional nested) - "Return the formatted keybinding of KEYMAP. -The keybindings are returned in their order of appearance. -If NESTED is non-nil subkeymaps are not flattened." - (let* ((commands - (cl-loop for (key . def) in (embark--all-bindings keymap nested) - for name = (embark--command-name def) - for cmd = (keymap--menu-item-binding def) - unless (memq cmd '(nil embark-keymap-help - negative-argument digit-argument)) - collect (list name cmd key - (concat - (if (eq (car-safe def) 'menu-item) - "menu-item" - (key-description key)))))) - (width (cl-loop for (_name _cmd _key desc) in commands - maximize (length desc))) - (default) - (candidates - (cl-loop for item in commands - for (name cmd key desc) = item - for desc-rep = - (concat - (propertize desc 'face 'embark-keybinding) - (and (embark--action-repeatable-p cmd) - embark-keybinding-repeat)) - for formatted = - (propertize - (concat desc-rep - (make-string (- width (length desc-rep) -1) ?\s) - name) - 'embark-command cmd) - when (equal key [13]) - do (setq default formatted) - collect (cons formatted item)))) - (cons candidates default))) - -(defun embark--with-category (category candidates) - "Return completion table for CANDIDATES of CATEGORY with sorting disabled." - (lambda (string predicate action) - (if (eq action 'metadata) - `(metadata (display-sort-function . identity) - (cycle-sort-function . identity) - (category . ,category)) - (complete-with-action - action candidates string predicate)))) - -(defun embark-completing-read-prompter (keymap update &optional no-default) - "Prompt via completion for a command bound in KEYMAP. -If NO-DEFAULT is t, no default value is passed to`completing-read'. - -UPDATE is the indicator update function. It is not used directly -here, but if the user switches to `embark-keymap-prompter', the -UPDATE function is passed to it." - (let* ((candidates+def (embark--formatted-bindings keymap)) - (candidates (car candidates+def)) - (def (and (not no-default) (cdr candidates+def))) - (buf (current-buffer)) - (choice - (catch 'choice - (minibuffer-with-setup-hook - (lambda () - (let ((map (make-sparse-keymap))) - (define-key map "\M-q" - (lambda () - (interactive) - (with-current-buffer buf - (embark-toggle-quit)))) - (when-let (cycle (embark--cycle-key)) - ;; Rebind `embark-cycle' in order allow cycling - ;; from the `completing-read' prompter. Additionally - ;; `embark-cycle' can be selected via - ;; `completing-read'. The downside is that this breaks - ;; recursively acting on the candidates of type - ;; embark-keybinding in the `completing-read' prompter. - (define-key map cycle - (cond - ((eq (lookup-key keymap cycle) 'embark-cycle) - (lambda () - (interactive) - (throw 'choice 'embark-cycle))) - ((null embark-cycle-key) - (lambda () - (interactive) - (minibuffer-message - "No cycling possible; press `%s' again to act." - (key-description cycle)) - (define-key map cycle #'embark-act)))))) - (when embark-keymap-prompter-key - (keymap-set map embark-keymap-prompter-key - (lambda () - (interactive) - (message "Press key binding") - (let ((cmd (embark-keymap-prompter keymap update))) - (if (null cmd) - (user-error "Unknown key") - (throw 'choice cmd)))))) - (use-local-map - (make-composed-keymap map (current-local-map))))) - (completing-read - "Command: " - (embark--with-category 'embark-keybinding candidates) - nil nil nil 'embark--prompter-history def))))) - (pcase (assoc choice candidates) - (`(,_formatted ,_name ,cmd ,key ,_desc) - ;; Set last-command-event as it would be from the command loop. - (setq last-command-event (aref key (1- (length key)))) - cmd) - ('nil (intern-soft choice))))) - -;;; Verbose action indicator - -(defgroup embark-indicators nil - "Indicators display information about actions and targets." - :group 'embark) - -(defcustom embark-indicators - '(embark-mixed-indicator - embark-highlight-indicator - embark-isearch-highlight-indicator) - "Indicator functions to use when acting or becoming. -The indicator functions are called from both `embark-act' and -from `embark-become' and should display information about this to -the user, such as: which of those two commands is running; a -description of the key bindings that are available for actions or -commands to become; and, in the case of `embark-act', the type -and value of the targets, and whether other targets are available -via `embark-cycle'. The indicator function is free to display as -much or as little of this information as desired and can use any -Emacs interface elements to do so. - -Embark comes with five such indicators: - -- `embark-minimal-indicator', which does not display any - information about keybindings, but does display types and - values of action targets in the echo area or minibuffer prompt, - -- `embark-verbose-indicator', which pops up a buffer containing - detailed information including key bindings and the first line - of the docstring of the commands they run, and - -- `embark-mixed-indicator', which combines the minimal and the - verbose indicator: the minimal indicator is shown first and the - verbose popup is shown after `embark-mixed-indicator-delay' - seconds. - -- `embark-highlight-indicator', which highlights the target - at point. - -- `embark-isearch-highlight-indicator', which when the target at - point is an identifier or symbol, lazily highlights all - occurrences of it. - -The protocol for indicator functions is as follows: - -When called from `embark-act', an indicator function is called -without arguments. The indicator function should then return a -closure, which captures the indicator state. The returned -closure must accept up to three optional arguments, the action -keymap, the targets (plists as returned by `embark--targets') and -the prefix keys typed by the user so far. The keymap, targets -and prefix keys may be updated when cycling targets at point -resulting in multiple calls to the closure. When called from -`embark-become', the indicator closure will be called with the -keymap of commands to become, a fake target list containing a -single target of type `embark-become' and whose value is the -minibuffer input, and the prefix set to nil. Note, in -particular, that if an indicator function wishes to distinguish -between `embark-act' and `embark-become' it should check whether -the `car' of the first target is `embark-become'. - -After the action has been performed the indicator closure is -called without arguments, such that the indicator can perform the -necessary cleanup work. For example, if the indicator adds -overlays, it should remove these overlays. The indicator should -be written in a way that it is safe to call it for cleanup more -than once, in fact, it should be able to handle any sequence of -update and cleanup calls ending in a call for cleanup. - -NOTE: Experience shows that the indicator calling convention may -change again in order to support more action features. The -calling convention should currently be considered unstable. -Please keep this in mind when writing a custom indicator -function, or when using the `which-key' indicator function from -the wiki." - :type '(repeat - (choice - (const :tag "Verbose indicator" embark-verbose-indicator) - (const :tag "Minimal indicator" embark-minimal-indicator) - (const :tag "Mixed indicator" embark-mixed-indicator) - (const :tag "Highlight target" embark-highlight-indicator) - (const :tag "Highlight all occurrences" - embark-isearch-highlight-indicator) - (function :tag "Other")))) - -(defface embark-verbose-indicator-documentation - '((t :inherit completions-annotations)) - "Face used by the verbose action indicator to display binding descriptions. -Used by `embark-verbose-indicator'.") - -(defface embark-verbose-indicator-title '((t :height 1.1 :weight bold)) - "Face used by the verbose action indicator for the title. -Used by `embark-verbose-indicator'.") - -(defface embark-verbose-indicator-shadowed '((t :inherit shadow)) - "Face used by the verbose action indicator for the shadowed targets. -Used by `embark-verbose-indicator'.") - -(defcustom embark-verbose-indicator-display-action - '(display-buffer-reuse-window) - "Parameters added to `display-buffer-alist' to show the actions buffer. -See the docstring of `display-buffer' for information on what -display actions and parameters are available." - :type `(choice - (const :tag "Reuse some window" - (display-buffer-reuse-window)) - (const :tag "Below target buffer" - (display-buffer-below-selected - (window-height . fit-window-to-buffer))) - (const :tag "Bottom of frame (fixed-size)" - (display-buffer-at-bottom)) - (const :tag "Bottom of frame (resizes during cycling)" - (display-buffer-at-bottom - (window-height . fit-window-to-buffer))) - (const :tag "Side window on the right" - (display-buffer-in-side-window (side . right))) - (const :tag "Side window on the left" - (display-buffer-in-side-window (side . left))) - (sexp :tag "Other"))) - -(defcustom embark-verbose-indicator-excluded-actions nil - "Commands not displayed by `embark-verbose-indicator'. -This variable should be set to a list of symbols and regexps. -The verbose indicator will exclude from its listing any commands -matching an element of this list." - :type '(choice - (const :tag "Exclude nothing" nil) - (const :tag "Exclude Embark general actions" - (embark-collect embark-live embark-export - embark-cycle embark-act-all embark-keymap-help - embark-become embark-isearch-forward - embark-isearch-backward)) - (repeat :tag "Other" (choice regexp symbol)))) - -(defcustom embark-verbose-indicator-buffer-sections - `(target "\n" shadowed-targets " " cycle "\n" bindings) - "List of sections to display in the verbose indicator buffer, in order. -You can use either a symbol designating a concrete section (one -of the keywords below, but without the colon), a string literal -or a function returning a string or list of strings to insert and -that accepts the following keyword arguments: - -- `:target', the target as a cons of type and value, -- `:shadowed-targets', a list of conses for the other targets, -- `:bindings' a list returned by `embark--formatted-bindings', and -- `:cycle', a string describing the key binding of `embark-cycle'." - :type '(repeat - (choice (const :tag "Current target name" target) - (const :tag "List of other shadowed targets" shadowed-targets) - (const :tag "Key bindings" bindings) - (const :tag "Cycle indicator" cycle) - (string :tag "Literal string") - (function :tag "Custom function")))) - -(defcustom embark-verbose-indicator-nested t - "Whether the verbose indicator should use nested keymap navigation. -When this variable is non-nil the actions buffer displayed by -`embark-verbose-indicator' will include any prefix keys found in -the keymap it is displaying, and will update to show what is -bound under the prefix if the prefix is pressed. If this -variable is nil, then the actions buffer will contain a flat list -of all full key sequences bound in the keymap." - :type 'boolean) - -(defun embark--verbose-indicator-excluded-p (cmd) - "Return non-nil if CMD should be excluded from the verbose indicator." - (seq-find (lambda (x) - (if (symbolp x) - (eq cmd x) - (string-match-p x (symbol-name cmd)))) - embark-verbose-indicator-excluded-actions)) - -(cl-defun embark--verbose-indicator-section-target - (&key targets bindings &allow-other-keys) - "Format the TARGETS section for the indicator buffer. -BINDINGS is the formatted list of keybindings." - (let ((result (embark--format-targets - (car targets) - nil ; the shadowed targets section deals with these - (cl-find 'embark-done bindings :key #'caddr :test #'eq)))) - (add-face-text-property 0 (length result) - 'embark-verbose-indicator-title - 'append - result) - result)) - -(cl-defun embark--verbose-indicator-section-cycle - (&key cycle shadowed-targets &allow-other-keys) - "Format the CYCLE key section for the indicator buffer. -SHADOWED-TARGETS is the list of other targets." - (concat - (and cycle (propertize (format "(%s to cycle)" cycle) - 'face 'embark-verbose-indicator-shadowed)) - (and shadowed-targets "\n"))) - -(cl-defun embark--verbose-indicator-section-shadowed-targets - (&key shadowed-targets &allow-other-keys) - "Format the SHADOWED-TARGETS section for the indicator buffer." - (when shadowed-targets - (propertize (format "Shadowed targets at point: %s" - (string-join shadowed-targets ", ")) - 'face 'embark-verbose-indicator-shadowed))) - -(cl-defun embark--verbose-indicator-section-bindings - (&key bindings &allow-other-keys) - "Format the BINDINGS section for the indicator buffer." - (let* ((max-width (apply #'max (cons 0 (mapcar (lambda (x) - (string-width (car x))) - bindings)))) - (fmt (format "%%-%ds" (1+ max-width))) - (result nil)) - (dolist (binding bindings (string-join (nreverse result))) - (let ((cmd (caddr binding))) - (unless (embark--verbose-indicator-excluded-p cmd) - (let ((keys (format fmt (car binding))) - (doc (embark--function-doc cmd))) - (push (format "%s%s\n" keys - (propertize - (car (split-string (or doc "") "\n")) - 'face 'embark-verbose-indicator-documentation)) - result))))))) - -(defun embark--verbose-indicator-update (keymap targets) - "Update verbose indicator buffer. -The arguments are the new KEYMAP and TARGETS." - (with-current-buffer (get-buffer-create embark--verbose-indicator-buffer) - (let* ((inhibit-read-only t) - (bindings - (embark--formatted-bindings keymap embark-verbose-indicator-nested)) - (bindings (car bindings)) - (shadowed-targets (mapcar - (lambda (x) (symbol-name (plist-get x :type))) - (cdr targets))) - (cycle (let ((ck (where-is-internal #'embark-cycle keymap))) - (and ck (key-description (car ck)))))) - (setq-local cursor-type nil) - (setq-local truncate-lines t) - (setq-local buffer-read-only t) - (erase-buffer) - (dolist (section embark-verbose-indicator-buffer-sections) - (insert - (if (stringp section) - section - (or (funcall - (let ((prefixed (intern (format - "embark--verbose-indicator-section-%s" - section)))) - (cond - ((fboundp prefixed) prefixed) - ((fboundp section) section) - (t (error "Undefined verbose indicator section `%s'" - section)))) - :targets targets :shadowed-targets shadowed-targets - :bindings bindings :cycle cycle) - "")))) - (goto-char (point-min))))) - -(defun embark-verbose-indicator () - "Indicator that displays a table of key bindings in a buffer. -The default display includes the type and value of the current -target, the list of other target types, and a table of key -bindings, actions and the first line of their docstrings. - -The order and formatting of these items is completely -configurable through the variable -`embark-verbose-indicator-buffer-sections'. - -If the keymap being shown contains prefix keys, the table of key -bindings can either show just the prefixes and update once the -prefix is pressed, or it can contain a flat list of all full key -sequences bound in the keymap. This is controlled by the -variable `embark-verbose-indicator-nested'. - -To reduce clutter in the key binding table, one can set the -variable `embark-verbose-indicator-excluded-actions' to a list -of symbols and regexps matching commands to exclude from the -table. - -To configure how a window is chosen to display this buffer, see -the variable `embark-verbose-indicator-display-action'." - (lambda (&optional keymap targets prefix) - (if (not keymap) - (when-let ((win (get-buffer-window embark--verbose-indicator-buffer - 'visible))) - (quit-window 'kill-buffer win)) - (embark--verbose-indicator-update - (if (and prefix embark-verbose-indicator-nested) - ;; Lookup prefix keymap globally if not found in action keymap - (let ((overriding-terminal-local-map keymap)) - (key-binding prefix 'accept-default)) - keymap) - targets) - (let ((display-buffer-alist - `(,@display-buffer-alist - (,(regexp-quote embark--verbose-indicator-buffer) - ,@embark-verbose-indicator-display-action)))) - (display-buffer embark--verbose-indicator-buffer))))) - -(defcustom embark-mixed-indicator-delay 0.5 - "Time in seconds after which the verbose indicator is shown. -The mixed indicator starts by showing the minimal indicator and -after this delay shows the verbose indicator." - :type '(choice (const :tag "No delay" 0) - (number :tag "Delay in seconds"))) - -(defcustom embark-mixed-indicator-both nil - "Show both indicators, even after the verbose indicator appeared." - :type 'boolean) - -(defun embark-mixed-indicator () - "Mixed indicator showing keymap and targets. -The indicator shows the `embark-minimal-indicator' by default. -After `embark-mixed-indicator-delay' seconds, the -`embark-verbose-indicator' is shown. This which-key-like approach -ensures that Embark stays out of the way for quick actions. The -helpful keybinding reminder still pops up automatically without -further user intervention." - (let ((vindicator (embark-verbose-indicator)) - (mindicator (embark-minimal-indicator)) - vindicator-active - vtimer) - (lambda (&optional keymap targets prefix) - ;; Always cancel the timer. - ;; 1. When updating, cancel timer, since the user has pressed - ;; a key before the timer elapsed. - ;; 2. For cleanup, the timer must also be canceled. - (when vtimer - (cancel-timer vtimer) - (setq vtimer nil)) - (if (not keymap) - (progn - (funcall vindicator) - (when mindicator - (funcall mindicator))) - (when mindicator - (funcall mindicator keymap targets prefix)) - (if vindicator-active - (funcall vindicator keymap targets prefix) - (setq vtimer - (run-at-time - embark-mixed-indicator-delay nil - (lambda () - (when (and (not embark-mixed-indicator-both) mindicator) - (funcall mindicator) - (setq mindicator nil)) - (setq vindicator-active t) - (funcall vindicator keymap targets prefix))))))))) - -;;;###autoload -(defun embark-bindings-in-keymap (keymap) - "Explore command key bindings in KEYMAP with `completing-read'. -The selected command will be executed. Interactively, prompt the -user for a KEYMAP variable." - (interactive - (list - (symbol-value - (intern-soft - (completing-read - "Keymap: " - (embark--with-category - 'variable - (cl-loop for x being the symbols - if (and (boundp x) (keymapp (symbol-value x))) - collect (symbol-name x))) - nil t nil 'variable-name-history - (let ((major-mode-map - (concat (symbol-name major-mode) "-map"))) - (when (intern-soft major-mode-map) major-mode-map))))))) - (when-let (command (embark-completing-read-prompter keymap nil 'no-default)) - (call-interactively command))) - -;;;###autoload -(defun embark-bindings (global) - "Explore current command key bindings with `completing-read'. -The selected command will be executed. - -This shows key bindings from minor mode maps and the local -map (usually set by the major mode), but also less common keymaps -such as those from a text property or overlay, or the overriding -maps: `overriding-terminal-local-map' and `overriding-local-map'. - -Additionally, if GLOBAL is non-nil (interactively, if called with -a prefix argument), this command includes global key bindings." - (interactive "P") - (embark-bindings-in-keymap - (make-composed-keymap - (let ((all-maps (current-active-maps t))) - (if global all-maps (remq global-map all-maps)))))) - -;;;###autoload -(defun embark-bindings-at-point () - "Explore all key bindings at point with `completing-read'. -The selected command will be executed. - -This command lists key bindings found in keymaps specified by the -text properties `keymap' or `local-map', from either buffer text -or an overlay. These are not widely used in Emacs, and when they -are used can be somewhat hard to discover. Examples of locations -that have such a keymap are links and images in `eww' buffers, -attachment links in `gnus' article buffers, and the stash line -in a `vc-dir' buffer." - (interactive) - (if-let ((keymaps (delq nil (list (get-char-property (point) 'keymap) - (get-char-property (point) 'local-map))))) - (embark-bindings-in-keymap (make-composed-keymap keymaps)) - (user-error "No key bindings found at point"))) - -;;;###autoload -(defun embark-prefix-help-command () - "Prompt for and run a command bound in the prefix used for this command. -The prefix described consists of all but the last event of the -key sequence that ran this command. This function is intended to -be used as a value for `prefix-help-command'. - -In addition to using completion to select a command, you can also -type @ and the key binding (without the prefix)." - (interactive) - (when-let ((keys (this-command-keys-vector)) - (prefix (seq-take keys (1- (length keys)))) - (keymap (key-binding prefix 'accept-default))) - (minibuffer-with-setup-hook - (lambda () - (let ((pt (- (minibuffer-prompt-end) 2))) - (overlay-put (make-overlay pt pt) 'before-string - (format " under %s" (key-description prefix))))) - (embark-bindings-in-keymap keymap)))) - -(defun embark--prompt (indicators keymap targets) - "Call the prompter with KEYMAP and INDICATORS. -The TARGETS are displayed for actions outside the minibuffer." - (mapc (lambda (i) (funcall i keymap targets)) indicators) - (condition-case nil - (minibuffer-with-setup-hook - (lambda () - ;; if the prompter opens its own minibuffer, show - ;; the indicator there too - (let ((inner-indicators (mapcar #'funcall embark-indicators))) - (mapc (lambda (i) (funcall i keymap targets)) inner-indicators) - (add-hook 'minibuffer-exit-hook - (lambda () (mapc #'funcall inner-indicators)) - nil t))) - (let ((enable-recursive-minibuffers t)) - (funcall embark-prompter keymap - (lambda (prefix) - (mapc (lambda (i) (funcall i keymap targets prefix)) - indicators))))) - (quit nil))) - -(defvar embark--run-after-command-functions nil - "Abnormal hook, used by `embark--run-after-command'.") - -(defun embark--run-after-command (fn &rest args) - "Call FN with ARGS after the current commands finishes. -If multiple functions are queued with this function during the -same command, they will be called in the order from the one -queued most recently to the one queued least recently." - ;; We don't simply add FN to `post-command-hook' because FN may recursively - ;; call this function. In that case, FN would modify `post-command-hook' - ;; from within post-command-hook, which doesn't behave properly in our case. - ;; We use our own abnormal hook and run it from PCH in a way that it is OK to - ;; modify it from within its own functions. - (unless embark--run-after-command-functions - (let (pch timer has-run) - (setq pch - (lambda () - (remove-hook 'post-command-hook pch) - (cancel-timer timer) - (unless has-run - (setq has-run t) - (while embark--run-after-command-functions - ;; The following funcall may recursively call - ;; `embark--run-after-command', modifying - ;; `embark--run-after-command-functions'. This is why this - ;; loop has to be implemented carefully. We have to pop the - ;; function off the hook before calling it. Using `dolist' - ;; on the hook would also be incorrect, because it wouldn't - ;; take modifications of this hook into account. - (with-demoted-errors "embark PCH: %S" - (condition-case nil - (funcall (pop embark--run-after-command-functions)) - (quit (message "Quit")))))))) - (add-hook 'post-command-hook pch 'append) - ;; Generally we prefer `post-command-hook' because it plays well with - ;; keyboard macros. In some cases, `post-command-hook' isn't run after - ;; exiting a recursive edit, so set up the following timer as a backup. - (setq timer (run-at-time 0 nil pch)))) - - ;; Keep the default-directory alive, since this is often overwritten, - ;; for example by Consult commands. - ;; TODO it might be necessary to add more dynamically bound variables - ;; here. What we actually want are functions `capture-dynamic-scope' - ;; and `eval-in-dynamic-scope', but this does not exist? - (let ((dir default-directory)) - (push (lambda () - (let ((default-directory dir)) - (apply fn args))) - embark--run-after-command-functions))) - -(defun embark--quit-and-run (fn &rest args) - "Quit the minibuffer and then call FN with ARGS. -If called outside the minibuffer, simply apply FN to ARGS." - (if (not (minibufferp)) - (apply fn args) - (apply #'embark--run-after-command fn args) - (embark--run-after-command #'set 'ring-bell-function ring-bell-function) - (setq ring-bell-function #'ignore) - (if (fboundp 'minibuffer-quit-recursive-edit) - (minibuffer-quit-recursive-edit) - (abort-recursive-edit)))) - -(defun embark--run-action-hooks (hooks action target quit) - "Run HOOKS for ACTION. -The HOOKS argument must be alist. The keys t and :always are -treated specially. The :always hooks are executed always and the -t hooks are the default hooks, for when there are no -command-specific hooks for ACTION. The QUIT, ACTION and TARGET -arguments are passed to the hooks as keyword arguments." - (mapc (lambda (h) (apply h :action action :quit quit target)) - (or (alist-get action hooks) - (alist-get t hooks))) - (mapc (lambda (h) (apply h :action action :quit quit target)) - (alist-get :always hooks))) - -(defun embark--run-around-action-hooks - (action target quit &optional non-interactive) - "Run the `embark-around-action-hooks' for ACTION. -All the applicable around hooks are composed in the order they -are present in `embark-around-action-hooks'. The keys t and -:always in `embark-around-action-hooks' are treated specially. -The :always hooks are executed always (outermost) and the t hooks -are the default hooks, for when there are no command-specific -hooks for ACTION. The QUIT, ACTION and TARGET arguments are -passed to the hooks as keyword arguments. - -The optional argument NON-INTERACTIVE controls whether the action -is run with `command-execute' or with `funcall' passing the -target as argument." - (apply - (seq-reduce - (lambda (fn hook) - (lambda (&rest args) (apply hook (plist-put args :run fn)))) - (let ((hooks embark-around-action-hooks)) - (reverse - (append (or (alist-get action hooks) (alist-get t hooks)) - (alist-get :always hooks)))) - (if non-interactive - (lambda (&rest args) - (funcall (plist-get args :action) - (or (plist-get args :candidates) (plist-get args :target)))) - (lambda (&rest args) - (command-execute (plist-get args :action))))) - :action action :quit quit target)) - -(defun embark--act (action target &optional quit) - "Perform ACTION injecting the TARGET. -If called from a minibuffer with non-nil QUIT, quit the -minibuffer before executing the action." - (if (memq action '(embark-become ; these actions should run in - embark-collect ; the current buffer, not the - embark-live ; target buffer - embark-export - embark-select - embark-act-all)) - (progn - (embark--run-action-hooks embark-pre-action-hooks action target quit) - (unwind-protect (embark--run-around-action-hooks action target quit) - (embark--run-action-hooks embark-post-action-hooks - action target quit))) - (let* ((command embark--command) - (prefix prefix-arg) - (action-window (embark--target-window t)) - (directory default-directory) - (inject - (lambda () - (let ((contents (minibuffer-contents))) - (delete-minibuffer-contents) - (insert - (propertize - (substring-no-properties (plist-get target :target)) - 'embark--initial-input contents))) - (if (memq 'ivy--queue-exhibit post-command-hook) - ;; Ivy has special needs: (1) for file names - ;; ivy-immediate-done is not equivalent to - ;; exit-minibuffer, (2) it needs a chance to run - ;; its post command hook first, so use depth 10 - (add-hook 'post-command-hook 'ivy-immediate-done 10 t) - (add-hook 'post-command-hook #'exit-minibuffer nil t)) - (embark--run-action-hooks embark-target-injection-hooks - action target quit))) - (dedicate (and (derived-mode-p 'embark-collect-mode) - (not (window-dedicated-p)) - (selected-window))) - (multi (memq action embark-multitarget-actions)) - (run-action - (if (and (commandp action) (not multi)) - (lambda () - (let (final-window) - (when dedicate (set-window-dedicated-p dedicate t)) - (unwind-protect - (with-selected-window action-window - (let ((enable-recursive-minibuffers t) - (embark--command command) - (prefix-arg prefix) - ;; the next two avoid mouse dialogs - (use-dialog-box nil) - (last-nonmenu-event 13) - (default-directory directory)) - (embark--run-action-hooks embark-pre-action-hooks - action target quit) - (minibuffer-with-setup-hook inject - ;; pacify commands that use (this-command-keys) - (when (= (length (this-command-keys)) 0) - (set--this-command-keys - (if (characterp last-command-event) - (string last-command-event) - "\r"))) - (setq this-command action) - (embark--run-around-action-hooks - action target quit))) - (setq final-window (selected-window))) - (embark--run-action-hooks embark-post-action-hooks - action target quit) - (when dedicate (set-window-dedicated-p dedicate nil))) - (unless (eq final-window action-window) - (select-window final-window)))) - (let ((target - (if (and multi (null (plist-get target :candidates))) - (plist-put - target :candidates (list (plist-get target :target))) - target))) - (lambda () - (with-selected-window action-window - (embark--run-action-hooks embark-pre-action-hooks - action target quit) - (unwind-protect - (let ((current-prefix-arg prefix) - (default-directory directory)) - (embark--run-around-action-hooks - action target quit :non-interactive)) - (embark--run-action-hooks embark-post-action-hooks - action target quit)))))))) - (setq prefix-arg nil) - (if quit (embark--quit-and-run run-action) (funcall run-action))))) - -(defun embark--refine-multi-category (_type target) - "Refine `multi-category' TARGET to its actual type." - (or (let ((mc (get-text-property 0 'multi-category target))) - (cond - ;; The `cdr' of the `multi-category' property can be a buffer object. - ((and (eq (car mc) 'buffer) (buffer-live-p (cdr mc))) - (cons 'buffer (buffer-name (cdr mc)))) - ((stringp (cdr mc)) mc))) - (cons 'general target))) - -(defun embark--simplify-path (_type target) - "Simplify and '//' or '~/' in the TARGET file path." - (cons 'file (substitute-in-file-name target))) - -(defun embark--keybinding-command (_type target) - "Treat an `embark-keybinding' TARGET as a command." - (when-let ((cmd (get-text-property 0 'embark-command target))) - (cons 'command (format "%s" cmd)))) - -(defun embark--lookup-lighter-minor-mode (_type target) - "If TARGET is a lighter, look up its minor mode. - -The `describe-minor-mode' command has as completion candidates -both minor-modes and their lighters. This function replaces the -lighters by their minor modes, so actions expecting a function -work on them." - (cons 'minor-mode - (let ((symbol (intern-soft target))) - (if (and symbol (boundp symbol)) - target - (symbol-name (lookup-minor-mode-from-indicator target)))))) - -(declare-function project-current "project") -(declare-function project-roots "project") -(declare-function project-root "project") - -(defun embark--project-file-full-path (_type target) - "Get full path of project file TARGET." - ;; TODO project-find-file can be called from outside all projects in - ;; which case it prompts for a project first; we don't support that - ;; case yet, since there is no current project. - (cons 'file - (if-let ((project (project-current)) - (root (if (fboundp 'project-root) - (project-root project) - (with-no-warnings - (car (project-roots project)))))) - (expand-file-name target root) - target))) - -(defun embark--remove-package-version (_type target) - "Remove version number from a versioned package TARGET." - (cons 'package (replace-regexp-in-string "-[0-9.]+$" "" target))) - -(defun embark--targets () - "Retrieve current targets. - -An initial guess at the current targets and their types is -determined by running the functions in `embark-target-finders'. -Each function should either return nil, a pair of a type symbol -and target string or a triple of a type symbol, target string and -target bounds. - -In the minibuffer only the first target finder returning non-nil -is taken into account. When finding targets at point in other -buffers, all target finder functions are executed. - -For each target, the type is then looked up as a key in the -variable `embark-transformer-alist'. If there is a transformer -for the type, it is called with the type and target, and must -return a `cons' of the transformed type and transformed target. - -The return value of `embark--targets' is a list of plists. Each -plist concerns one target, and has keys `:type', `:target', -`:orig-type', `:orig-target' and `:bounds'." - (let (targets) - (run-hook-wrapped - 'embark-target-finders - (lambda (fun) - (dolist (found (when-let (result (funcall fun)) - (if (consp (car result)) result (list result)))) - (let* ((type (or (car found) 'general)) - (target+bounds (cdr found)) - (target (if (consp target+bounds) - (car target+bounds) - target+bounds)) - (bounds (and (consp target+bounds) (cdr target+bounds))) - (full-target - (append - (list :orig-type type :orig-target target :bounds bounds) - (if-let (transform (alist-get type embark-transformer-alist)) - (let ((trans (funcall transform type target))) - (list :type (car trans) :target (cdr trans))) - (list :type type :target target))))) - (push full-target targets))) - (and targets (minibufferp)))) - (nreverse - (cl-delete-duplicates ; keeps last duplicate, but we reverse - targets - :test (lambda (t1 t2) - (and (equal (plist-get t1 :target) (plist-get t2 :target)) - (eq (plist-get t1 :type) (plist-get t2 :type)))))))) - -(defun embark--default-action (type) - "Return default action for the given TYPE of target. -The most common case is that the target comes from minibuffer -completion, in which case the default action is the command that -opened the minibuffer in the first place. This can be overridden -by `embark-default-action-overrides'. - -For targets that do not come from minibuffer completion -\(typically some thing at point in a regular buffer) and whose -type is not listed in `embark-default-action-overrides', the -default action is given by whatever binding RET has in the action -keymap for the given type." - (or (alist-get (cons type embark--command) embark-default-action-overrides - nil nil #'equal) - (alist-get type embark-default-action-overrides) - (alist-get t embark-default-action-overrides) - embark--command - (lookup-key (embark--raw-action-keymap type) "\r"))) - -(defun embark--rotate (list k) - "Rotate LIST by K elements and return the rotated list." - (setq k (mod k (length list))) - (append (seq-drop list k) (seq-take list k))) - -(defun embark--orig-target (target) - "Convert TARGET to original target." - (plist-put - (plist-put - (copy-sequence target) - :target (plist-get target :orig-target)) - :type (plist-get target :orig-type))) - -(defun embark--quit-p (action) - "Determine whether to quit the minibuffer after ACTION. -This function consults `embark-quit-after-action' to decide -whether or not the user wishes to quit the minibuffer after -performing the ACTION, assuming this is done from a minibuffer." - (let* ((cfg embark-quit-after-action) - (quit (if (consp cfg) (alist-get action cfg (alist-get t cfg)) cfg))) - (when embark--toggle-quit (setq quit (not quit))) - (setq embark--toggle-quit nil) - quit)) - -;;;###autoload -(defun embark-act (&optional arg) - "Prompt the user for an action and perform it. -The targets of the action are chosen by `embark-target-finders'. -By default, if called from a minibuffer the target is the top -completion candidate. When called from a non-minibuffer buffer -there can multiple targets and you can cycle among them by using -`embark-cycle' (which is bound by default to the same key -binding `embark-act' is, but see `embark-cycle-key'). - -This command uses `embark-prompter' to ask the user to specify an -action, and calls it injecting the target at the first minibuffer -prompt. - -If you call this from the minibuffer, it can optionally quit the -minibuffer. The variable `embark-quit-after-action' controls -whether calling `embark-act' with nil ARG quits the minibuffer, -and if ARG is non-nil it will do the opposite. Interactively, -ARG is the prefix argument. - -If instead you call this from outside the minibuffer, the first -ARG targets are skipped over (if ARG is negative the skipping is -done by cycling backwards) and cycling starts from the following -target." - (interactive "P") - (let* ((targets (or (embark--targets) (user-error "No target found"))) - (indicators (mapcar #'funcall embark-indicators)) - (default-done nil)) - (when arg - (if (minibufferp) - (embark-toggle-quit) - (setq targets (embark--rotate targets (prefix-numeric-value arg))))) - (unwind-protect - (while - (let* ((target (car targets)) - (action - (or (embark--prompt - indicators - (let ((embark-default-action-overrides - (if default-done - `((t . ,default-done)) - embark-default-action-overrides))) - (embark--action-keymap (plist-get target :type) - (cdr targets))) - targets) - (user-error "Canceled"))) - (default-action (or default-done - (embark--default-action - (plist-get target :type))))) - (cond - ;; When acting twice in the minibuffer, do not restart - ;; `embark-act'. Otherwise the next `embark-act' will - ;; find a target in the original buffer. - ((eq action #'embark-act) - (message "Press an action key")) - ((eq action #'embark-cycle) - (setq targets (embark--rotate - targets (prefix-numeric-value prefix-arg)))) - (t - ;; if the action is non-repeatable, cleanup indicator now - (let ((repeat (embark--action-repeatable-p action))) - (unless repeat (mapc #'funcall indicators)) - (condition-case err - (embark--act - action - (if (and (eq action default-action) - (eq action embark--command) - (not (memq action embark-multitarget-actions))) - (embark--orig-target target) - target) - (embark--quit-p action)) - (user-error - (funcall (if repeat #'message #'user-error) - "%s" (cadr err)))) - (when-let (new-targets (and repeat (embark--targets))) - ;; Terminate repeated prompter on default action, - ;; when repeating. Jump to the region type if the - ;; region is active after the action, or else to the - ;; current type again. - (setq default-done #'embark-done - targets - (embark--rotate - new-targets - (or (cl-position-if - (let ((desired-type - (if (eq repeat t) - (plist-get (car targets) :type) - repeat))) - (lambda (x) - (eq (plist-get x :type) desired-type))) - new-targets) - 0))))))))) - (mapc #'funcall indicators)))) - -(defun embark--maybe-transform-candidates () - "Collect candidates and see if they all transform to the same type. -Return a plist with keys `:type', `:orig-type', `:candidates', and -`:orig-candidates'." - (pcase-let* ((`(,type . ,candidates) - (run-hook-with-args-until-success 'embark-candidate-collectors)) - (bounds (mapcar #'cdr-safe candidates))) - (setq candidates - (mapcar (lambda (x) (if (consp x) (car x) x)) candidates)) - (when (eq type 'file) - (let ((dir (embark--default-directory))) - (setq candidates - (mapcar (lambda (cand) - (abbreviate-file-name - (expand-file-name (substitute-in-file-name cand) dir))) - candidates)))) - ;; TODO more systematic approach to applying substitute-in-file-name - (append - (list :orig-type type :orig-candidates candidates :bounds bounds) - (or (when candidates - (when-let ((transformer (alist-get type embark-transformer-alist))) - (pcase-let* ((`(,new-type . ,first-cand) - (funcall transformer type (car candidates)))) - (let ((new-candidates (list first-cand))) - (when (cl-every - (lambda (cand) - (pcase-let ((`(,t-type . ,t-cand) - (funcall transformer type cand))) - (when (eq t-type new-type) - (push t-cand new-candidates) - t))) - (cdr candidates)) - (list :type new-type - :candidates (nreverse new-candidates))))))) - (list :type type :candidates candidates))))) - -;;;###autoload -(defun embark-act-all (&optional arg) - "Prompt the user for an action and perform it on each candidate. -The candidates are chosen by `embark-candidate-collectors'. By -default, if `embark-select' has been used to select some -candidates, then `embark-act-all' will act on those candidates; -otherwise, if the selection is empty and `embark-act-all' is -called from a minibuffer, then the candidates are the completion -candidates. - -This command uses `embark-prompter' to ask the user to specify an -action, and calls it injecting the target at the first minibuffer -prompt. - -If you call this from the minibuffer, it can optionally quit the -minibuffer. The variable `embark-quit-after-action' controls -whether calling `embark-act' with nil ARG quits the minibuffer, -and if ARG is non-nil it will do the opposite. Interactively, -ARG is the prefix argument." - (interactive "P") - (let* ((transformed (embark--maybe-transform-candidates)) - (type (plist-get transformed :type)) - (orig-type (plist-get transformed :orig-type)) - (candidates - (or (cl-mapcar - (lambda (cand orig-cand bounds) - (list :type type :target cand - :bounds (when bounds - (cons (copy-marker (car bounds)) - (copy-marker (cdr bounds)))) - :orig-type orig-type :orig-target orig-cand)) - (plist-get transformed :candidates) - (plist-get transformed :orig-candidates) - (plist-get transformed :bounds)) - (user-error "No candidates to act on"))) - (indicators (mapcar #'funcall embark-indicators))) - (when arg (embark-toggle-quit)) - (unwind-protect - (let* ((action - (or (embark--prompt - indicators (embark--action-keymap type nil) - (list (list :type type :multi (length candidates)))) - (user-error "Canceled"))) - (prefix prefix-arg) - (act (lambda (candidate) - (cl-letf (((symbol-function 'embark--restart) #'ignore) - ((symbol-function 'embark--confirm) #'ignore)) - (let ((prefix-arg prefix)) - (when-let ((bounds (plist-get candidate :bounds))) - (goto-char (car bounds))) - (embark--act action candidate))))) - (quit (embark--quit-p action))) - (when (and (eq action (embark--default-action type)) - (eq action embark--command)) - (setq candidates (mapcar #'embark--orig-target candidates))) - (when (or (not (or embark-confirm-act-all - (memq 'embark--confirm - (alist-get action embark-pre-action-hooks)))) - (y-or-n-p (format "Run %s on %d %ss? " - action (length candidates) type))) - (if (memq action embark-multitarget-actions) - (let ((prefix-arg prefix)) - (embark--act action transformed quit)) - (save-excursion - (if quit - (embark--quit-and-run #'mapc act candidates) - (mapc act candidates)))) - (when (and (not quit) - (memq 'embark--restart - (alist-get action embark-post-action-hooks))) - (embark--restart)))) - (dolist (cand candidates) - (when-let ((bounds (plist-get cand :bounds))) - (set-marker (car bounds) nil) ; yay, manual memory management! - (set-marker (cdr bounds) nil))) - (setq prefix-arg nil) - (mapc #'funcall indicators)))) - -(defun embark-highlight-indicator () - "Action indicator highlighting the target at point." - (let (overlay) - (lambda (&optional keymap targets _prefix) - (let ((bounds (plist-get (car targets) :bounds))) - (when (and overlay (or (not keymap) (not bounds))) - (delete-overlay overlay) - (setq overlay nil)) - (when bounds - (if overlay - (move-overlay overlay (car bounds) (cdr bounds)) - (setq overlay (make-overlay (car bounds) (cdr bounds))) - (overlay-put overlay 'category 'embark-target-overlay)) - (overlay-put overlay 'window (selected-window))))))) - -(defun embark-isearch-highlight-indicator () - "Action indicator highlighting all occurrences of the identifier at point. -This indicator only does something for targets which are -identifiers or symbols. For those it uses `isearch''s lazy -highlighting feature to highlight all occurrences of the target in -the buffer. This indicator is best used in conjunction with -`embark-highlight-indicator': by using them both you get the -target and the other occurrences of it highlighted in different -colors." - (lambda (&optional _keymap targets _prefix) - (if (and (not (minibufferp)) - (memq (plist-get (car targets) :orig-type) '(symbol identifier))) - (let ((isearch-string (plist-get (car targets) :target)) - (isearch-regexp-function #'isearch-symbol-regexp)) - (isearch-lazy-highlight-new-loop)) - (setq isearch-lazy-highlight-last-string nil) - (lazy-highlight-cleanup t)))) - -(defun embark-cycle (_arg) - "Cycle over the next ARG targets at point. -If ARG is negative, cycle backwards." - (interactive "p") - (user-error "Not meant to be called directly")) - -(defun embark-done () - "Terminate sequence of repeated actions." - (interactive)) - -;;;###autoload -(defun embark-dwim (&optional arg) - "Run the default action on the current target. -The target of the action is chosen by `embark-target-finders'. - -If the target comes from minibuffer completion, then the default -action is the command that opened the minibuffer in the first -place, unless overridden by `embark-default-action-overrides'. - -For targets that do not come from minibuffer completion -\(typically some thing at point in a regular buffer) and whose -type is not listed in `embark-default-action-overrides', the -default action is given by whatever binding RET has in the action -keymap for the target's type. - -See `embark-act' for the meaning of the prefix ARG." - (interactive "P") - (if-let ((targets (embark--targets))) - (let* ((target - (or (nth - (if (or (null arg) (minibufferp)) - 0 - (mod (prefix-numeric-value arg) (length targets))) - targets))) - (type (plist-get target :type)) - (default-action (embark--default-action type)) - (action (or (command-remapping default-action) default-action))) - (unless action - (user-error "No default action for %s targets" type)) - (when (and arg (minibufferp)) (setq embark--toggle-quit t)) - (embark--act action - (if (and (eq default-action embark--command) - (not (memq default-action - embark-multitarget-actions))) - (embark--orig-target target) - target) - (embark--quit-p action))) - (user-error "No target found"))) - -(defun embark--become-keymap () - "Return keymap of commands to become for current command." - (let ((map (make-composed-keymap - (cl-loop for keymap-name in embark-become-keymaps - for keymap = (symbol-value keymap-name) - when (where-is-internal embark--command (list keymap)) - collect keymap)))) - (when embark-help-key - (keymap-set map embark-help-key #'embark-keymap-help)) - map)) - -;;;###autoload -(defun embark-become (&optional full) - "Make current command become a different command. -Take the current minibuffer input as initial input for new -command. The new command can be run normally using key bindings or -\\[execute-extended-command], but if the current command is found in a keymap in -`embark-become-keymaps', that keymap is activated to provide -convenient access to the other commands in it. - -If FULL is non-nil (interactively, if called with a prefix -argument), the entire minibuffer contents are used as the initial -input of the new command. By default only the part of the -minibuffer contents between the current completion boundaries is -taken. What this means is fairly technical, but (1) usually -there is no difference: the completion boundaries include the -entire minibuffer contents, and (2) the most common case where -these notions differ is file completion, in which case the -completion boundaries single out the path component containing -point." - (interactive "P") - (unless (minibufferp) - (user-error "Not in a minibuffer")) - (let* ((target (embark--display-string ; remove invisible portions - (if full - (minibuffer-contents) - (pcase-let ((`(,beg . ,end) (embark--boundaries))) - (substring (minibuffer-contents) beg - (+ end (embark--minibuffer-point))))))) - (keymap (embark--become-keymap)) - (targets `((:type embark-become :target ,target))) - (indicators (mapcar #'funcall embark-indicators)) - (become (unwind-protect - (embark--prompt indicators keymap targets) - (mapc #'funcall indicators)))) - (unless become - (user-error "Canceled")) - (embark--become-command become target))) - -(defun embark--become-command (command input) - "Quit current minibuffer and start COMMAND with INPUT." - (embark--quit-and-run - (lambda () - (minibuffer-with-setup-hook - (lambda () - (delete-minibuffer-contents) - (insert input)) - (let ((use-dialog-box nil) ;; avoid mouse dialogs - (last-nonmenu-event 13)) - (setq this-command command) - (command-execute command)))))) - -;;; Embark collect - -(defgroup embark-collect nil - "Buffers for acting on collected Embark targets." - :group 'embark) - -(defcustom embark-candidate-collectors - '(embark-selected-candidates - embark-minibuffer-candidates - embark-completion-list-candidates - embark-dired-candidates - embark-ibuffer-candidates - embark-embark-collect-candidates - embark-custom-candidates) - "List of functions that collect all candidates in a given context. -These are used to fill an Embark Collect buffer. Each function -should return either nil (to indicate it found no candidates) or -a list whose first element is a symbol indicating the type of -candidates and whose `cdr' is the list of candidates, each of -which should be either a string or a dotted list of the -form (TARGET START . END), where START and END are the buffer -positions bounding the TARGET string." - :type 'hook) - -(defcustom embark-exporters-alist - '((buffer . embark-export-ibuffer) - (file . embark-export-dired) - (package . embark-export-list-packages) - (bookmark . embark-export-bookmarks) - (variable . embark-export-customize-variable) - (face . embark-export-customize-face) - (symbol . embark-export-apropos) - (minor-mode . embark-export-apropos) - (function . embark-export-apropos) - (command . embark-export-apropos) - (t . embark-collect)) - "Alist associating completion types to export functions. -Each function should take a list of strings which are candidates -for actions and make a buffer appropriate to manage them. For -example, the default is to make a Dired buffer for files, and an -ibuffer for buffers. - -The key t is also allowed in the alist, and the corresponding -value indicates the default function to use for other types. The -default is `embark-collect'" - :type '(alist :key-type symbol :value-type function)) - -(defcustom embark-after-export-hook nil - "Hook run after `embark-export' in the newly created buffer." - :type 'hook) - -(defface embark-collect-candidate '((t :inherit default)) - "Face for candidates in Embark Collect buffers.") - -(defface embark-collect-group-title - '((t :inherit shadow :slant italic)) - "Face for group titles in Embark Collect buffers.") - -(defface embark-collect-group-separator - '((t :inherit shadow :strike-through t italic)) - "Face for group titles in Embark Collect buffers.") - -(defcustom embark-collect-group-format - (concat - (propertize " " 'face 'embark-collect-group-separator) - (propertize " %s " 'face 'embark-collect-group-title) - (propertize " " 'face 'completions-group-separator - 'display '(space :align-to right))) - "Format string used for the group title in Embark Collect buffers." - :type 'string) - -(defface embark-collect-annotation '((t :inherit completions-annotations)) - "Face for annotations in Embark Collect. -This is only used for annotation that are not already fontified.") - -(defvar-local embark--rerun-function nil - "Function to rerun the collect or export that made the current buffer.") - -(autoload 'package-delete "package") -(declare-function package--from-builtin "package") -(declare-function package-desc-extras "package") -(declare-function package-desc-name "package") -(defvar package--builtins) -(defvar package-alist) -(defvar package-archive-contents) -(defvar package--initialized) - -(defun embark--package-desc (pkg) - "Return the description structure for package PKG." - (or ; found this in `describe-package-1' - (car (alist-get pkg package-alist)) - (if-let ((built-in (assq pkg package--builtins))) - (package--from-builtin built-in) - (car (alist-get pkg package-archive-contents))))) - -(defun embark-minibuffer-candidates () - "Return all current completion candidates from the minibuffer." - (when (minibufferp) - (let* ((all (completion-all-completions - (minibuffer-contents) - minibuffer-completion-table - minibuffer-completion-predicate - (embark--minibuffer-point))) - (last (last all))) - (when last (setcdr last nil)) - (cons - (completion-metadata-get (embark--metadata) 'category) - all)))) - -(defun embark-sorted-minibuffer-candidates () - "Return a sorted list of current minibuffer completion candidates. -This using the same sort order that `icomplete' and -`minibuffer-force-complete' use. The intended usage is that you -replace `embark-minibuffer-candidates' with this function in the -list `embark-candidate-collectors'." - (when (minibufferp) - (cons - (completion-metadata-get (embark--metadata) 'category) - (nconc (cl-copy-list (completion-all-sorted-completions)) nil)))) - -(declare-function dired-get-marked-files "dired") -(declare-function dired-move-to-filename "dired") -(declare-function dired-move-to-end-of-filename "dired") - -(defun embark-dired-candidates () - "Return marked or all files shown in Dired buffer. -If any buffer is marked, return marked buffers; otherwise, return -all buffers." - (when (derived-mode-p 'dired-mode) - (cons 'file - (or - ;; dired-get-marked-files returns the file on the current - ;; line if no marked files are found; and when the fourth - ;; argument is non-nil, the "no marked files" case is - ;; distinguished from the "single marked file" case by - ;; returning (list t marked-file) in the latter - (let ((marked (dired-get-marked-files t nil nil t))) - (and (cdr marked) - (if (eq (car marked) t) (cdr marked) marked))) - (save-excursion - (goto-char (point-min)) - (let (files) - (while (not (eobp)) - (when-let (file (dired-get-filename t t)) - (push `(,file - ,(progn (dired-move-to-filename) (point)) - . ,(progn (dired-move-to-end-of-filename t) (point))) - files)) - (forward-line)) - (nreverse files))))))) - -(autoload 'ibuffer-marked-buffer-names "ibuffer") -(declare-function ibuffer-map-lines-nomodify "ibuffer") - -(defun embark-ibuffer-candidates () - "Return marked or all buffers listed in ibuffer buffer. -If any buffer is marked, return marked buffers; otherwise, return -all buffers." - (when (derived-mode-p 'ibuffer-mode) - (cons 'buffer - (or (ibuffer-marked-buffer-names) - (let (buffers) - (ibuffer-map-lines-nomodify - (lambda (buffer _mark) - (push (buffer-name buffer) buffers))) - (nreverse buffers)))))) - -(defun embark-embark-collect-candidates () - "Return candidates in Embark Collect buffer. -This makes `embark-export' work in Embark Collect buffers." - (when (derived-mode-p 'embark-collect-mode) - (cons embark--type - (save-excursion - (goto-char (point-min)) - (let (all) - (when-let ((cand (embark-target-collect-candidate))) - (push (cdr cand) all)) - (while (forward-button 1 nil nil t) - (when-let ((cand (embark-target-collect-candidate))) - (push (cdr cand) all))) - (nreverse all)))))) - -(defun embark-completion-list-candidates () - "Return all candidates in a completions buffer." - (when (derived-mode-p 'completion-list-mode) - (cons - embark--type - (save-excursion - (goto-char (point-min)) - (next-completion 1) - (let (all) - (while (not (eobp)) - (push (cdr (embark-target-completion-list-candidate)) all) - (next-completion 1)) - (nreverse all)))))) - -(defun embark-custom-candidates () - "Return all variables and faces listed in this `Custom-mode' buffer." - (when (derived-mode-p 'Custom-mode) - (cons 'symbol ; gets refined to variable or face when acted upon - (save-excursion - (goto-char (point-min)) - (let (symbols) - (while (not (eobp)) - (when-let (widget (widget-at (point))) - (when (eq (car widget) 'custom-visibility) - (push - `(,(symbol-name - (plist-get (cdr (plist-get (cdr widget) :parent)) - :value)) - ,(point) - . ,(progn - (re-search-forward ":" (line-end-position) 'noerror) - (point))) - symbols))) - (forward-line)) - (nreverse symbols)))))) - - -(defun embark-collect--target () - "Return the Embark Collect candidate at point. -This takes into account `embark-transformer-alist'." - (let ((embark-target-finders '(embark-target-collect-candidate))) - (car (embark--targets)))) - -(defun embark--action-command (action) - "Turn an ACTION into a command to perform the action. -Returns the name of the command." - (let ((name (intern (format "embark-action--%s" - (embark--command-name action))))) - (fset name (lambda (arg) - (interactive "P") - (when-let (target (embark-collect--target)) - (let ((prefix-arg arg)) - (embark--act action target))))) - (when (fboundp action) - (put name 'function-documentation (documentation action))) - name)) - -(defun embark--all-bindings (keymap &optional nested) - "Return an alist of all bindings in KEYMAP. -If NESTED is non-nil subkeymaps are not flattened." - (let (bindings maps) - (map-keymap - (lambda (key def) - (cond - ((keymapp def) - (if nested - (push (cons (vector key) def) maps) - (dolist (bind (embark--all-bindings def)) - (push (cons (vconcat (vector key) (car bind)) (cdr bind)) - maps)))) - (def (push (cons (vector key) def) bindings)))) - (keymap-canonicalize keymap)) - (nconc (nreverse bindings) (nreverse maps)))) - -(defun embark-collect--direct-action-map (type) - "Return a direct action keymap for targets of given TYPE." - (let* ((actions (embark--action-keymap type nil)) - (map (make-sparse-keymap))) - (set-keymap-parent map button-map) - (pcase-dolist (`(,key . ,cmd) (embark--all-bindings actions)) - (unless (or (equal key [13]) - (memq cmd '(digit-argument negative-argument))) - (define-key map key (if (eq cmd 'embark-keymap-help) - #'embark-bindings-at-point - (embark--action-command cmd))))) - map)) - -(define-minor-mode embark-collect-direct-action-minor-mode - "Bind type-specific actions directly (without need for `embark-act')." - :init-value nil - :lighter " Act" - (unless (derived-mode-p 'embark-collect-mode) - (user-error "Not in an Embark Collect buffer")) - (save-excursion - (goto-char (point-min)) - (let ((inhibit-read-only t) maps) - (while (progn - (when (tabulated-list-get-id) - (put-text-property - (point) (button-end (point)) 'keymap - (if embark-collect-direct-action-minor-mode - (when-let ((target (embark-collect--target)) - (type (plist-get target :type))) - (or (alist-get type maps) - (setf (alist-get type maps) - (embark-collect--direct-action-map type))))))) - (forward-button 1 nil nil t)))))) - -(define-button-type 'embark-collect-entry - 'face 'embark-collect-candidate - 'action 'embark-collect-choose) - -(declare-function outline-toggle-children "outline") -(define-button-type 'embark-collect-group - 'face 'embark-collect-group-title - 'action (lambda (_) (outline-toggle-children))) - -(defun embark--boundaries () - "Get current minibuffer completion boundaries." - (let ((contents (minibuffer-contents)) - (pt (embark--minibuffer-point))) - (completion-boundaries - (substring contents 0 pt) - minibuffer-completion-table - minibuffer-completion-predicate - (substring contents pt)))) - -(defun embark-collect-choose (entry) - "Run default action on Embark Collect ENTRY." - (pcase-let ((`(,type ,text ,start . ,end) - (save-excursion - (goto-char entry) - (embark-target-collect-candidate)))) - (embark--act (embark--default-action type) - (list :target text - :type type - :bounds (cons start end))))) - -(defvar-keymap embark-collect-mode-map - :doc "Keymap for Embark collect mode." - :parent tabulated-list-mode-map - "a" #'embark-act - "A" #'embark-act-all - "M-a" #'embark-collect-direct-action-minor-mode - "E" #'embark-export - "s" #'isearch-forward - "n" #'forward-button - "p" #'backward-button - "}" 'outline-next-heading - "{" 'outline-previous-heading - " " 'outline-next-heading - " " 'outline-previous-heading - " " #'embark-rerun-collect-or-export) - -(defconst embark-collect--outline-string (string #x210000) - "Special string used for outline headings in Embark Collect buffers. -Chosen to be extremely unlikely to appear in a candidate.") - -(define-derived-mode embark-collect-mode tabulated-list-mode "Embark Collect" - "List of candidates to be acted on. -The command `embark-act' is bound `embark-collect-mode-map', but -you might prefer to change the key binding to match your other -key binding for it. Or alternatively you might want to enable the -embark collect direct action minor mode by adding the function -`embark-collect-direct-action-minor-mode' to -`embark-collect-mode-hook'. - -Reverting an Embark Collect buffer has slightly unusual behavior -if the buffer was obtained by running `embark-collect' from -within a minibuffer completion session. In that case reverting -just restarts the completion session, that is, the command that -opened the minibuffer is run again and the minibuffer contents -restored. You can then interact normally with the command, -perhaps editing the minibuffer contents, and, if you wish, you -can rerun `embark-collect' to get an updated buffer." - :interactive nil :abbrev-table nil :syntax-table nil) - -(defun embark-collect--metadatum (type metadatum) - "Get METADATUM for current buffer's candidates. -For non-minibuffers, assume candidates are of given TYPE." - (if (minibufferp) - (or (completion-metadata-get (embark--metadata) metadatum) - (plist-get completion-extra-properties - (intern (format ":%s" metadatum)))) - ;; otherwise fake some metadata for Marginalia users's benefit - (completion-metadata-get `((category . ,type)) metadatum))) - -(defun embark-collect--affixator (type) - "Get affixation function for current buffer's candidates. -For non-minibuffers, assume candidates are of given TYPE." - (or (embark-collect--metadatum type 'affixation-function) - (let ((annotator - (or (embark-collect--metadatum type 'annotation-function) - (lambda (_) "")))) - (lambda (candidates) - (mapcar (lambda (c) - (if-let (a (funcall annotator c)) (list c "" a) c)) - candidates))))) - -(defun embark--display-string (str) - ;; Note: Keep in sync with vertico--display-string - "Return display STR without display and invisible properties." - (let ((end (length str)) (pos 0) chunks) - (while (< pos end) - (let ((nextd (next-single-property-change pos 'display str end)) - (disp (get-text-property pos 'display str))) - (if (stringp disp) - (let ((face (get-text-property pos 'face str))) - (when face - (add-face-text-property - 0 (length disp) face t (setq disp (concat disp)))) - (setq pos nextd chunks (cons disp chunks))) - (while (< pos nextd) - (let ((nexti - (next-single-property-change pos 'invisible str nextd))) - (unless (or (get-text-property pos 'invisible str) - (and (= pos 0) (= nexti end))) ;; full=>no allocation - (push (substring str pos nexti) chunks)) - (setq pos nexti)))))) - (if chunks (apply #'concat (nreverse chunks)) str))) - -(defconst embark--hline - (propertize - (concat "\n" (propertize - " " 'display '(space :align-to right) - 'face '(:inherit completions-group-separator :height 0.01) - 'cursor-intangible t 'intangible t))) - "Horizontal line used to separate multiline collect entries.") - -(defun embark-collect--format-entries (candidates grouper) - "Format CANDIDATES for `tabulated-list-mode' grouped by GROUPER. -The GROUPER is either nil or a function like the `group-function' -completion metadatum, that is, a function of two arguments, the -first of which is a candidate and the second controls what is -computed: if nil, the title of the group the candidate belongs -to, and if non-nil, a rewriting of the candidate (useful to -simplify the candidate so it doesn't repeat the group title, for -example)." - (let ((max-width 0) - (transform - (if grouper (lambda (cand) (funcall grouper cand t)) #'identity))) - (setq - tabulated-list-entries - (mapcan - (lambda (group) - (let ((multiline (seq-some (lambda (x) (string-match-p "\n" (car x))) - candidates))) - (cons - `(nil [(,(concat (propertize embark-collect--outline-string - 'invisible t) - (format embark-collect-group-format (car group))) - type embark-collect-group) - ("" skip t)]) - (mapcar - (pcase-lambda (`(,cand ,prefix ,annotation)) - (let* ((display (embark--display-string (funcall transform cand))) - (length (length annotation)) - (faces (text-property-not-all - 0 length 'face nil annotation))) - (setq max-width (max max-width (+ (string-width prefix) - (string-width display)))) - (when faces - (add-face-text-property 0 length 'default t annotation)) - `(,cand - [(,(propertize - (if multiline (concat display embark--hline) display) - 'line-prefix prefix) - type embark-collect-entry) - (,annotation - skip t - ,@(unless faces - '(face embark-collect-annotation)))]))) - (cdr group))))) - (if grouper - (seq-group-by (lambda (item) (funcall grouper (car item) nil)) - candidates) - (list (cons "" candidates))))) - (if (null grouper) - (pop tabulated-list-entries) - (setq-local outline-regexp embark-collect--outline-string) - (outline-minor-mode)) - (setq tabulated-list-format - `[("Candidate" ,max-width t) ("Annotation" 0 t)]))) - -(defun embark-collect--update-candidates (buffer) - "Update candidates for Embark Collect BUFFER." - (let* ((transformed (embark--maybe-transform-candidates)) - (type (plist-get transformed :orig-type)) ; we need the originals for - (candidates (plist-get transformed :orig-candidates)) ; default action - (bounds (plist-get transformed :bounds)) - (affixator (embark-collect--affixator type)) - (grouper (embark-collect--metadatum type 'group-function))) - (when (eq type 'file) - (let ((dir (buffer-local-value 'default-directory buffer))) - (setq candidates - (mapcar (lambda (cand) - (let ((rel (file-relative-name cand dir))) - (if (string-prefix-p "../" rel) cand rel))) - candidates)))) - (if (seq-some #'identity bounds) - (cl-loop for cand in candidates and (start . _end) in bounds - when start - do (add-text-properties - 0 1 `(embark--location ,(copy-marker start)) cand))) - (setq candidates (funcall affixator candidates)) - (with-current-buffer buffer - (setq embark--type type) - (unless embark--command - (setq embark--command #'embark--goto)) - (embark-collect--format-entries candidates grouper)) - candidates)) - -(defun embark--goto (target) - "Jump to the original location of TARGET. -This function is used as a default action in Embark Collect -buffers when the candidates were a selection from a regular -buffer." - ;; TODO: ensure the location jumped to is visible - ;; TODO: remove duplication with embark-org-goto-heading - (when-let ((marker (get-text-property 0 'embark--location target))) - (pop-to-buffer (marker-buffer marker)) - (widen) - (goto-char marker) - (pulse-momentary-highlight-one-line))) - -(defun embark--collect (buffer-name) - "Create an Embark Collect buffer named BUFFER-NAME. - -The function `generate-new-buffer-name' is used to ensure the -buffer has a unique name." - (let ((buffer (generate-new-buffer buffer-name)) - (rerun (embark--rerun-function #'embark-collect))) - (with-current-buffer buffer - ;; we'll run the mode hooks once the buffer is displayed, so - ;; the hooks can make use of the window - (delay-mode-hooks (embark-collect-mode))) - - (embark--cache-info buffer) - (unless (embark-collect--update-candidates buffer) - (user-error "No candidates to collect")) - - (with-current-buffer buffer - (setq tabulated-list-use-header-line nil ; default to no header - header-line-format nil - tabulated-list--header-string nil) - (setq embark--rerun-function rerun)) - - (let ((window (display-buffer buffer))) - (with-selected-window window - (run-mode-hooks) - (tabulated-list-revert)) - (set-window-dedicated-p window t) - buffer))) - -(defun embark--descriptive-buffer-name (type) - "Return a descriptive name for an Embark collect or export buffer. -TYPE should be either `collect' or `export'." - (format "*Embark %s: %s*" - (capitalize (symbol-name type)) - (if (minibufferp) - (format "%s - %s" embark--command - (minibuffer-contents-no-properties)) - (buffer-name)))) - -;;;###autoload -(defun embark-collect () - "Create an Embark Collect buffer. - -To control the display, add an entry to `display-buffer-alist' -with key \"Embark Collect\". - -In Embark Collect buffers `revert-buffer' is remapped to -`embark-rerun-collect-or-export', which has slightly unusual -behavior if the buffer was obtained by running `embark-collect' -from within a minibuffer completion session. In that case -rerunning just restarts the completion session, that is, the -command that opened the minibuffer is run again and the -minibuffer contents restored. You can then interact normally with -the command, perhaps editing the minibuffer contents, and, if you -wish, you can rerun `embark-collect' to get an updated buffer." - (interactive) - (let ((buffer (embark--collect (embark--descriptive-buffer-name 'collect)))) - (when (minibufferp) - (embark--run-after-command #'pop-to-buffer buffer) - (embark--quit-and-run #'message nil)))) - -;;;###autoload -(defun embark-live () - "Create a live-updating Embark Collect buffer. - -To control the display, add an entry to `display-buffer-alist' -with key \"Embark Live\"." - (interactive) - (let ((live-buffer (embark--collect - (format "*Embark Live: %s*" - (if (minibufferp) - (format "M-x %s" embark--command) - (buffer-name))))) - (run-collect (make-symbol "run-collect")) - (stop-collect (make-symbol "stop-collect")) - timer) - (setf (symbol-function stop-collect) - (lambda () - (remove-hook 'change-major-mode-hook stop-collect t) - (remove-hook 'after-change-functions run-collect t))) - (setf (symbol-function run-collect) - (lambda (_1 _2 _3) - (unless timer - (setq timer - (run-with-idle-timer - 0.05 nil - (lambda () - (if (not (buffer-live-p live-buffer)) - (funcall stop-collect) - (embark-collect--update-candidates live-buffer) - (with-current-buffer live-buffer - ;; TODO figure out why I can't restore point - (tabulated-list-print t t)) - (setq timer nil)))))))) - (add-hook 'after-change-functions run-collect nil t) - (when (minibufferp) - (add-hook 'change-major-mode-hook stop-collect nil t)))) - -(defun embark--rerun-function (kind) - "Return a rerun function for an export or collect buffer in this context. -The parameter KIND should be either `embark-export' or `embark-collect'." - (let ((buffer (or embark--target-buffer (embark--target-buffer))) - (command embark--command)) - (cl-flet ((rerunner (action) - (lambda (&rest _) - (quit-window 'kill-buffer) - (with-current-buffer - (if (buffer-live-p buffer) buffer (current-buffer)) - (let ((embark--command command)) - (funcall action)))))) - (if (minibufferp) - (rerunner - (let ((input (minibuffer-contents-no-properties))) - (lambda () - (minibuffer-with-setup-hook - (lambda () - (delete-minibuffer-contents) - (insert input)) - (setq this-command embark--command) - (command-execute embark--command))))) - (rerunner kind))))) - -(defun embark-rerun-collect-or-export () - "Rerun the `embark-collect' or `embark-export' that created this buffer." - (interactive) - (if embark--rerun-function - (funcall embark--rerun-function) - (user-error "No function to rerun collect or export found"))) - -;;;###autoload -(defun embark-export () - "Create a type-specific buffer to manage current candidates. -The variable `embark-exporters-alist' controls how to make the -buffer for each type of completion. - -In Embark Export buffers `revert-buffer' is remapped to -`embark-rerun-collect-or-export', which has slightly unusual -behavior if the buffer was obtained by running `embark-export' -from within a minibuffer completion session. In that case -reverting just restarts the completion session, that is, the -command that opened the minibuffer is run again and the -minibuffer contents restored. You can then interact normally -with the command, perhaps editing the minibuffer contents, and, -if you wish, you can rerun `embark-export' to get an updated -buffer." - (interactive) - (let* ((transformed (embark--maybe-transform-candidates)) - (candidates (or (plist-get transformed :candidates) - (user-error "No candidates for export"))) - (type (plist-get transformed :type))) - (let ((exporter (or (alist-get type embark-exporters-alist) - (alist-get t embark-exporters-alist)))) - (if (eq exporter 'embark-collect) - (embark-collect) - (let* ((after embark-after-export-hook) - (cmd embark--command) - (name (embark--descriptive-buffer-name 'export)) - (rerun (embark--rerun-function #'embark-export)) - (buffer (save-excursion - (funcall exporter candidates) - (rename-buffer name t) - (current-buffer)))) - (embark--quit-and-run - (lambda () - (pop-to-buffer buffer) - (setq embark--rerun-function rerun) - (use-local-map - (make-composed-keymap - '(keymap - (remap keymap - (revert-buffer . embark-rerun-collect-or-export))) - (current-local-map))) - (let ((embark-after-export-hook after) - (embark--command cmd)) - (run-hooks 'embark-after-export-hook))))))))) - -(defmacro embark--export-rename (buffer title &rest body) - "Run BODY and rename BUFFER to Embark export buffer with TITLE." - (declare (indent 2)) - (let ((saved (make-symbol "saved"))) - `(let ((,saved (embark-rename-buffer - ,buffer " *Embark Saved*" t))) - ,@body - (set-buffer (embark-rename-buffer - ,buffer ,(format "*Embark Export %s*" title) t)) - (when ,saved (embark-rename-buffer ,saved ,buffer))))) - -(defun embark--export-customize (items type pred) - "Create a customization buffer listing ITEMS. -TYPE is the items type. -PRED is a predicate function used to filter the items." - (custom-buffer-create - (cl-loop for item in items - for sym = (intern-soft item) - when (and sym (funcall pred sym)) collect `(,sym ,type)))) - -(autoload 'apropos-parse-pattern "apropos") -(autoload 'apropos-symbols-internal "apropos") -(defun embark-export-apropos (symbols) - "Create apropos buffer listing SYMBOLS." - (embark--export-rename "*Apropos*" "Apropos" - (apropos-parse-pattern "") ;; Initialize apropos pattern - ;; HACK: Ensure that order of exported symbols is kept. - (cl-letf (((symbol-function #'sort) (lambda (list _pred) (nreverse list)))) - (apropos-symbols-internal - (delq nil (mapcar #'intern-soft symbols)) - (bound-and-true-p apropos-do-all))))) - -(defun embark-export-customize-face (faces) - "Create a customization buffer listing FACES." - (embark--export-customize faces 'custom-face #'facep)) - -(defun embark-export-customize-variable (variables) - "Create a customization buffer listing VARIABLES." - ;; The widget library serializes/deserializes the values. We advise - ;; the serialization in order to avoid errors for nonserializable - ;; variables. - (cl-letf* ((ht (make-hash-table :test #'equal)) - (orig-read (symbol-function #'read)) - (orig-write (symbol-function 'widget-sexp-value-to-internal)) - ((symbol-function #'read) - (lambda (&optional str) - (condition-case nil - (funcall orig-read str) - (error (gethash str ht))))) - ((symbol-function 'widget-sexp-value-to-internal) - (lambda (widget val) - (let ((str (funcall orig-write widget val))) - (puthash str val ht) - str)))) - (embark--export-customize variables 'custom-variable #'boundp))) - -(defun embark-export-ibuffer (buffers) - "Create an ibuffer buffer listing BUFFERS." - (ibuffer t "*Embark Export Ibuffer*" - `((predicate . (member (buffer-name) ',buffers))))) - -(autoload 'dired-check-switches "dired") -(declare-function dired-unadvertise "dired") -(defvar dired-directory) - -(defun embark-export-dired (files) - "Create a Dired buffer listing FILES." - (setq files (mapcar #'directory-file-name - (cl-remove-if-not #'file-exists-p files))) - (when (dired-check-switches dired-listing-switches "A" "almost-all") - (setq files (cl-remove-if - (lambda (path) - (let ((file (file-name-nondirectory path))) - (or (string= file ".") (string= file "..")))) - files))) - (cl-letf* ((dir (or (file-name-directory (try-completion "" files)) "")) - ;; Prevent reusing existing Dired buffer. - ((symbol-function 'dired-find-buffer-nocreate) #'ignore) - (buf (dired-noselect - (cons (expand-file-name dir) - (mapcar (lambda (file) (string-remove-prefix dir file)) - files))))) - (with-current-buffer buf - ;; Unadvertise to prevent the new buffer from being reused. - (dired-unadvertise default-directory) - (rename-buffer (format "*Embark Export Dired %s*" default-directory))) - (pop-to-buffer buf))) - -(autoload 'package-menu-mode "package") -(autoload 'package-menu--generate "package") - -(defun embark-export-list-packages (packages) - "Create a package menu mode buffer listing PACKAGES." - (let ((buf (generate-new-buffer "*Embark Export Packages*"))) - (with-current-buffer buf - (package-menu-mode) - (package-menu--generate nil (mapcar #'intern packages))) - (pop-to-buffer buf))) - -(defvar bookmark-alist) - -(defun embark-export-bookmarks (bookmarks) - "Create a `bookmark-bmenu-mode' buffer listing BOOKMARKS." - (embark--export-rename "*Bookmark List*" "Bookmarks" - (let ((bookmark-alist - (cl-remove-if-not - (lambda (bmark) - (member (car bmark) bookmarks)) - bookmark-alist))) - (bookmark-bmenu-list)))) - -;;; Multiple target selection - -(defface embark-selected '((t (:inherit match))) - "Face for selected candidates.") - -(defcustom embark-selection-indicator - #(" Embark:%s " 1 12 (face (embark-selected bold))) - "Mode line indicator used for selected candidates." - :type '(choice string (const nil))) - -(defvar-local embark--selection nil - "Buffer local list of selected targets. -Add or remove elements to this list using the `embark-select' -action.") - -(defun embark--selection-indicator () - "Mode line indicator showing number of selected items." - (when-let ((sel - (buffer-local-value - 'embark--selection - (or (when-let ((win (active-minibuffer-window))) - (window-buffer win)) - (current-buffer))))) - (format embark-selection-indicator (length sel)))) - -(cl-defun embark--select - (&key orig-target orig-type bounds &allow-other-keys) - "Add or remove ORIG-TARGET of given ORIG-TYPE to the selection. -If BOUNDS are given, also highlight the target when selecting it." - (cl-flet ((multi-type (x) (car (get-text-property 0 'multi-category x)))) - (if-let* ((existing (seq-find - (pcase-lambda (`(,cand . ,ov)) - (and - (equal cand orig-target) - (if (and bounds ov) - (and (= (car bounds) (overlay-start ov)) - (= (cdr bounds) (overlay-end ov))) - (let ((cand-type (multi-type cand))) - (or (eq cand-type orig-type) - (eq cand-type (multi-type orig-target))))))) - embark--selection))) - (progn - (when (cdr existing) (delete-overlay (cdr existing))) - (setq embark--selection (delq existing embark--selection))) - (let ((target (copy-sequence orig-target)) overlay) - (when bounds - (setq overlay (make-overlay (car bounds) (cdr bounds))) - (overlay-put overlay 'category 'embark-selected-overlay)) - (add-text-properties 0 (length orig-target) - `(multi-category ,(cons orig-type orig-target)) - target) - (push (cons target overlay) embark--selection)))) - (when embark-selection-indicator - (add-to-list 'mode-line-misc-info '(:eval (embark--selection-indicator))) - (force-mode-line-update t))) - -;;;###autoload -(defun embark-select () - "Add or remove the target from the current buffer's selection. -You can act on all selected targets at once with `embark-act-all'. -When called from outside `embark-act' this command will select -the first target at point." - (interactive) - (if-let ((target (car (embark--targets)))) - (apply #'embark--select target) - (user-error "No target to select"))) - -(defun embark-selected-candidates () - "Return currently selected candidates in the buffer." - (when embark--selection - (cl-flet ((unwrap (x) (get-text-property 0 'multi-category x))) - (let* ((first-type (car (unwrap (caar embark--selection)))) - (same (cl-every (lambda (item) - (eq (car (unwrap (car item))) first-type)) - embark--selection)) - (extract (if same - (pcase-lambda (`(,cand . ,overlay)) - (cons (cdr (unwrap cand)) overlay)) - #'identity))) - (cons - (if same first-type 'multi-category) - (nreverse - (mapcar - (lambda (item) - (pcase-let ((`(,cand . ,ov) (funcall extract item))) - (if ov `(,cand ,(overlay-start ov) . ,(overlay-end ov)) cand))) - embark--selection))))))) - -;;; Integration with external packages, mostly completion UIs - -;; marginalia - -;; Ensure that the Marginalia cache is reset, such that -;; `embark-toggle-variable-value' updates the display (See #540). -(with-eval-after-load 'marginalia - (push 'marginalia--cache-reset (alist-get :always embark-post-action-hooks))) - -;; vertico - -(declare-function vertico--candidate "ext:vertico") -(declare-function vertico--update "ext:vertico") -(defvar vertico--input) -(defvar vertico--candidates) -(defvar vertico--base) - -(defun embark--vertico-selected () - "Target the currently selected item in Vertico. -Return the category metadatum as the type of the target." - (when vertico--input - ;; Force candidate computation, if candidates are not yet available. - (vertico--update) - (cons (completion-metadata-get (embark--metadata) 'category) - (vertico--candidate)))) - -(defun embark--vertico-candidates () - "Collect the current Vertico candidates. -Return the category metadatum as the type of the candidates." - (when vertico--input - ;; Force candidate computation, if candidates are not yet available. - (vertico--update) - (cons (completion-metadata-get (embark--metadata) 'category) - vertico--candidates))) - -(defun embark--vertico-indicator () - "Embark indicator highlighting the current Vertico candidate." - (let ((fr face-remapping-alist)) - (lambda (&optional keymap _targets _prefix) - (when vertico--input - (setq-local face-remapping-alist - (if keymap - (cons '(vertico-current . embark-target) fr) - fr)))))) - -(with-eval-after-load 'vertico - (cl-defmethod vertico--format-candidate - :around (cand prefix suffix index start &context (embark--selection cons)) - (when (cl-find (concat vertico--base (nth index vertico--candidates)) - embark--selection - :test #'equal :key #'car) - (setq cand (copy-sequence cand)) - (add-face-text-property 0 (length cand) 'embark-selected t cand)) - (cl-call-next-method cand prefix suffix index start)) - (add-hook 'embark-indicators #'embark--vertico-indicator) - (add-hook 'embark-target-finders #'embark--vertico-selected) - (add-hook 'embark-candidate-collectors #'embark--vertico-candidates) - (remove-hook 'embark-candidate-collectors #'embark-selected-candidates) - (add-hook 'embark-candidate-collectors #'embark-selected-candidates)) - -;; ivy - -(declare-function ivy--expand-file-name "ext:ivy") -(declare-function ivy-state-current "ext:ivy") -(defvar ivy-text) -(defvar ivy-last) -(defvar ivy--old-cands) ; this stores the current candidates :) -(defvar ivy--length) - -(defun embark--ivy-selected () - "Target the currently selected item in Ivy. -Return the category metadatum as the type of the target." - ;; my favorite way of detecting Ivy - (when (memq 'ivy--queue-exhibit post-command-hook) - (cons - (completion-metadata-get (embark--metadata) 'category) - (ivy--expand-file-name - (if (and (> ivy--length 0) - (stringp (ivy-state-current ivy-last))) - (ivy-state-current ivy-last) - ivy-text))))) - -(defun embark--ivy-candidates () - "Return all current Ivy candidates." - ;; my favorite way of detecting Ivy - (when (memq 'ivy--queue-exhibit post-command-hook) - (cons - ;; swiper-isearch uses swiper-isearch-function as a completion - ;; table, but it doesn't understand metadata queries - (ignore-errors - (completion-metadata-get (embark--metadata) 'category)) - ivy--old-cands))) - -(with-eval-after-load 'ivy - (add-hook 'embark-target-finders #'embark--ivy-selected) - (add-hook 'embark-candidate-collectors #'embark--ivy-candidates) - (remove-hook 'embark-candidate-collectors #'embark-selected-candidates) - (add-hook 'embark-candidate-collectors #'embark-selected-candidates)) - -;;; Custom actions - -(defvar embark-separator-history nil - "Input history for the separators used by some embark commands. -The commands that prompt for a string separator are -`embark-insert' and `embark-copy-as-kill'.") - -(defun embark-keymap-help () - "Prompt for an action to perform or command to become and run it." - (interactive) - (user-error "Not meant to be called directly")) - -(defun embark-toggle-quit () - "Toggle whether the following action quits the minibuffer." - (interactive) - (when (minibufferp) - (setq embark--toggle-quit (not embark--toggle-quit)) - (if (consp embark-quit-after-action) - (message "Will %sobey embark-quit-after-action." - (if embark--toggle-quit "dis" "")) - (message - "Will %squit minibuffer after action" - (if (eq embark--toggle-quit embark-quit-after-action) "not " ""))))) - -(defun embark--separator (strings) - "Return a separator to join the STRINGS together. -With a prefix argument, prompt the user (unless STRINGS has 0 or -1 elements, in which case a separator is not needed)." - (if (and current-prefix-arg (cdr strings)) - (read-string "Separator: " nil 'embark-separator-history) - "\n")) - -(defun embark-copy-as-kill (strings) - "Join STRINGS and save on the `kill-ring'. -With a prefix argument, prompt for the separator to join the -STRINGS, which defaults to a newline." - (kill-new (string-join strings (embark--separator strings)))) - -(defun embark-insert (strings) - "Join STRINGS and insert the result at point. -With a prefix argument, prompt for the separator to join the -STRINGS, which defaults to a newline. - -Some whitespace is also inserted if necessary to avoid having the -inserted string blend into the existing buffer text. More -precisely: - -1. If the inserted string does not contain newlines, a space may -be added before or after it as needed to avoid inserting a word -constituent character next to an existing word constituent. - -2. For a multiline inserted string, newlines may be added before -or after as needed to ensure the inserted string is on lines of -its own." - (let* ((separator (embark--separator strings)) - (multiline - (or (and (cdr strings) (string-match-p "\n" separator)) - (and (null (cdr strings)) - (equal (buffer-substring (line-beginning-position) - (line-end-position)) - (car strings))) - (seq-some (lambda (s) (string-match-p "\n" s)) strings)))) - (cl-labels ((maybe-space () - (and (looking-at "\\w") (looking-back "\\w" 1) - (insert " "))) - (maybe-newline () - (or (looking-back "^[ \t]*" 40) (looking-at "\n") - (newline-and-indent))) - (maybe-whitespace () - (if multiline (maybe-newline) (maybe-space))) - (ins-string () - (let ((start (point))) - (insert - (mapconcat #'substring-no-properties strings separator)) - (save-excursion (goto-char start) (maybe-whitespace)) - (when (looking-back "\n" 1) (delete-char -1)) - (save-excursion (maybe-whitespace))))) - (if buffer-read-only - (with-selected-window (other-window-for-scrolling) - (ins-string)) - (ins-string))))) - -;; For Emacs 28 dired-jump will be moved to dired.el, but it seems -;; that since it already has an autoload in Emacs 28, this next -;; autoload is ignored. -(autoload 'dired-jump "dired-x" nil t) - -(defun embark-dired-jump (file &optional other-window) - "Open Dired buffer in directory containing FILE and move to its line. -When called with a prefix argument OTHER-WINDOW, open Dired in other window." - (interactive "fJump to Dired file: \nP") - (dired-jump other-window file)) - -(defun embark--read-from-history (prompt candidates &optional category) - "Read with completion from list of history CANDIDATES of CATEGORY. -Sorting and history are disabled. PROMPT is the prompt message." - (completing-read prompt - (embark--with-category category candidates) - nil t nil t)) - -(defun embark-kill-ring-remove (text) - "Remove TEXT from `kill-ring'." - (interactive (list (embark--read-from-history - "Remove from kill-ring: " kill-ring 'kill-ring))) - (embark-history-remove text) - (setq kill-ring (delete text kill-ring))) - -(defvar recentf-list) -(defun embark-recentf-remove (file) - "Remove FILE from the list of recent files." - (interactive (list (embark--read-from-history - "Remove recent file: " recentf-list 'file))) - (embark-history-remove (expand-file-name file)) - (embark-history-remove (abbreviate-file-name file)) - (when (and (boundp 'recentf-list) (fboundp 'recentf-expand-file-name)) - (setq recentf-list (delete (recentf-expand-file-name file) recentf-list)))) - -(defun embark-history-remove (str) - "Remove STR from `minibuffer-history-variable'. -Many completion UIs sort by history position. This command can be used -to remove entries from the history, such that they are not sorted closer -to the top." - (interactive (list (embark--read-from-history - "Remove history item: " - (if (eq minibuffer-history-variable t) - (user-error "No minibuffer history") - (symbol-value minibuffer-history-variable))))) - (unless (eq minibuffer-history-variable t) - (set minibuffer-history-variable - (delete str (symbol-value minibuffer-history-variable))))) - -(defvar xref-backend-functions) - -(defun embark-find-definition (symbol) - "Find definition of Emacs Lisp SYMBOL." - (interactive "sSymbol: ") - (let ((xref-backend-functions (lambda () 'elisp))) - (xref-find-definitions symbol))) - -(defun embark-info-lookup-symbol (symbol) - "Display the definition of SYMBOL, from the Elisp manual." - (interactive "SSymbol: ") - (info-lookup-symbol symbol 'emacs-lisp-mode)) - -(defun embark-rename-buffer (buffer newname &optional unique) - "Rename BUFFER to NEWNAME, optionally making it UNIQUE. -Interactively, you can set UNIQUE with a prefix argument. -Returns the new name actually used." - (interactive "bBuffer: \nBRename %s to: \nP") - (when-let ((buf (get-buffer buffer))) - (with-current-buffer buf - (rename-buffer newname unique)))) - -(defun embark--package-url (pkg) - "Return homepage for package PKG." - (when-let (desc (embark--package-desc pkg)) - (alist-get :url (package-desc-extras desc)))) - -(defun embark--prompt-for-package () - "Prompt user for a package name." - ;; this code is taken from the interactive spec of describe-package - (unless package--initialized - (package-initialize t)) - (intern - (completing-read "Package: " - (append (mapcar #'car package-alist) - (mapcar #'car package-archive-contents) - (mapcar #'car package--builtins))))) - -(defun embark-browse-package-url (pkg) - "Open homepage for package PKG with `browse-url'." - (interactive (list (embark--prompt-for-package))) - (if-let ((url (embark--package-url pkg))) - (browse-url url) - (user-error "No homepage found for `%s'" pkg))) - -(defun embark-save-package-url (pkg) - "Save URL of homepage for package PKG on the `kill-ring'." - (interactive (list (embark--prompt-for-package))) - (if-let ((url (embark--package-url pkg))) - (kill-new url) - (user-error "No homepage found for `%s'" pkg))) - -(defun embark-save-variable-value (var) - "Save value of VAR in the `kill-ring'." - (interactive "SVariable: ") - (kill-new (string-trim (pp-to-string (symbol-value var))))) - -(defun embark-insert-variable-value (var) - "Insert value of VAR." - (interactive "SVariable: ") - (embark-insert (list (string-trim (pp-to-string (symbol-value var)))))) - -(defun embark-toggle-variable (var &optional local) - "Toggle value of boolean variable VAR. -If prefix LOCAL is non-nil make variable local." - (interactive "SVariable: \nP") - (let ((val (symbol-value var))) - (unless (memq val '(nil t)) - (user-error "Not a boolean variable")) - (when local - (make-local-variable var)) - (funcall (or (get var 'custom-set) 'set) var (not val)))) - -(defun embark-insert-relative-path (file) - "Insert relative path to FILE. -The insert path is relative to `default-directory'." - (interactive "FFile: ") - (embark-insert (list (file-relative-name (substitute-in-file-name file))))) - -(defun embark-save-relative-path (file) - "Save the relative path to FILE in the kill ring. -The insert path is relative to `default-directory'." - (interactive "FFile: ") - (kill-new (file-relative-name (substitute-in-file-name file)))) - -(defun embark-shell-command-on-buffer (buffer command &optional replace) - "Run shell COMMAND on contents of BUFFER. -Called with \\[universal-argument], replace contents of buffer -with command output. For replacement behavior see -`shell-command-dont-erase-buffer' setting." - (interactive - (list - (read-buffer "Buffer: " nil t) - (read-shell-command "Shell command: ") - current-prefix-arg)) - (with-current-buffer buffer - (shell-command-on-region (point-min) (point-max) - command - (and replace (current-buffer))))) - -(defun embark-open-externally (file) - "Open FILE or url using system's default application." - (interactive "sOpen externally: ") - (unless (string-match-p "\\`[a-z]+://" file) - (setq file (expand-file-name file))) - (message "Opening `%s' externally..." file) - (if (and (eq system-type 'windows-nt) - (fboundp 'w32-shell-execute)) - (w32-shell-execute "open" file) - (call-process (pcase system-type - ('darwin "open") - ('cygwin "cygstart") - (_ "xdg-open")) - nil 0 nil file))) - -(declare-function bookmark-prop-get "bookmark") -(declare-function bookmark-completing-read "bookmark") - -(defun embark-bookmark-open-externally (bookmark) - "Open BOOKMARK in external application." - (interactive (list (bookmark-completing-read "Open externally: "))) - (embark-open-externally - (or (bookmark-prop-get bookmark 'location) - (bookmark-prop-get bookmark 'filename) - (user-error "Bookmark `%s' does not have a location" bookmark)))) - -(defun embark-bury-buffer (buf) - "Bury buffer BUF." - (interactive "bBuffer: ") - (if-let (win (get-buffer-window buf)) - (with-selected-window win - (bury-buffer)) - (bury-buffer))) - -(defun embark-kill-buffer-and-window (buf) - "Kill buffer BUF and delete its window." - (interactive "bBuffer: ") - (when-let (buf (get-buffer buf)) - (if-let (win (get-buffer-window buf)) - (with-selected-window win - (kill-buffer-and-window)) - (kill-buffer buf)))) - -(defun embark-save-unicode-character (char) - "Save Unicode character CHAR to kill ring." - (interactive - (list (read-char-by-name "Insert character (Unicode name or hex): "))) - (kill-new (format "%c" char))) - -(defun embark-isearch-forward () - "Prompt for string in the minibuffer and start isearch forwards. -Unlike isearch, this command reads the string from the -minibuffer, which means it can be used as an Embark action." - (interactive) - (isearch-mode t) - (isearch-edit-string)) - -(defun embark-isearch-backward () - "Prompt for string in the minibuffer and start isearch backwards. -Unlike isearch, this command reads the string from the -minibuffer, which means it can be used as an Embark action." - (interactive) - (isearch-mode nil) - (isearch-edit-string)) - -(defun embark-toggle-highlight () - "Toggle symbol highlighting using `highlight-symbol-at-point'." - (interactive) - (let ((regexp (find-tag-default-as-symbol-regexp)) - (highlighted (cl-find-if #'boundp - '(hi-lock-interactive-lighters - hi-lock-interactive-patterns)))) - (if (and highlighted (assoc regexp (symbol-value highlighted))) - (unhighlight-regexp regexp) - (highlight-symbol-at-point)))) - -(defun embark-next-symbol () - "Jump to next occurrence of symbol at point. -The search respects symbol boundaries." - (interactive) - (if-let ((symbol (thing-at-point 'symbol))) - (let ((regexp (format "\\_<%s\\_>" (regexp-quote symbol)))) - (when (looking-at regexp) - (forward-symbol 1)) - (unless (re-search-forward regexp nil t) - (user-error "Symbol `%s' not found" symbol))) - (user-error "No symbol at point"))) - -(defun embark-previous-symbol () - "Jump to previous occurrence of symbol at point. -The search respects symbol boundaries." - (interactive) - (if-let ((symbol (thing-at-point 'symbol))) - (let ((regexp (format "\\_<%s\\_>" (regexp-quote symbol)))) - (when (looking-back regexp (- (point) (length symbol))) - (forward-symbol -1)) - (unless (re-search-backward regexp nil t) - (user-error "Symbol `%s' not found" symbol))) - (user-error "No symbol at point"))) - -(defun embark-compose-mail (address) - "Compose email to ADDRESS." - ;; The only reason we cannot use compose-mail directly is its - ;; interactive specification, which just supplies nil for the - ;; address (and several other arguments). - (interactive "sTo: ") - (compose-mail address)) - -(autoload 'pp-display-expression "pp") - -(defun embark-pp-eval-defun (edebug) - "Run `eval-defun' and pretty print the result. -With a prefix argument EDEBUG, instrument the code for debugging." - (interactive "P") - (cl-letf (((symbol-function #'eval-expression-print-format) - (lambda (result) - (pp-display-expression result "*Pp Eval Output*")))) - (eval-defun edebug))) - -(defun embark-eval-replace (noquote) - "Evaluate region and replace with evaluated result. -If NOQUOTE is non-nil (interactively, if called with a prefix -argument), no quoting is used for strings." - (interactive "P") - (let ((beg (region-beginning)) - (end (region-end))) - (save-excursion - (goto-char end) - (insert (format (if noquote "%s" "%S") - (eval (read (buffer-substring beg end)) lexical-binding))) - (delete-region beg end)))) - -(when (< emacs-major-version 29) - (defun embark-elp-restore-package (prefix) - "Remove instrumentation from functions with names starting with PREFIX." - (interactive "SPrefix: ") - (when (fboundp 'elp-restore-list) - (elp-restore-list - (mapcar #'intern - (all-completions (symbol-name prefix) - obarray 'elp-profilable-p)))))) - -(defmacro embark--define-hash (algorithm) - "Define command which computes hash from a string. -ALGORITHM is the hash algorithm symbol understood by `secure-hash'." - `(defun ,(intern (format "embark-hash-%s" algorithm)) (str) - ,(format "Compute %s hash of STR and store it in the kill ring." algorithm) - (interactive "sString: ") - (let ((hash (secure-hash ',algorithm str))) - (kill-new hash) - (message "%s: %s" ',algorithm hash)))) - -(embark--define-hash md5) -(embark--define-hash sha1) -(embark--define-hash sha224) -(embark--define-hash sha256) -(embark--define-hash sha384) -(embark--define-hash sha512) - -(defun embark-encode-url (start end) - "Properly URI-encode the region between START and END in current buffer." - (interactive "r") - (let ((encoded (url-encode-url (buffer-substring-no-properties start end)))) - (delete-region start end) - (insert encoded))) - -(defun embark-decode-url (start end) - "Decode the URI-encoded region between START and END in current buffer." - (interactive "r") - (let ((decoded (url-unhex-string (buffer-substring-no-properties start end)))) - (delete-region start end) - (insert decoded))) - -(defvar epa-replace-original-text) -(defun embark-epa-decrypt-region (start end) - "Decrypt region between START and END." - (interactive "r") - (let ((epa-replace-original-text t)) - (epa-decrypt-region start end))) - -(defvar eww-download-directory) -(autoload 'eww-download-callback "eww") - -(defun embark-download-url (url) - "Download URL to `eww-download-directory'." - (interactive "sDownload URL: ") - (let ((dir eww-download-directory)) - (when (functionp dir) (setq dir (funcall dir))) - (access-file dir "Download failed") - (url-retrieve - url #'eww-download-callback - (if (>= emacs-major-version 28) (list url dir) (list url))))) - -;;; Setup and pre-action hooks - -(defun embark--restart (&rest _) - "Restart current command with current input. -Use this to refresh the list of candidates for commands that do -not handle that themselves." - (when (minibufferp) - (embark--become-command embark--command (minibuffer-contents)))) - -(defun embark--shell-prep (&rest _) - "Prepare target for use as argument for a shell command. -This quotes the spaces, inserts an extra space at the beginning -and leaves the point to the left of it." - (let ((contents (minibuffer-contents))) - (delete-minibuffer-contents) - (insert " " (shell-quote-wildcard-pattern contents)) - (goto-char (minibuffer-prompt-end)))) - -(defun embark--force-complete (&rest _) - "Select first minibuffer completion candidate matching target." - (minibuffer-force-complete)) - -(cl-defun embark--eval-prep (&key type &allow-other-keys) - "If target's TYPE is variable, skip edit; if function, wrap in ()." - (when (memq type '(command function)) - (embark--allow-edit) - (goto-char (minibuffer-prompt-end)) - (insert "(") - (goto-char (point-max)) - (insert ")") - (backward-char))) - -(cl-defun embark--beginning-of-target (&key bounds &allow-other-keys) - "Go to beginning of the target BOUNDS." - (when (number-or-marker-p (car bounds)) - (goto-char (car bounds)))) - -(cl-defun embark--end-of-target (&key bounds &allow-other-keys) - "Go to end of the target BOUNDS." - (when (number-or-marker-p (cdr bounds)) - (goto-char (cdr bounds)))) - -(cl-defun embark--mark-target (&rest rest &key run bounds &allow-other-keys) - "Mark the target if its BOUNDS are known. -After marking the target, call RUN with the REST of its arguments." - (cond - ((and bounds run) - (save-mark-and-excursion - (set-mark (cdr bounds)) - (goto-char (car bounds)) - (apply run :bounds bounds rest))) - (bounds ;; used as pre- or post-action hook - (set-mark (cdr bounds)) - (goto-char (car bounds))) - (run (apply run rest)))) - -(cl-defun embark--unmark-target (&rest _) - "Deactivate the region target." - (deactivate-mark t)) - -(cl-defun embark--narrow-to-target - (&rest rest &key run bounds &allow-other-keys) - "Narrow buffer to target if its BOUNDS are known. -Intended for use as an Embark around-action hook. This function -runs RUN with the buffer narrowed to given BOUNDS passing along -the REST of the arguments." - (if bounds - (save-excursion - (save-restriction - (narrow-to-region (car bounds) (cdr bounds)) - (goto-char (car bounds)) - (apply run :bounds bounds rest))) - (apply run rest))) - -(defun embark--allow-edit (&rest _) - "Allow editing the target." - (remove-hook 'post-command-hook #'exit-minibuffer t) - (remove-hook 'post-command-hook 'ivy-immediate-done t)) - -(defun embark--ignore-target (&rest _) - "Ignore the target." - (let ((contents - (get-text-property (minibuffer-prompt-end) 'embark--initial-input))) - (delete-minibuffer-contents) - (when contents (insert contents))) - (embark--allow-edit)) - -(autoload 'xref-push-marker-stack "xref") -(defun embark--xref-push-marker (&rest _) - "Push a marker onto the xref marker stack." - (xref-push-marker-stack)) - -(cl-defun embark--confirm (&key action target &allow-other-keys) - "Ask for confirmation before running the ACTION on the TARGET." - (unless (y-or-n-p (format "Run %s on %s? " action target)) - (user-error "Canceled"))) - -(defconst embark--associated-file-fn-alist - `((file . identity) - (buffer . ,(lambda (target) - (let ((buffer (get-buffer target))) - (or (buffer-file-name buffer) - (buffer-local-value 'default-directory buffer))))) - (bookmark . bookmark-location) - (library . locate-library)) - "Alist of functions that extract a file path from targets of a given type.") - -(defun embark--associated-directory (target type) - "Return directory associated to TARGET of given TYPE. -The supported values of TYPE are file, buffer, bookmark and -library, which have an obvious notion of associated directory." - (when-let ((file-fn (alist-get type embark--associated-file-fn-alist)) - (file (funcall file-fn target))) - (if (file-directory-p file) - (file-name-as-directory file) - (file-name-directory file)))) - -(cl-defun embark--cd (&rest rest &key run target type &allow-other-keys) - "Run action with `default-directory' set to the directory of TARGET. -The supported values of TYPE are file, buffer, bookmark and -library, which have an obvious notion of associated directory. -The REST of the arguments are also passed to RUN." - (let ((default-directory - (or (embark--associated-directory target type) default-directory))) - (apply run :target target :type type rest))) - -(cl-defun embark--save-excursion (&rest rest &key run &allow-other-keys) - "Run action without moving point. -This simply calls RUN with the REST of its arguments inside -`save-excursion'." - (save-excursion (apply run rest))) - -(defun embark--universal-argument (&rest _) - "Run action with a universal prefix argument." - (setq prefix-arg '(4))) - -;;; keymaps - -(defvar-keymap embark-meta-map - :doc "Keymap for non-action Embark functions." - "-" #'negative-argument - "0" #'digit-argument - "1" #'digit-argument - "2" #'digit-argument - "3" #'digit-argument - "4" #'digit-argument - "5" #'digit-argument - "6" #'digit-argument - "7" #'digit-argument - "8" #'digit-argument - "9" #'digit-argument) - -(defvar-keymap embark-general-map - :doc "Keymap for Embark general actions." - :parent embark-meta-map - "i" #'embark-insert - "w" #'embark-copy-as-kill - "q" #'embark-toggle-quit - "E" #'embark-export - "S" #'embark-collect - "L" #'embark-live - "B" #'embark-become - "A" #'embark-act-all - "C-s" #'embark-isearch-forward - "C-r" #'embark-isearch-backward - "C-SPC" #'mark - "DEL" #'delete-region - "SPC" #'embark-select) - -(defvar-keymap embark-encode-map - :doc "Keymap for Embark region encoding actions." - "r" #'rot13-region - "." #'morse-region - "-" #'unmorse-region - "s" #'studlify-region - "m" #'embark-hash-md5 - "1" #'embark-hash-sha1 - "2" #'embark-hash-sha256 - "3" #'embark-hash-sha384 - "4" #'embark-hash-sha224 - "5" #'embark-hash-sha512 - "f" #'format-encode-region - "F" #'format-decode-region - "b" #'base64-encode-region - "B" #'base64-decode-region - "u" #'embark-encode-url - "U" #'embark-decode-url - "c" #'epa-encrypt-region - "C" #'embark-epa-decrypt-region) - -(fset 'embark-encode-map embark-encode-map) - -(defvar-keymap embark-sort-map - :doc "Keymap for Embark actions that sort the region" - "l" #'sort-lines - "P" #'sort-pages - "f" #'sort-fields - "c" #'sort-columns - "p" #'sort-paragraphs - "r" #'sort-regexp-fields - "n" #'sort-numeric-fields) - -(fset 'embark-sort-map embark-sort-map) - -;; these will have autoloads in Emacs 28 -(autoload 'calc-grab-sum-down "calc" nil t) -(autoload 'calc-grab-sum-across "calc" nil t) - -;; this has had an autoload cookie since at least Emacs 26 -;; but that autoload doesn't seem to work for me -(autoload 'org-table-convert-region "org-table" nil t) - -(defvar-keymap embark-region-map - :doc "Keymap for Embark actions on the active region." - :parent embark-general-map - "u" #'upcase-region - "l" #'downcase-region - "c" #'capitalize-region - "|" #'shell-command-on-region - "e" #'eval-region - "<" #'embark-eval-replace - "a" #'align - "A" #'align-regexp - "" #'indent-rigidly - "" #'indent-rigidly - "TAB" #'indent-region - "f" #'fill-region - "p" #'fill-region-as-paragraph - "$" #'ispell-region - "=" #'count-words-region - "F" #'whitespace-cleanup-region - "t" #'transpose-regions - "o" #'org-table-convert-region - ";" #'comment-or-uncomment-region - "W" #'write-region - "+" #'append-to-file - "m" #'apply-macro-to-region-lines - "n" #'narrow-to-region - "*" #'calc-grab-region - ":" #'calc-grab-sum-down - "_" #'calc-grab-sum-across - "r" #'reverse-region - "d" #'delete-duplicate-lines - "b" #'browse-url-of-region - "h" #'shr-render-region - "'" #'expand-region-abbrevs - "v" #'vc-region-history - "R" #'repunctuate-sentences - "s" 'embark-sort-map - ">" 'embark-encode-map) - -(defvar-keymap embark-vc-file-map - :doc "Keymap for Embark VC file actions." - "d" #'vc-delete-file - "r" #'vc-rename-file - "i" #'vc-ignore) - -(fset 'embark-vc-file-map embark-vc-file-map) - -(defvar-keymap embark-file-map - :doc "Keymap for Embark file actions." - :parent embark-general-map - "RET" #'find-file - "f" #'find-file - "F" #'find-file-literally - "o" #'find-file-other-window - "d" #'delete-file - "D" #'delete-directory - "r" #'rename-file - "c" #'copy-file - "s" #'make-symbolic-link - "j" #'embark-dired-jump - "!" #'shell-command - "&" #'async-shell-command - "$" #'eshell - "<" #'insert-file - "m" #'chmod - "=" #'ediff-files - "+" #'make-directory - "\\" #'embark-recentf-remove - "I" #'embark-insert-relative-path - "W" #'embark-save-relative-path - "x" #'embark-open-externally - "e" #'eww-open-file - "l" #'load-file - "b" #'byte-compile-file - "R" #'byte-recompile-directory - "v" 'embark-vc-file-map) - -(defvar-keymap embark-kill-ring-map - :doc "Keymap for `kill-ring' commands." - :parent embark-general-map - "\\" #'embark-kill-ring-remove) - -(defvar-keymap embark-url-map - :doc "Keymap for Embark url actions." - :parent embark-general-map - "RET" #'browse-url - "b" #'browse-url - "d" #'embark-download-url - "x" #'embark-open-externally - "e" #'eww) - -(defvar-keymap embark-email-map - :doc "Keymap for Embark email actions." - :parent embark-general-map - "RET" #'embark-compose-mail - "c" #'embark-compose-mail) - -(defvar-keymap embark-library-map - :doc "Keymap for operations on Emacs Lisp libraries." - :parent embark-general-map - "RET" #'find-library - "l" #'load-library - "f" #'find-library - "h" #'finder-commentary - "a" #'apropos-library - "L" #'locate-library - "m" #'info-display-manual - "$" #'eshell) - -(defvar-keymap embark-buffer-map - :doc "Keymap for Embark buffer actions." - :parent embark-general-map - "RET" #'switch-to-buffer - "k" #'kill-buffer - "b" #'switch-to-buffer - "o" #'switch-to-buffer-other-window - "z" #'embark-bury-buffer - "K" #'embark-kill-buffer-and-window - "r" #'embark-rename-buffer - "=" #'ediff-buffers - "|" #'embark-shell-command-on-buffer - "<" #'insert-buffer - "$" #'eshell) - -(defvar-keymap embark-tab-map - :doc "Keymap for actions for tab-bar tabs." - :parent embark-general-map - "RET" #'tab-bar-select-tab-by-name - "s" #'tab-bar-select-tab-by-name - "r" #'tab-bar-rename-tab-by-name - "k" #'tab-bar-close-tab-by-name) - -(defvar-keymap embark-identifier-map - :doc "Keymap for Embark identifier actions." - :parent embark-general-map - "RET" #'xref-find-definitions - "h" #'display-local-help - "H" #'embark-toggle-highlight - "d" #'xref-find-definitions - "r" #'xref-find-references - "a" #'xref-find-apropos - "s" #'info-lookup-symbol - "n" #'embark-next-symbol - "p" #'embark-previous-symbol - "'" #'expand-abbrev - "$" #'ispell-word - "o" #'occur) - -(defvar-keymap embark-expression-map - :doc "Keymap for Embark expression actions." - :parent embark-general-map - "RET" #'pp-eval-expression - "e" #'pp-eval-expression - "<" #'embark-eval-replace - "m" #'pp-macroexpand-expression - "TAB" #'indent-region - "r" #'raise-sexp - ";" #'comment-dwim - "t" #'transpose-sexps - "k" #'kill-region - "u" #'backward-up-list - "n" #'forward-list - "p" #'backward-list) - -(defvar-keymap embark-defun-map - :doc "Keymap for Embark defun actions." - :parent embark-expression-map - "RET" #'embark-pp-eval-defun - "e" #'embark-pp-eval-defun - "c" #'compile-defun - "D" #'edebug-defun - "o" #'checkdoc-defun - "N" #'narrow-to-defun) - -;; Use quoted symbols to avoid byte-compiler warnings. -(defvar-keymap embark-heading-map - :doc "Keymap for Embark heading actions." - :parent embark-general-map - "RET" 'outline-show-subtree - "TAB" 'outline-cycle ;; New in Emacs 28! - "C-SPC" 'outline-mark-subtree - "n" 'outline-next-visible-heading - "p" 'outline-previous-visible-heading - "f" 'outline-forward-same-level - "b" 'outline-backward-same-level - "^" 'outline-move-subtree-up - "v" 'outline-move-subtree-down - "u" 'outline-up-heading - "+" 'outline-show-subtree - "-" 'outline-hide-subtree - ">" 'outline-demote - "<" 'outline-promote) - -(defvar-keymap embark-symbol-map - :doc "Keymap for Embark symbol actions." - :parent embark-identifier-map - "RET" #'embark-find-definition - "h" #'describe-symbol - "s" #'embark-info-lookup-symbol - "d" #'embark-find-definition - "e" #'pp-eval-expression - "a" #'apropos - "\\" #'embark-history-remove) - -(defvar-keymap embark-face-map - :doc "Keymap for Embark face actions." - :parent embark-symbol-map - "h" #'describe-face - "c" #'customize-face - "+" #'make-face-bold - "-" #'make-face-unbold - "/" #'make-face-italic - "|" #'make-face-unitalic - "!" #'invert-face - "f" #'set-face-foreground - "b" #'set-face-background) - -(defvar-keymap embark-variable-map - :doc "Keymap for Embark variable actions." - :parent embark-symbol-map - "=" #'set-variable - "c" #'customize-set-variable - "u" #'customize-variable - "v" #'embark-save-variable-value - "<" #'embark-insert-variable-value - "t" #'embark-toggle-variable) - -(defvar-keymap embark-function-map - :doc "Keymap for Embark function actions." - :parent embark-symbol-map - "m" #'elp-instrument-function ;; m=measure - "M" 'elp-restore-function ;; quoted, not autoloaded - "k" #'debug-on-entry ;; breaKpoint (running out of letters, really) - "K" #'cancel-debug-on-entry - "t" #'trace-function - "T" 'untrace-function) ;; quoted, not autoloaded - -(defvar-keymap embark-command-map - :doc "Keymap for Embark command actions." - :parent embark-function-map - "x" #'execute-extended-command - "I" #'Info-goto-emacs-command-node - "b" #'where-is - "g" #'global-set-key - "l" #'local-set-key) - -(defvar-keymap embark-package-map - :doc "Keymap for Embark package actions." - :parent embark-general-map - "RET" #'describe-package - "h" #'describe-package - "i" #'package-install - "I" #'embark-insert - "d" #'package-delete - "r" #'package-reinstall - "u" #'embark-browse-package-url - "W" #'embark-save-package-url - "a" #'package-autoremove - "g" #'package-refresh-contents - "m" #'elp-instrument-package ;; m=measure - "M" (if (fboundp 'embark-elp-restore-package) - 'embark-elp-restore-package - 'elp-restore-package)) - -(defvar-keymap embark-bookmark-map - :doc "Keymap for Embark bookmark actions." - :parent embark-general-map - "RET" #'bookmark-jump - "s" #'bookmark-set - "d" #'bookmark-delete - "r" #'bookmark-rename - "R" #'bookmark-relocate - "l" #'bookmark-locate - "<" #'bookmark-insert - "j" #'bookmark-jump - "o" #'bookmark-jump-other-window - "f" #'bookmark-jump-other-frame - "a" 'bookmark-show-annotation - "e" 'bookmark-edit-annotation - "x" #'embark-bookmark-open-externally - "$" #'eshell) - -(defvar-keymap embark-unicode-name-map - :doc "Keymap for Embark Unicode name actions." - :parent embark-general-map - "RET" #'insert-char - "I" #'insert-char - "W" #'embark-save-unicode-character) - -(defvar-keymap embark-prose-map - :doc "Keymap for Embark actions for dealing with prose." - :parent embark-general-map - "$" #'ispell-region - "f" #'fill-region - "u" #'upcase-region - "l" #'downcase-region - "c" #'capitalize-region - "F" #'whitespace-cleanup-region - "=" #'count-words-region) - -(defvar-keymap embark-sentence-map - :doc "Keymap for Embark actions for dealing with sentences." - :parent embark-prose-map - "t" #'transpose-sentences - "n" #'forward-sentence - "p" #'backward-sentence) - -(defvar-keymap embark-paragraph-map - :doc "Keymap for Embark actions for dealing with paragraphs." - :parent embark-prose-map - "t" #'transpose-paragraphs - "n" #'forward-paragraph - "p" #'backward-paragraph - "R" #'repunctuate-sentences) - -(defvar-keymap embark-flymake-map - :doc "Keymap for Embark actions on Flymake diagnostics." - :parent embark-general-map - "RET" 'flymake-show-buffer-diagnostics - "n" 'flymake-goto-next-error - "p" 'flymake-goto-prev-error) - -(defvar-keymap embark-become-help-map - :doc "Keymap for Embark help actions." - :parent embark-meta-map - "V" #'apropos-variable - "U" #'apropos-user-option - "C" #'apropos-command - "v" #'describe-variable - "f" #'describe-function - "s" #'describe-symbol - "F" #'describe-face - "p" #'describe-package - "i" #'describe-input-method) - -(autoload 'recentf-open-files "recentf" nil t) - -(defvar-keymap embark-become-file+buffer-map - :doc "Embark become keymap for files and buffers." - :parent embark-meta-map - "f" #'find-file - "4 f" #'find-file-other-window - "." #'find-file-at-point - "p" #'project-find-file - "r" #'recentf-open-files - "b" #'switch-to-buffer - "4 b" #'switch-to-buffer-other-window - "l" #'locate - "L" #'find-library - "v" #'vc-dir) - -(defvar-keymap embark-become-shell-command-map - :doc "Embark become keymap for shell commands." - :parent embark-meta-map - "!" #'shell-command - "&" #'async-shell-command - "c" #'comint-run - "t" #'term) - -(defvar-keymap embark-become-match-map - :doc "Embark become keymap for search." - :parent embark-meta-map - "o" #'occur - "k" #'keep-lines - "f" #'flush-lines - "c" #'count-matches) - -(provide 'embark) - -;; Check that embark-consult is installed. If Embark is used in -;; combination with Consult, you should install the integration package, -;; such that features like embark-export from consult-grep work as -;; expected. - -(with-eval-after-load 'consult - (unless (require 'embark-consult nil 'noerror) - (warn "The package embark-consult should be installed if you use both Embark and Consult"))) - -(with-eval-after-load 'org - (require 'embark-org)) - -;;; embark.el ends here blob - d065002fe448518c34b6dd1d7d20f4ac1fb9255a (mode 644) blob + /dev/null --- elpa/embark-1.1/embark.info +++ /dev/null @@ -1,1527 +0,0 @@ -This is docytLD1w.info, produced by makeinfo version 6.8 from -embark.texi. - -INFO-DIR-SECTION Emacs misc features -START-INFO-DIR-ENTRY -* Embark: (embark). Emacs Mini-Buffer Actions Rooted in Keymaps. -END-INFO-DIR-ENTRY - - -File: docytLD1w.info, Node: Top, Next: Overview, Up: (dir) - -Embark: Emacs Mini-Buffer Actions Rooted in Keymaps -*************************************************** - -* Menu: - -* Overview:: -* Quick start:: -* Advanced configuration:: -* How does Embark call the actions?:: -* Embark, Marginalia and Consult: Embark Marginalia and Consult. -* Related Packages:: -* Resources:: -* Contributions:: -* Acknowledgments:: - -— The Detailed Node Listing — - -Overview - -* Acting on targets:: -* The default action on a target:: -* Working with sets of possible targets:: -* Switching to a different command without losing what you've typed:: - -Working with sets of possible targets - -* Selecting some targets to make an ad hoc candidate set:: -* embark-live a live-updating variant of embark-collect:: - -Advanced configuration - -* Showing information about available targets and actions:: -* Selecting commands via completions instead of key bindings:: -* Quitting the minibuffer after an action:: -* Running some setup after injecting the target:: -* Running hooks before, after or around an action: Running hooks before after or around an action. -* Creating your own keymaps:: -* Defining actions for new categories of targets:: - -Selecting commands via completions instead of key bindings - -* Selecting commands via completion outside of Embark:: - -Defining actions for new categories of targets - -* New minibuffer target example - tab-bar tabs:: -* New target example in regular buffers - short Wikipedia links:: - -How does Embark call the actions? - -* Non-interactive functions as actions:: - -Embark, Marginalia and Consult - -* Marginalia:: -* Consult:: - - - -File: docytLD1w.info, Node: Overview, Next: Quick start, Prev: Top, Up: Top - -1 Overview -********** - -Embark makes it easy to choose a command to run based on what is near -point, both during a minibuffer completion session (in a way familiar to -Helm or Counsel users) and in normal buffers. Bind the command -‘embark-act’ to a key and it acts like prefix-key for a keymap of -_actions_ (commands) relevant to the _target_ around point. With point -on an URL in a buffer you can open the URL in a browser or eww or -download the file it points to. If while switching buffers you spot an -old one, you can kill it right there and continue to select another. -Embark comes preconfigured with over a hundred actions for common types -of targets such as files, buffers, identifiers, s-expressions, -sentences; and it is easy to add more actions and more target types. -Embark can also collect all the candidates in a minibuffer to an -occur-like buffer or export them to a buffer in a major-mode specific to -the type of candidates, such as dired for a set of files, ibuffer for a -set of buffers, or customize for a set of variables. - -* Menu: - -* Acting on targets:: -* The default action on a target:: -* Working with sets of possible targets:: -* Switching to a different command without losing what you've typed:: - - -File: docytLD1w.info, Node: Acting on targets, Next: The default action on a target, Up: Overview - -1.1 Acting on targets -===================== - -You can think of ‘embark-act’ as a keyboard-based version of a -right-click contextual menu. The ‘embark-act’ command (which you should -bind to a convenient key), acts as a prefix for a keymap offering you -relevant _actions_ to use on a _target_ determined by the context: - - • In the minibuffer, the target is the current top completion - candidate. - • In the ‘*Completions*’ buffer the target is the completion at - point. - • In a regular buffer, the target is the region if active, or else - the file, symbol, URL, s-expression or defun at point. - - Multiple targets can be present at the same location and you can -cycle between them by repeating the ‘embark-act’ key binding. The type -of actions offered depend on the type of the target. Here is a sample -of a few of the actions offered in the default configuration: - - • For files you get offered actions like deleting, copying, renaming, - visiting in another window, running a shell command on the file, - etc. - • For buffers the actions include switching to or killing the buffer. - • For package names the actions include installing, removing or - visiting the homepage. - • For Emacs Lisp symbols the actions include finding the definition, - looking up documentation, evaluating (which for a variable - immediately shows the value, but for a function lets you pass it - some arguments first). There are some actions specific to - variables, such as setting the value directly or though the - customize system, and some actions specific to commands, such as - binding it to a key. - - By default when you use ‘embark-act’ if you don’t immediately select -an action, after a short delay Embark will pop up a buffer showing a -list of actions and their corresponding key bindings. If you are using -‘embark-act’ outside the minibuffer, Embark will also highlight the -current target. These behaviors are configurable via the variable -‘embark-indicators’. Instead of selecting an action via its key -binding, you can select it by name with completion by typing ‘C-h’ after -‘embark-act’. - - Everything is easily configurable: determining the current target, -classifying it, and deciding which actions are offered for each type in -the classification. The above introduction just mentions part of the -default configuration. - - Configuring which actions are offered for a type is particularly easy -and requires no programming: the variable ‘embark-keymap-alist’ -associates target types with variables containing keymaps, and those -keymaps containing bindings for the actions. (To examine the available -categories and their associated keymaps, you can use ‘C-h v -embark-keymap-alist’ or customize that variable.) For example, in the -default configuration the type ‘file’ is associated with the symbol -‘embark-file-map’. That symbol names a keymap with single-letter key -bindings for common Emacs file commands, for instance ‘c’ is bound to -‘copy-file’. This means that if you are in the minibuffer after running -a command that prompts for a file, such as ‘find-file’ or ‘rename-file’, -you can copy a file by running ‘embark-act’ and then pressing ‘c’. - - These action keymaps are very convenient but not strictly necessary -when using ‘embark-act’: you can use any command that reads from the -minibuffer as an action and the target of the action will be inserted at -the first minibuffer prompt. After running ‘embark-act’ all of your key -bindings and even ‘execute-extended-command’ can be used to run a -command. For example, if you want to replace all occurrences of the -symbol at point, just use ‘M-%’ as the action, there is no need to bind -‘query-replace’ in one of Embark’s keymaps. Also, those action keymaps -are normal Emacs keymaps and you should feel free to bind in them -whatever commands you find useful as actions and want to be available -through convenient bindings. - - The actions in ‘embark-general-map’ are available no matter what type -of completion you are in the middle of. By default this includes -bindings to save the current candidate in the kill ring and to insert -the current candidate in the previously selected buffer (the buffer that -was current when you executed a command that opened up the minibuffer). - - Emacs’s minibuffer completion system includes metadata indicating the -_category_ of what is being completed. For example, ‘find-file’’s -metadata indicates a category of ‘file’ and ‘switch-to-buffer’’s -metadata indicates a category of ‘buffer’. Embark has the related -notion of the _type_ of a target for actions, and by default when -category metadata is present it is taken to be the type of minibuffer -completion candidates when used as targets. Emacs commands often do not -set useful category metadata so the Marginalia -(https://github.com/minad/marginalia) package, which supplies this -missing metadata, is highly recommended for use with Embark. - - Embark’s default configuration has actions for the following target -types: files, buffers, symbols, packages, URLs, bookmarks, and as a -somewhat special case, actions for when the region is active. You can -read about the default actions and their key bindings -(https://github.com/oantolin/embark/wiki/Default-Actions) on the GitHub -project wiki. - - -File: docytLD1w.info, Node: The default action on a target, Next: Working with sets of possible targets, Prev: Acting on targets, Up: Overview - -1.2 The default action on a target -================================== - -Embark has a notion of default action for a target: - - • If the target is a minibuffer completion candidate, then the - default action is whatever command opened the minibuffer in the - first place. For example if you run ‘kill-buffer’, then the - default action will be to kill buffers. - • If the target comes from a regular buffer (i.e., not a minibuffer), - then the default action is whatever is bound to ‘RET’ in the keymap - of actions for that type of target. For example, in Embark’s - default configuration for a URL found at point the default action - is ‘browse-url’, because ‘RET’ is bound to ‘browse-url’ in the - ‘embark-url-map’ keymap. - - To run the default action you can press ‘RET’ after running -‘embark-act’. Note that if there are several different targets at a -given location, each has its own default action, so first cycle to the -target you want and then press ‘RET’ to run the corresponding default -action. - - There is also ‘embark-dwim’ which runs the default action for the -first target found. It’s pretty handy in non-minibuffer buffers: with -Embark’s default configuration it will: - - • Open the file at point. - • Open the URL at point in a web browser (using the ‘browse-url’ - command). - • Compose a new email to the email address at point. - • In an Emacs Lisp buffer, if point is on an opening parenthesis or - right after a closing one, it will evaluate the corresponding - expression. - • Go to the definition of an Emacs Lisp function, variable or macro - at point. - • Find the file corresponding to an Emacs Lisp library at point. - - -File: docytLD1w.info, Node: Working with sets of possible targets, Next: Switching to a different command without losing what you've typed, Prev: The default action on a target, Up: Overview - -1.3 Working with sets of possible targets -========================================= - -Besides acting individually on targets, Embark lets you work -collectively on a set of target _candidates_. For example, while you -are in the minibuffer the candidates are simply the possible completions -of your input. Embark provides three main commands to work on candidate -sets: - - • The ‘embark-act-all’ command runs the same action on each of the - current candidates. It is just like using ‘embark-act’ on each - candidate in turn. (Because you can easily act on many more - candidates than you meant to, by default Embark asks you to confirm - uses of ‘embark-act-all’; you can turn this off by setting the user - option ‘embark-confirm-act-all’ to ‘nil’.) - - • The ‘embark-collect’ command produces a buffer listing all the - current candidates, for you to peruse and run actions on at your - leisure. The candidates are displayed as a list showing additional - annotations. If any of the candidates contain newlines, then - horizontal lines are used to separate candidates. - - The Embark Collect buffer is somewhat “dired-like”: you can select - and deselect candidates through ‘embark-select’ (available as an - action in ‘embark-act’, bound to ‘SPC’; but you could also give it - a global key binding). In an Embark Collect buffer ‘embark-act’ is - bound to ‘a’ and ‘embark-act-all’ is bound to ‘A’; ‘embark-act-all’ - will act on all currently marked candidates if there any, and will - act on all candidates if none are marked. In particular, this - means that ‘a SPC’ will toggle whether the candidate at point is - selected, and ‘A SPC’ will select all candidates if none are - selected, or deselect all selected candidates if there are some. - - • The ‘embark-export’ command tries to open a buffer in an - appropriate major mode for the set of candidates. If the - candidates are files export produces a Dired buffer; if they are - buffers, you get an Ibuffer buffer; and if they are packages you - get a buffer in package menu mode. - - If you use the grepping commands from the Consult - (https://github.com/minad/consult/) package, ‘consult-grep’, - ‘consult-git-grep’ or ‘consult-ripgrep’, then you should install - the ‘embark-consult’ package, which adds support for exporting a - list of grep results to an honest grep-mode buffer, on which you - can even use wgrep (https://github.com/mhayashi1120/Emacs-wgrep) if - you wish. - - When in doubt choosing between exporting and collecting, a good rule -of thumb is to always prefer ‘embark-export’ since when an exporter to a -special major mode is available for a given type of target, it will be -more featureful than an Embark collect buffer, and if no such exporter -is configured the ‘embark-export’ command falls back to the generic -‘embark-collect’. - - These commands are always available as “actions” (although they do -not act on just the current target but on all candidates) for -‘embark-act’ and are bound to ‘A’, ‘S’ (for “snapshot”), and ‘E’, -respectively, in ‘embark-general-map’. This means that you do not have -to bind your own key bindings for these (although you can, of course!), -just a key binding for ‘embark-act’. - - In Embark Collect or Embark Export buffers that were obtained by -running ‘embark-collect’ or ‘embark-export’ from within a minibuffer -completion session, ‘g’ is bound to a command that restarts the -completion session, that is, the command that opened the minibuffer is -run again and the minibuffer contents restored. You can then interact -normally with the command, perhaps editing the minibuffer contents, and, -if you wish, you can rerun ‘embark-collect’ or ‘embark-export’ to get an -updated buffer. - -* Menu: - -* Selecting some targets to make an ad hoc candidate set:: -* embark-live a live-updating variant of embark-collect:: - - -File: docytLD1w.info, Node: Selecting some targets to make an ad hoc candidate set, Next: embark-live a live-updating variant of embark-collect, Up: Working with sets of possible targets - -1.3.1 Selecting some targets to make an ad hoc candidate set ------------------------------------------------------------- - -The commands for working with sets of candidates just described, namely -‘embark-act-all’, ‘embark-export’ and ‘embark-collect’ by default work -with all candidates defined in the current context. For example, in the -minibuffer they operate on all currently completion candidates, or in a -dired buffer they work on all marked files (or all files if none are -marked). Embark also has a notion of _selection_, where you can -accumulate an ad hoc list of targets for these commands to work on. - - The selection is controlled by using the ‘embark-select’ action, -bound to ‘SPC’ in ‘embark-general-map’ so that it is always available -(you can also give ‘embark-select’ a global key binding if you wish; -when called directly, not as an action for ‘embark-act’, it will select -the first target at point). Calling this action on a target toggles its -membership in the current buffer’s Embark selection; that is, it adds it -to selection if not selected and removes it from the selection if it was -selected. Whenever the selection for a buffer is non-empty, the -commands ‘embark-act-all’, ‘embark-export’ and ‘embark-collect’ will act -on the selection. - - To deselect all selected targets, you can use the ‘embark-select’ -action through ‘embark-act-all’, since this will run ‘embark-select’ on -each member of the current selection. Similarly if no targets are -selected and you are in a minibuffer completion session, running -‘embark-select’ from ‘embark-act-all’ will select all the current -completion candidates. - - By default, whenever some targets are selected in the current buffer, -a count of selected targets appears in the mode line. This can be -turned off or customized through the ‘embark-selection-indicator’ user -option. - - The selection functionality is supported in every buffer: - - • In the minibuffer this gives a convenient way to act on several - completion candidates that don’t follow any simple pattern: just go - through the completions selecting the ones you want, then use - ‘embark-act-all’. For example, you could attach several files at - once to an email. - • For Embark Collect buffers this functionality enables a dired-like - workflow, in which you mark various candidates and apply an action - to all at once. (It supersedes a previous ad hoc dired-like - interface that was implemented only in Embark Collect buffers, with - a slightly different interface.) - • In a eww buffer you could use this to select various links you wish - to follow up on, and then collect them into a buffer. Similarly, - while reading Emacs’s info manual you could select some symbols you - want to read more about and export them to an ‘apropos-mode’ - buffer. - • You can use selections in regular text or programming buffers to do - complex editing operations. For example, if you have three - paragraphs scattered over a file and you want to bring them - together, you can select each one, insert them all somewhere and - finally delete all of them (from their original locations). - - -File: docytLD1w.info, Node: embark-live a live-updating variant of embark-collect, Prev: Selecting some targets to make an ad hoc candidate set, Up: Working with sets of possible targets - -1.3.2 ‘embark-live’ a live-updating variant of ‘embark-collect’ ---------------------------------------------------------------- - -Finally, there is also an ‘embark-live’ variant of the ‘embark-collect’ -command which automatically updates the collection after each change in -the source buffer. Users of a completion UI that automatically updates -and displays the candidate list (such as Vertico, Icomplete, Fido-mode, -or MCT) will probably not want to use ‘embark-live’ from the minibuffer -as they will then have two live updating displays of the completion -candidates! - - A more likely use of ‘embark-live’ is to be called from a regular -buffer to display a sort of live updating “table of contents” for the -buffer. This depends on having appropriate candidate collectors -configured in ‘embark-candidate-collectors’. There are not many in -Embark’s default configuration, but you can try this experiment: open a -dired buffer in a directory that has very many files, mark a few, and -run ‘embark-live’. You’ll get an Embark Collect buffer containing only -the marked files, which updates as you mark or unmark files in dired. -To make ‘embark-live’ genuinely useful other candidate collectors are -required. The ‘embark-consult’ package (documented near the end of this -manual) contains a few: one for imenu items and one for outline headings -as used by ‘outline-minor-mode’. Those collectors really do give -‘embark-live’ a table-of-contents feel. - - -File: docytLD1w.info, Node: Switching to a different command without losing what you've typed, Prev: Working with sets of possible targets, Up: Overview - -1.4 Switching to a different command without losing what you’ve typed -===================================================================== - -Embark also has the ‘embark-become’ command which is useful for when you -run a command, start typing at the minibuffer and realize you meant a -different command. The most common case for me is that I run -‘switch-to-buffer’, start typing a buffer name and realize I haven’t -opened the file I had in mind yet! I’ll use this situation as a running -example to illustrate ‘embark-become’. When this happens I can, of -course, press ‘C-g’ and then run ‘find-file’ and open the file, but this -requires retyping the portion of the file name you already typed. This -process can be streamlined with ‘embark-become’: while still in the -‘switch-to-buffer’ you can run ‘embark-become’ and effectively make the -‘switch-to-buffer’ command become ‘find-file’ for this run. - - You can bind ‘embark-become’ to a key in ‘minibuffer-local-map’, but -it is also available as an action under the letter ‘B’ (uppercase), so -you don’t need a binding if you already have one for ‘embark-act’. So, -assuming I have ‘embark-act’ bound to, say, ‘C-.’, once I realize I -haven’t open the file I can type ‘C-. B C-x C-f’ to have -‘switch-to-buffer’ become ‘find-file’ without losing what I have already -typed in the minibuffer. - - But for even more convenience, ‘embark-become’ offers shorter key -bindings for commands you are likely to want the current command to -become. When you use ‘embark-become’ it looks for the current command -in all keymaps named in the list ‘embark-become-keymaps’ and then -activates all keymaps that contain it. For example, the default value -of ‘embark-become-keymaps’ contains a keymap -‘embark-become-file+buffer-map’ with bindings for several commands -related to files and buffers, in particular, it binds ‘switch-to-buffer’ -to ‘b’ and ‘find-file’ to ‘f’. So when I accidentally try to switch to -a buffer for a file I haven’t opened yet, ‘embark-become’ finds that the -command I ran, ‘switch-to-buffer’, is in the keymap -‘embark-become-file+buffer-map’, so it activates that keymap (and any -others that also contain a binding for ‘switch-to-buffer’). The end -result is that I can type ‘C-. B f’ to switch to ‘find-file’. - - -File: docytLD1w.info, Node: Quick start, Next: Advanced configuration, Prev: Overview, Up: Top - -2 Quick start -************* - -The easiest way to install Embark is from GNU ELPA, just run ‘M-x -package-install RET embark RET’. (It is also available on MELPA.) It -is highly recommended to also install Marginalia -(https://github.com/minad/marginalia) (also available on GNU ELPA), so -that Embark can offer you preconfigured actions in more contexts. For -‘use-package’ users, the following is a very reasonable starting -configuration: - - (use-package marginalia - :ensure t - :config - (marginalia-mode)) - - (use-package embark - :ensure t - - :bind - (("C-." . embark-act) ;; pick some comfortable binding - ("C-;" . embark-dwim) ;; good alternative: M-. - ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' - - :init - - ;; Optionally replace the key help with a completing-read interface - (setq prefix-help-command #'embark-prefix-help-command) - - ;; Show the Embark target at point via Eldoc. You may adjust the - ;; Eldoc strategy, if you want to see the documentation from - ;; multiple providers. Beware that using this can be a little - ;; jarring since the message shown in the minibuffer can be more - ;; than one line, causing the modeline to move up and down: - - ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) - ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) - - :config - - ;; Hide the mode line of the Embark live/completions buffers - (add-to-list 'display-buffer-alist - '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" - nil - (window-parameters (mode-line-format . none))))) - - ;; Consult users will also want the embark-consult package. - (use-package embark-consult - :ensure t ; only need to install it, embark loads it after consult if found - :hook - (embark-collect-mode . consult-preview-at-point-mode)) - - About the suggested key bindings for ‘embark-act’ and ‘embark-dwim’: - • Those key bindings are unlikely to work in the terminal, but - terminal users are probably well aware of this and will know to - select different bindings. - • The suggested ‘C-.’ binding is used by default in (at least some - installations of) GNOME to input emojis, and Emacs doesn’t even get - a chance to respond to the binding. You can select a different key - binding for ‘embark-act’ or use ‘ibus-setup’ to change the shortcut - for emoji insertion (Emacs 29 will likely use ‘C-x 8 e e’, in case - you want to set the same one system-wide). - • The suggested alternative of ‘M-.’ for ‘embark-dwim’ is bound by - default to ‘xref-find-definitions’. That is a very useful command - but overwriting it with ‘embark-dwim’ is sensible since in Embark’s - default configuration, ‘embark-dwim’ will also find the definition - of the identifier at point. (Note that ‘xref-find-definitions’ - with a prefix argument prompts you for an identifier, ‘embark-dwim’ - does not cover this case). - - Other Embark commands such as ‘embark-act-all’, ‘embark-become’, -‘embark-collect’, and ‘embark-export’ can be run through ‘embark-act’ as -actions bound to ‘A’, ‘B’, ‘S’ (for “snapshot”), and ‘E’ respectively, -and thus don’t really need a dedicated key binding, but feel free to -bind them directly if you so wish. If you do choose to bind them -directly, you’ll probably want to bind them in ‘minibuffer-local-map’, -since they are most useful in the minibuffer (in fact, ‘embark-become’ -only works in the minibuffer). - - The command ‘embark-dwim’ executes the default action at point. -Another good keybinding for ‘embark-dwim’ is ‘M-.’ since ‘embark-dwim’ -acts like ‘xref-find-definitions’ on the symbol at point. ‘C-.’ can be -seen as a right-click context menu at point and ‘M-.’ acts like -left-click. The keybindings are mnemonic, both act at the point (‘.’). - - Embark needs to know what your minibuffer completion system considers -to be the list of candidates and which one is the current candidate. -Embark works out of the box if you use Emacs’s default tab completion, -the built-in ‘icomplete-mode’ or ‘fido-mode’, or the third-party -packages Vertico (https://github.com/minad/vertico) or Ivy -(https://github.com/abo-abo/swiper). - - If you are a Helm (https://emacs-helm.github.io/helm/) or Ivy -(https://github.com/abo-abo/swiper) user you are unlikely to want Embark -since those packages include comprehensive functionality for acting on -minibuffer completion candidates. (Embark does come with Ivy -integration despite this.) - - -File: docytLD1w.info, Node: Advanced configuration, Next: How does Embark call the actions?, Prev: Quick start, Up: Top - -3 Advanced configuration -************************ - -* Menu: - -* Showing information about available targets and actions:: -* Selecting commands via completions instead of key bindings:: -* Quitting the minibuffer after an action:: -* Running some setup after injecting the target:: -* Running hooks before, after or around an action: Running hooks before after or around an action. -* Creating your own keymaps:: -* Defining actions for new categories of targets:: - - -File: docytLD1w.info, Node: Showing information about available targets and actions, Next: Selecting commands via completions instead of key bindings, Up: Advanced configuration - -3.1 Showing information about available targets and actions -=========================================================== - -By default, if you run ‘embark-act’ and do not immediately select an -action, after a short delay Embark will pop up a buffer called ‘*Embark -Actions*’ containing a list of available actions with their key -bindings. You can scroll that buffer with the mouse of with the usual -commands ‘scroll-other-window’ and ‘scroll-other-window-down’ (bound by -default to ‘C-M-v’ and ‘C-M-S-v’). - - That functionality is provided by the ‘embark-mixed-indicator’, but -Embark has other indicators that can provide information about the -target and its type, what other targets you can cycle to, and which -actions have key bindings in the action map for the current type of -target. Any number of indicators can be active at once and the user -option ‘embark-indicators’ should be set to a list of the desired -indicators. - - Embark comes with the following indicators: - - • ‘embark-minimal-indicator’: shows a messages in the echo area or - minibuffer prompt showing the current target and the types of all - targets starting with the current one. - - • ‘embark-highlight-indicator’: highlights the target at point; on by - default. - - • ‘embark-verbose-indicator’: displays a table of actions and their - key bindings in a buffer; this is not on by default, in favor of - the mixed indicator described next. - - • ‘embark-mixed-indicator’: starts out by behaving as the minimal - indicator but after a short delay acts as the verbose indicator; - this is on by default. - - • ‘embark-isearch-highlight-indicator’: this only does something when - the current target is the symbol at point, in which case it lazily - highlights all occurrences of that symbol in the current buffer, - like isearch; also on by default. - - Users of the popular which-key -(https://github.com/justbur/emacs-which-key) package may prefer to use -the ‘embark-which-key-indicator’ from the Embark wiki -(https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt). -Just copy its definition from the wiki into your configuration and -customize the ‘embark-indicators’ user option to exclude the mixed and -verbose indicators and to include ‘embark-which-key-indicator’. - - If you use Vertico (https://github.com/minad/vertico), there is an -even easier way to get a ‘which-key’-like display that also lets you use -completion to narrow down the list of alternatives, described at the end -of the next section. - - -File: docytLD1w.info, Node: Selecting commands via completions instead of key bindings, Next: Quitting the minibuffer after an action, Prev: Showing information about available targets and actions, Up: Advanced configuration - -3.2 Selecting commands via completions instead of key bindings -============================================================== - -As an alternative to reading the list of actions in the verbose or mixed -indicators (see the previous section for a description of these), you -can press the ‘embark-help-key’, which is ‘C-h’ by default (but you may -prefer ‘?’ to free up ‘C-h’ for use as a prefix) after running -‘embark-act’. Pressing the help key will prompt you for the name of an -action with completion (but feel free to enter a command that is not -among the offered candidates!), and will also remind you of the key -bindings. You can press ‘embark-keymap-prompter-key’, which is ‘@’ by -default, at the prompt and then one of the key bindings to enter the -name of the corresponding action. - - You may think that with the ‘*Embark Actions*’ buffer popping up to -remind you of the key bindings you’d never want to use completion to -select an action by name, but personally I find that typing a small -portion of the action name to narrow down the list of candidates feels -significantly faster than visually scanning the entire list of actions. - - If you find you prefer selecting actions that way, you can configure -embark to always prompt you for actions by setting the variable -‘embark-prompter’ to ‘embark-completing-read-prompter’. - - On the other hand, you may wish to continue using key bindings for -the actions you perform most often, and to use completion only to -explore what further actions are available or when you’ve forgotten a -key binding. In that case, you may prefer to use the minimal indicator, -which does not pop-up an ‘*Embark Actions*’ buffer at all, and to use -the ‘embark-help-key’ whenever you need help. This unobtrusive setup is -achieved with the following configuration: - - (setq embark-indicators - '(embark-minimal-indicator ; default is embark-mixed-indicator - embark-highlight-indicator - embark-isearch-highlight-indicator)) - - Vertico (https://github.com/minad/vertico) users may wish to -configure a grid display for the actions and key-bindings, reminiscent -of the popular package which-key -(https://github.com/justbur/emacs-which-key), but, of course, enhanced -by the use of completion to narrow the list of commands. In order to -get the grid display, put the following in your Vertico configuration: - - (add-to-list 'vertico-multiform-categories '(embark-keybinding grid)) - (vertico-multiform-mode) - - This will make the available keys be shown in a compact grid like in -‘which-key’. The ‘vertico-multiform-mode’ also enables keys such as -‘M-V’, ‘M-G’, ‘M-B’, and ‘M-U’ for manually switching between layouts in -Vertico buffers. - -* Menu: - -* Selecting commands via completion outside of Embark:: - - -File: docytLD1w.info, Node: Selecting commands via completion outside of Embark, Up: Selecting commands via completions instead of key bindings - -3.2.1 Selecting commands via completion outside of Embark ---------------------------------------------------------- - -If you like this completion interface for exploring key bindings for -Embark actions, you may want to use it elsewhere in Emacs. You can use -Embark’s completion-based command prompter to list: - - • key bindings under a prefix, - • local key bindings, or - • all key bindings. - - To use it for key bindings under a prefix (you can use this to -replace the ‘which-key’ package, for example), use this configuration: - - (setq prefix-help-command #'embark-prefix-help-command) - - Now, when you have started on a prefix sequence such as ‘C-x’ or -‘C-c’, pressing ‘C-h’ will bring up the Embark version of the built-in -‘prefix-help-command’, which will list the keys under that prefix and -their bindings, and lets you select the one you wanted with completion, -or by key binding if you press ‘embark-keymap-prompter-key’. - - To list local or global key bindings, use the command -‘embark-bindings’. You can bind that to ‘C-h b’, which is the default -key binding for the built-in ‘describe-bindings’ command, which this -command can replace. By default, ‘embark-bindings’ lists local key -bindings, typically those bound in the major mode keymap; to get global -bindings as well, call it with a ‘C-u’ prefix argument. - - -File: docytLD1w.info, Node: Quitting the minibuffer after an action, Next: Running some setup after injecting the target, Prev: Selecting commands via completions instead of key bindings, Up: Advanced configuration - -3.3 Quitting the minibuffer after an action -=========================================== - -By default, if you call ‘embark-act’ from the minibuffer it quits the -minibuffer after performing the action. You can change this by setting -the user option ‘embark-quit-after-action’ to ‘nil’. Having -‘embark-act’ _not_ quit the minibuffer can be useful to turn commands -into little “thing managers”. For example, you can use ‘find-file’ as a -little file manager or ‘describe-package’ as a little package manager: -you can run those commands, perform a series of actions, and then quit -the command. - - If you want to control the quitting behavior in a fine-grained manner -depending on the action, you can set ‘embark-quit-after-action’ to an -alist, associating commands to either ‘t’ for quitting or ‘nil’ for not -quitting. When using an alist, you can use the special key ‘t’ to -specify the default behavior. For example, to specify that by default -actions should not quit the minibuffer but that using ‘kill-buffer’ as -an action should quit, you can use the following configuration: - - (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) - - The variable ‘embark-quit-after-action’ only specifies a default, -that is, it only controls whether or not ‘embark-act’ quits the -minibuffer when you call it without a prefix argument, and you can -select the opposite behavior to what the variable says by calling -‘embark-act’ with ‘C-u’. Also note that both the variable -‘embark-quit-after-action’ and ‘C-u’ have no effect when you call -‘embark-act’ outside the minibuffer. - - If you find yourself using the quitting and non-quitting variants of -‘embark-act’ about equally often, independently of the action, you may -prefer to simply have separate commands for them instead of a single -command that you call with ‘C-u’ half the time. You could, for example, -keep the default exiting behavior of ‘embark-act’ and define a -non-quitting version as follows: - - (defun embark-act-noquit () - "Run action but don't quit the minibuffer afterwards." - (interactive) - (let ((embark-quit-after-action nil)) - (embark-act))) - - -File: docytLD1w.info, Node: Running some setup after injecting the target, Next: Running hooks before after or around an action, Prev: Quitting the minibuffer after an action, Up: Advanced configuration - -3.4 Running some setup after injecting the target -================================================= - -You can customize what happens after the target is inserted at the -minibuffer prompt of an action. There are -‘embark-target-injection-hooks’, that are run by default after injecting -the target into the minibuffer. The variable -‘embark-target-injection-hooks’ is an alist associating commands to -their setup hooks. There are two special keys: if no setup hook is -specified for a given action, the hook associated to ‘t’ is run; and the -hook associated to ‘:always’ is run regardless of the action. (This -variable used to have the less explicit name of -‘embark-setup-action-hooks’, so please update your configuration.) - - For example, consider using ‘shell-command’ as an action during file -completion. It would be useful to insert a space before the target file -name and to leave the point at the beginning, so you can immediately -type the shell command to run on that file. That’s why in Embark’s -default configuration there is an entry in -‘embark-target-injection-hooks’ associating ‘shell-command’ to a hook -that includes ‘embark--shell-prep’, a simple helper function that quotes -all the spaces in the file name, inserts an extra space at the beginning -of the line and leaves point to the left of it. - - Now, the preparation that ‘embark--shell-prep’ does would be useless -if Embark did what it normally does after it inserts the target of the -action at the minibuffer prompt, which is to “press ‘RET’” for you, -accepting the target as is; if Embark did that for ‘shell-command’ you -wouldn’t get a chance to type in the command to execute! That is why in -Embark’s default configuration the entry for ‘shell-command’ in -‘embark-target-injection-hooks’ also contains the function -‘embark--allow-edit’. - - Embark used to have a dedicated variable ‘embark-allow-edit-actions’ -to which you could add commands for which Embark should forgo pressing -‘RET’ for you after inserting the target. Since its effect can also be -achieved via the general ‘embark-target-injection-hooks’ mechanism, that -variable has been removed to simplify Embark. Be sure to update your -configuration; if you had something like: - - (add-to-list 'embark-allow-edit-actions 'my-command) - - you should replace it with: - - (push 'embark--allow-edit - (alist-get 'my-command embark-target-injection-hooks)) - - Also note that while you could abuse ‘embark--allow-edit’ so that you -have to confirm “dangerous” actions such as ‘delete-file’, it is better -to implement confirmation by adding the ‘embark--confirm’ function to -the appropriate entry of a different hook alist, namely, -‘embark-pre-action-hooks’. - - Besides ‘embark--allow-edit’, Embark comes with another function that -is of general utility in action setup hooks: ‘embark--ignore-target’. -Use it for commands that do prompt you in the minibuffer but for which -inserting the target would be inappropriate. This is not a common -situation but does occasionally arise. For example it is used by -default for ‘shell-command-on-region’: that command is used as an action -for region targets, and it prompts you for a shell command; you -typically do _not_ want the target, that is the contents of the region, -to be entered at that prompt! - - -File: docytLD1w.info, Node: Running hooks before after or around an action, Next: Creating your own keymaps, Prev: Running some setup after injecting the target, Up: Advanced configuration - -3.5 Running hooks before, after or around an action -=================================================== - -Embark has three variables, ‘embark-pre-action-hooks’, -‘embark-post-action-hooks’ and ‘embark-around-action-hooks’, which are -alists associating commands to hooks that should run before or after or -as around advice for the command when used as an action. As with -‘embark-target-injection-hooks’, there are two special keys for the -alists: ‘t’ designates the default hook to run when no specific hook is -specified for a command; and the hook associated to ‘:always’ runs -regardless. - - The default values of those variables are fairly extensive, adding -creature comforts to make running actions a smooth experience. Embark -comes with several functions intended to be added to these hooks, and -used in the default values of ‘embark-pre-action-hooks’, -‘embark-post-action-hooks’ and ‘embark-around-action-hooks’. - - For pre-action hooks: - -‘embark--confirm’ - Prompt the user for confirmation before executing the action. This - is used be default for commands deemed “dangerous”, or, more - accurately, hard to undo, such as ‘delete-file’ and ‘kill-buffer’. - -‘embark--unmark-target’ - Unmark the active region. Use this for commands you want to act on - the region contents but without the region being active. The - default configuration uses this function as a pre-action hook for - ‘occur’ and ‘query-replace’, for example, so that you can use them - as actions with region targets to search the whole buffer for the - text contained in the region. Without this pre-action hook using - ‘occur’ as an action for a region target would be pointless: it - would search for the the region contents _in the region_, - (typically, due to the details of regexps) finding only one match! - -‘embark--beginning-of-target’ - Move to the beginning of the target (for targets that report - bounds). This is used by default for backward motion commands such - as ‘backward-sexp’, so that they don’t accidentally leave you on - the current target. - -‘embark--end-of-target’ - Move to the end of the target. This is used similarly to the - previous function, but also for commands that act on the last - s-expression like ‘eval-last-sexp’. This allow you to act on an - s-expression from anywhere inside it and still use ‘eval-last-sexp’ - as an action. - -‘embark--xref-push-markers’ - Push the current location on the xref marker stack. Use this for - commands that take you somewhere and for which you’d like to be - able to come back to where you were using ‘xref-pop-marker-stack’. - This is used by default for ‘find-library’. - - For post-action hooks: - -‘embark--restart’ - Restart the command currently prompting in the minibuffer, so that - the list of completion candidates is updated. This is useful as a - post action hook for commands that delete or rename a completion - candidate; for example the default value of - ‘embark-post-action-hooks’ uses it for ‘delete-file’, - ‘kill-buffer’, ‘rename-file’, ‘rename-buffer’, etc. - - For around-action hooks: - -‘embark--mark-target’ - Save existing mark and point location, mark the target and run the - action. Most targets at point outside the minibuffer report which - region of the buffer they correspond to (this is the information - used by ‘embark-highlight-indicator’ to know what portion of the - buffer to highlight); this function marks that region. It is - useful as an around action hook for commands that expect a region - to be marked, for example, it is used by default for - ‘indent-region’ so that it works on s-expression targets, or for - ‘fill-region’ so that it works on paragraph targets. - -‘embark--cd’ - Run the action with ‘default-directory’ set to the directory - associated to the current target. The target should be of type - ‘file’, ‘buffer’, ‘bookmark’ or ‘library’, and the associated - directory is what you’d expect in each case. - -‘embark--narrow-to-target’ - Run the action with buffer narrowed to current target. Use this as - an around hook to localize the effect of actions that don’t already - work on just the region. In the default configuration it is used - for ‘repunctuate-sentences’. - -‘embark--save-excursion’ - Run the action restoring point at the end. The current default - configuration doesn’t use this but it is available for users. - - -File: docytLD1w.info, Node: Creating your own keymaps, Next: Defining actions for new categories of targets, Prev: Running hooks before after or around an action, Up: Advanced configuration - -3.6 Creating your own keymaps -============================= - -All internal keymaps are defined with the standard helper macro -‘defvar-keymap’. For example a simple version of the file action keymap -could be defined as follows: - - (defvar-keymap embark-file-map - :doc "Example keymap with a few file actions" - :parent embark-general-map - "d" #'delete-file - "r" #'rename-file - "c" #'copy-file) - - These action keymaps are perfectly normal Emacs keymaps. You may -want to inherit from the ‘embark-general-map’ if you want to access the -default Embark actions. Note that ‘embark-collect’ and ‘embark-export’ -are also made available via ‘embark-general-map’. - - -File: docytLD1w.info, Node: Defining actions for new categories of targets, Prev: Creating your own keymaps, Up: Advanced configuration - -3.7 Defining actions for new categories of targets -================================================== - -It is easy to configure Embark to provide actions for new types of -targets, either in the minibuffer or outside it. I present below two -very detailed examples of how to do this. At several points I’ll -explain more than one way to proceed, typically with the easiest option -first. I include the alternative options since there will be similar -situations where the easiest option is not available. - -* Menu: - -* New minibuffer target example - tab-bar tabs:: -* New target example in regular buffers - short Wikipedia links:: - - -File: docytLD1w.info, Node: New minibuffer target example - tab-bar tabs, Next: New target example in regular buffers - short Wikipedia links, Up: Defining actions for new categories of targets - -3.7.1 New minibuffer target example - tab-bar tabs --------------------------------------------------- - -As an example, take the new tab bars -(https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html) -from Emacs 27. I’ll explain how to configure Embark to offer -tab-specific actions when you use the tab-bar-mode commands that mention -tabs by name. The configuration explained here is now built-in to -Embark (and Marginalia), but it’s still a good self-contained example. -In order to setup up tab actions you would need to: (1) make sure Embark -knows those commands deal with tabs, (2) define a keymap for tab actions -and configure Embark so it knows that’s the keymap you want. - - 1. Telling Embark about commands that prompt for tabs by name - - For step (1), it would be great if the ‘tab-bar-mode’ commands - reported the completion category ‘tab’ when asking you for a tab - with completion. (All built-in Emacs commands that prompt for file - names, for example, do have metadata indicating that they want a - ‘file’.) They do not, unfortunately, and I will describe a couple - of ways to deal with this. - - Maybe the easiest thing is to configure Marginalia - (https://github.com/minad/marginalia) to enhance those commands. - All of the ‘tab-bar-*-tab-by-name’ commands have the words “tab by - name” in the minibuffer prompt, so you can use: - - (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) - - That’s it! But in case you are ever in a situation where you don’t - already have commands that prompt for the targets you want, I’ll - describe how writing your own command with appropriate ‘category’ - metadata looks: - - (defun my-select-tab-by-name (tab) - (interactive - (list - (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - (tab-bar-tabs)) - (user-error "No tabs found")))) - (completing-read - "Tabs: " - (lambda (string predicate action) - (if (eq action 'metadata) - '(metadata (category . tab)) - (complete-with-action - action tab-list string predicate))))))) - (tab-bar-select-tab-by-name tab)) - - As you can see, the built-in support for setting the category - meta-datum is not very easy to use or pretty to look at. To help - with this I recommend the ‘consult--read’ function from the - excellent Consult (https://github.com/minad/consult/) package. - With that function we can rewrite the command as follows: - - (defun my-select-tab-by-name (tab) - (interactive - (list - (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - (tab-bar-tabs)) - (user-error "No tabs found")))) - (consult--read tab-list - :prompt "Tabs: " - :category 'tab)))) - (tab-bar-select-tab-by-name tab)) - - Much nicer! No matter how you define the ‘my-select-tab-by-name’ - command, the first approach with Marginalia and prompt detection - has the following advantages: you get the ‘tab’ category for all - the ‘tab-bar-*-bar-by-name’ commands at once, also, you enhance - built-in commands, instead of defining new ones. - - 2. Defining and configuring a keymap for tab actions - - Let’s say we want to offer select, rename and close actions for - tabs (in addition to Embark general actions, such as saving the tab - name to the kill-ring, which you get for free). Then this will do: - - (defvar-keymap embark-tab-actions - :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." - :parent embark-general-map - "s" #'tab-bar-select-tab-by-name - "r" #'tab-bar-rename-tab-by-name - "k" #'tab-bar-close-tab-by-name) - - (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) - - What if after using this for a while you feel closing the tab - without confirmation is dangerous? You have a couple of options: - - 1. You can keep using the ‘tab-bar-close-tab-by-name’ command, - but have Embark ask you for confirmation: - (push #'embark--confirm - (alist-get 'tab-bar-close-tab-by-name - embark-pre-action-hooks)) - - 2. You can write your own command that prompts for confirmation - and use that instead of ‘tab-bar-close-tab-by-name’ in the - above keymap: - (defun my-confirm-close-tab-by-name (tab) - (interactive "sTab to close: ") - (when (y-or-n-p (format "Close tab '%s'? " tab)) - (tab-bar-close-tab-by-name tab))) - - Notice that this is a command you can also use directly from - ‘M-x’ independently of Embark. Using it from ‘M-x’ leaves - something to be desired, though, since you don’t get - completion for the tab names. You can fix this if you wish as - described in the previous section. - - -File: docytLD1w.info, Node: New target example in regular buffers - short Wikipedia links, Prev: New minibuffer target example - tab-bar tabs, Up: Defining actions for new categories of targets - -3.7.2 New target example in regular buffers - short Wikipedia links -------------------------------------------------------------------- - -Say you want to teach Embark to treat text of the form -‘wikipedia:Garry_Kasparov’ in any regular buffer as a link to Wikipedia, -with actions to open the Wikipedia page in eww or an external browser or -to save the URL of the page in the kill-ring. We can take advantage of -the actions that Embark has preconfigured for URLs, so all we need to do -is teach Embark that ‘wikipedia:Garry_Kasparov’ stands for the URL -‘https://en.wikipedia.org/wiki/Garry_Kasparov’. - - You can be as fancy as you want with the recognized syntax. Here, to -keep the example simple, I’ll assume the link matches the regexp -‘wikipedia:[[:alnum:]_]+’. We will write a function that looks for a -match surrounding point, and returns a dotted list of the form ‘'(url -URL-OF-THE-PAGE START . END)’ where ‘START’ and ‘END’ are the buffer -positions bounding the target, and are used by Embark to highlight it if -you have ‘embark-highlight-indicator’ included in the list -‘embark-indicators’. (There are a couple of other options for the -return value of a target finder: the bounding positions are optional and -a single target finder is allowed to return multiple targets; see the -documentation for ‘embark-target-finders’ for details.) - - (defun my-short-wikipedia-link () - "Target a link at point of the form wikipedia:Page_Name." - (save-excursion - (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) - (end (progn (skip-chars-forward "[:alnum:]_:") (point))) - (str (buffer-substring-no-properties start end))) - (save-match-data - (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) - `(url - ,(format "https://en.wikipedia.org/wiki/%s" - (match-string 1 str)) - ,start . ,end)))))) - - (add-to-list 'embark-target-finders 'my-short-wikipedia-link) - - -File: docytLD1w.info, Node: How does Embark call the actions?, Next: Embark Marginalia and Consult, Prev: Advanced configuration, Up: Top - -4 How does Embark call the actions? -*********************************** - -Embark actions are normal Emacs commands, that is, functions with an -interactive specification. In order to execute an action, Embark calls -the command with ‘call-interactively’, so the command reads user input -exactly as if run directly by the user. For example the command may -open a minibuffer and read a string (‘read-from-minibuffer’) or open a -completion interface (‘completing-read’). If this happens, Embark takes -the target string and inserts it automatically into the minibuffer, -simulating user input this way. After inserting the string, Embark -exits the minibuffer, submitting the input. (The immediate minibuffer -exit can be disabled for specific actions in order to allow editing the -input; this is done by adding the ‘embark--allow-edit’ function to the -appropriate entry of ‘embark-target-injection-hooks’). Embark inserts -the target string at the first minibuffer opened by the action command, -and if the command happens to prompt the user for input more than once, -the user still interacts with the second and further prompts in the -normal fashion. Note that if a command does not prompt the user for -input in the minibuffer, Embark still allows you to use it as an action, -but of course, never inserts the target anywhere. (There are plenty of -examples in the default configuration of commands that do not prompt the -user bound to keys in the action maps, most of the region actions, for -instance.) - - This is how Embark manages to reuse normal commands as actions. The -mechanism allows you to use as Embark actions commands that were not -written with Embark in mind (and indeed almost all actions that are -bound by default in Embark’s action keymaps are standard Emacs -commands). It also allows you to write new custom actions in such a way -that they are useful even without Embark. - - Staring from version 28.1, Emacs has a variable -‘y-or-n-p-use-read-key’, which when set to ‘t’ causes ‘y-or-n-p’ to use -‘read-key’ instead of ‘read-from-minibuffer’. Setting -‘y-or-n-p-use-read-key’ to ‘t’ is recommended for Embark users because -it keeps Embark from attempting to insert the target at a ‘y-or-n-p’ -prompt, which would almost never be sensible. Also consider this as a -warning to structure your own action commands so that if they use -‘y-or-n-p’, they do so only after the prompting for the target. - - Here is a simple example illustrating the various ways of reading -input from the user mentioned above. Bind the following commands to the -‘embark-symbol-map’ to be used as actions, then put the point on some -symbol and run them with ‘embark-act’: - - (defun example-action-command1 () - (interactive) - (message "The input was `%s'." (read-from-minibuffer "Input: "))) - - (defun example-action-command2 (arg input1 input2) - (interactive "P\nsInput 1: \nsInput 2: ") - (message "The first input %swas `%s', and the second was `%s'." - (if arg "truly " "") - input1 - input2)) - - (defun example-action-command3 () - (interactive) - (message "Your selection was `%s'." - (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) - - (defun example-action-command4 () - (interactive) - (message "I don't prompt you for input and thus ignore the target!")) - - (keymap-set embark-symbol-map "X 1" #'example-action-command1) - (keymap-set embark-symbol-map "X 2" #'example-action-command2) - (keymap-set embark-symbol-map "X 3" #'example-action-command3) - (keymap-set embark-symbol-map "X 4" #'example-action-command4) - - Also note that if you are using the key bindings to call actions, you -can pass prefix arguments to actions in the normal way. For example, -you can use ‘C-u X2’ with the above demonstration actions to make the -message printed by ‘example-action-command2’ more emphatic. This -ability to pass prefix arguments to actions is useful for some actions -in the default configuration, such as ‘embark-shell-command-on-buffer’. - -* Menu: - -* Non-interactive functions as actions:: - - -File: docytLD1w.info, Node: Non-interactive functions as actions, Up: How does Embark call the actions? - -4.1 Non-interactive functions as actions -======================================== - -Alternatively, Embark does support one other type of action: a -non-interactive function of a single argument. The target is passed as -argument to the function. For example: - - (defun example-action-function (target) - (message "The target was `%s'." target)) - - (keymap-set embark-symbol-map "X 4" #'example-action-function) - - Note that normally binding non-interactive functions in a keymap is -useless, since when attempting to run them using the key binding you get -an error message similar to “Wrong type argument: commandp, -example-action-function”. In general it is more flexible to write any -new Embark actions as commands, that is, as interactive functions, -because that way you can also run them directly, without Embark. But -there are a couple of reasons to use non-interactive functions as -actions: - - 1. You may already have the function lying around, and it is - convenient to simply reuse it. - - 2. For command actions the targets can only be simple string, with no - text properties. For certain advanced uses you may want the action - to receive a string _with_ some text properties, or even a - non-string target. - - -File: docytLD1w.info, Node: Embark Marginalia and Consult, Next: Related Packages, Prev: How does Embark call the actions?, Up: Top - -5 Embark, Marginalia and Consult -******************************** - -Embark cooperates well with the Marginalia -(https://github.com/minad/marginalia) and Consult -(https://github.com/minad/consult) packages. Neither of those packages -is a dependency of Embark, but both are highly recommended companions to -Embark, for opposite reasons: Marginalia greatly enhances Embark’s -usefulness, while Embark can help enhance Consult. - - In the remainder of this section I’ll explain what exactly Marginalia -does for Embark, and what Embark can do for Consult. - -* Menu: - -* Marginalia:: -* Consult:: - - -File: docytLD1w.info, Node: Marginalia, Next: Consult, Up: Embark Marginalia and Consult - -5.1 Marginalia -============== - -Embark comes with actions for symbols (commands, functions, variables -with actions such as finding the definition, looking up the -documentation, evaluating, etc.) in the ‘embark-symbol-map’ keymap, and -for packages (actions like install, delete, browse url, etc.) in the -‘embark-package-keymap’. - - Unfortunately Embark does not automatically offers you these keymaps -when relevant, because many built-in Emacs commands don’t report -accurate category metadata. For example, a command like -‘describe-package’, which reads a package name from the minibuffer, does -not have metadata indicating this fact. - - In an earlier Embark version, there were functions to supply this -missing metadata, but they have been moved to Marginalia, which augments -many Emacs command to report accurate category metadata. Simply -activating ‘marginalia-mode’ allows Embark to offer you the package and -symbol actions when appropriate again. Candidate annotations in the -Embark collect buffer are also provided by the Marginalia package: - - • If you install Marginalia and activate ‘marginalia-mode’, Embark - Collect buffers will use the Marginalia annotations automatically. - - • If you don’t install Marginalia, you will see only the annotations - that come with Emacs (such as key bindings in ‘M-x’, or the unicode - characters in ‘C-x 8 RET’). - - -File: docytLD1w.info, Node: Consult, Prev: Marginalia, Up: Embark Marginalia and Consult - -5.2 Consult -=========== - -The excellent Consult package provides many commands that use minibuffer -completion, via the ‘completing-read’ function; plenty of its commands -can be considered enhanced versions of built-in Emacs commands, and some -are completely new functionality. One common enhancement provided in -all commands for which it makes sense is preview functionality, for -example ‘consult-buffer’ will show you a quick preview of a buffer -before you actually switch to it. - - If you use both Consult and Embark you should install the -‘embark-consult’ package which provides integration between the two. It -provides exporters for several Consult commands and also tweaks the -behavior of many Consult commands when used as actions with ‘embark-act’ -in subtle ways that you may not even notice, but make for a smoother -experience. You need only install it to get these benefits: Embark will -automatically load it after Consult if found. - - The ‘embark-consult’ package provides the following exporters: - - • You can use ‘embark-export’ from ‘consult-line’, ‘consult-outline’, - or ‘consult-mark’ to obtain an ‘occur-mode’ buffer. As with the - built-in ‘occur’ command you use that buffer to jump to a match and - after that, you can then use ‘next-error’ and ‘previous-error’ to - navigate to other matches. You can also press ‘e’ to activate - ‘occur-edit-mode’ and edit the matches in place! - - • You can export from any of the Consult asynchronous search - commands, ‘consult-grep’, ‘consult-git-grep’, or ‘consult-ripgrep’ - to get a ‘grep-mode’ buffer. Here too you can use ‘next-error’ and - ‘previous-error’ to navigate among matches, and, if you install the - wgrep - (http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el) - package, you can use it to edit the matches in place. - - In both cases, pressing ‘g’ will rerun the Consult command you had -exported from and re-enter the input you had typed (which is similar to -reverting but a little more flexible). You can then proceed to -re-export if that’s what you want, but you can also edit the input -changing the search terms or simply cancel if you see you are done with -that search. - - The ‘embark-consult’ also contains some candidates collectors that -allow you to run ‘embark-live’ to get a live-updating table of contents -for your buffer: - - • ‘embark-consult-outline-candidates’ produces the outline headings - of the current buffer, using ‘consult-outline’. - • ‘embark-consult-imenu-candidates’ produces the imenu items of the - current buffer, using ‘consult-imenu’. - • ‘embark-consult-imenu-or-outline-candidates’ is a simple - combination of the two previous functions: it produces imenu items - in buffers deriving from ‘prog-mode’ and otherwise outline - headings. - - The way to configure ‘embark-live’ (or ‘embark-collect’ and -‘embark-export’ for that matter) to use one of these function is to add -it at the end of the ‘embark-candidate-collectors’ list. The -‘embark-consult’ package by default adds the last one, which seems to be -the most sensible default. - - Besides those exporters and candidate collectors, the -‘embark-consult’ package provides many subtle tweaks and small -integrations between Embark and Consult. Some examples are: - - • When used as actions, the asynchronous search commands will search - only the files associated to the targets: if the targets _are_ - files, it searches those files; for buffers it will search either - the associated file if there is one, else all files in the buffer’s - ‘default-directory’; for bookmarks it will search the file they - point to, same for Emacs Lisp libraries. This is particularly - powerful when using ‘embark-act-all’ to act on multiple files at - once, for example you can use ‘consult-find’ to search among file - _names_ and then ‘embark-act-all’ and ‘consult-grep’ to search - within the matching files. - - • For all other target types, those that do not have a sensible - notion of associated file, a Consult search command - (asynchronous or not) will search for the text of the target - but leave the minibuffer open so you can interact with the - Consult command. - - • ‘consult-imenu’ will search for the target and take you directly to - the location if it matches a unique imenu entry, otherwise it will - leave the minibuffer open so you can navigate among the matches. - - -File: docytLD1w.info, Node: Related Packages, Next: Resources, Prev: Embark Marginalia and Consult, Up: Top - -6 Related Packages -****************** - -There are several packages that offer functionality similar to Embark’s. - -Acting on minibuffer completion candidates - The popular Ivy and Helm packages have support for acting on the - completion candidates of commands written using their APIs, and - there is an extensive ecosystem of packages meant for Helm and for - Ivy (the Ivy ones usually have “counsel” in the name) providing - commands and appropriate actions. -Acting on things at point - The built-in ‘context-menu-mode’ provides a mouse-driven - context-sensitive configurable menu. The ‘do-at-point’ package by - Philip Kaludercic (available on GNU ELPA), on the other hand is - keyboard-driven. -Collecting completion candidates into a buffer - The Ivy package has the command ‘ivy-occur’ which is similar to - ‘embark-collect’. As with Ivy actions, ‘ivy-occur’ only works for - commands written using the Ivy API. - - -File: docytLD1w.info, Node: Resources, Next: Contributions, Prev: Related Packages, Up: Top - -7 Resources -*********** - -If you want to learn more about how others have used Embark here are -some links to read: - - • Fifteen ways to use Embark - (https://karthinks.com/software/fifteen-ways-to-use-embark/), a - blog post by Karthik Chikmagalur. - • Protesilaos Stavrou’s dotemacs (https://protesilaos.com/dotemacs/), - look for the section called “Extended minibuffer actions and more - (embark.el and prot-embark.el)” - - And some videos to watch: - - • Embark and my extras - (https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/) - by Protesilaos Stavrou. - • Embark – Key features and tweaks (https://youtu.be/qpoQiiinCtY) by - Raoul Comninos on the Emacs-Elements YouTube channel. - • Livestreamed: Adding an Embark context action to send a stream - message (https://youtu.be/WsxXr1ncukY) by Sacha Chua. - • System Crafters Live! - The Many Uses of Embark - (https://youtu.be/qk2Is_sC8Lk) by David Wilson. - • Marginalia, Consult and Embark by Mike Zamansky. - - -File: docytLD1w.info, Node: Contributions, Next: Acknowledgments, Prev: Resources, Up: Top - -8 Contributions -*************** - -Contributions to Embark are very welcome. There is a wish list -(https://github.com/oantolin/embark/issues/95) for actions, target -finders, candidate collectors and exporters. For other ideas you have -for Embark, feel free to open an issue on the issue tracker -(https://github.com/oantolin/embark/issues). Any neat configuration -tricks you find might be a good fit for the wiki -(https://github.com/oantolin/embark/wiki). - - Code contributions are very welcome too, but since Embark is now on -GNU ELPA, copyright assignment to the FSF is required before you can -contribute code. - - -File: docytLD1w.info, Node: Acknowledgments, Prev: Contributions, Up: Top - -9 Acknowledgments -***************** - -While I, Omar Antolín Camarena, have written most of the Embark code and -remain very stubborn about some of the design decisions, Embark has -received substantial help from a number of other people which this -document has neglected to mention for far too long. In particular, -Daniel Mendler has been absolutely invaluable, implementing several -important features, and providing a lot of useful advice. - - Code contributions: - - • Daniel Mendler (https://github.com/minad) - • Clemens Radermacher (https://github.com/clemera/) - • José Antonio Ortega Ruiz (https://codeberg.org/jao/) - • Itai Y. Efrat (https://github.com/iyefrat) - • a13 (https://github.com/a13) - • jakanakaevangeli (https://github.com/jakanakaevangeli) - • mihakam (https://github.com/mihakam) - • Brian Leung (https://github.com/leungbk) - • Karthik Chikmagalur (https://github.com/karthink) - • Roshan Shariff (https://github.com/roshanshariff) - • condy0919 (https://github.com/condy0919) - • Damien Cassou (https://github.com/DamienCassou) - • JimDBh (https://github.com/JimDBh) - - Advice and useful discussions: - - • Daniel Mendler (https://github.com/minad) - • Protesilaos Stavrou (https://gitlab.com/protesilaos/) - • Clemens Radermacher (https://github.com/clemera/) - • Howard Melman (https://github.com/hmelman/) - • Augusto Stoffel (https://github.com/astoff) - • Bruce d’Arcus (https://github.com/bdarcus) - • JD Smith (https://github.com/jdtsmith) - • Karthik Chikmagalur (https://github.com/karthink) - • jakanakaevangeli (https://github.com/jakanakaevangeli) - • Itai Y. Efrat (https://github.com/iyefrat) - • Mohsin Kaleem (https://github.com/mohkale) - - - -Tag Table: -Node: Top223 -Node: Overview1848 -Node: Acting on targets3157 -Node: The default action on a target8702 -Node: Working with sets of possible targets10612 -Node: Selecting some targets to make an ad hoc candidate set14891 -Node: embark-live a live-updating variant of embark-collect18347 -Node: Switching to a different command without losing what you've typed20045 -Node: Quick start22622 -Node: Advanced configuration27552 -Node: Showing information about available targets and actions28137 -Node: Selecting commands via completions instead of key bindings30959 -Node: Selecting commands via completion outside of Embark34047 -Node: Quitting the minibuffer after an action35582 -Node: Running some setup after injecting the target38038 -Node: Running hooks before after or around an action41656 -Node: Creating your own keymaps46535 -Node: Defining actions for new categories of targets47442 -Node: New minibuffer target example - tab-bar tabs48214 -Ref: Telling Embark about commands that prompt for tabs by name49120 -Ref: Defining and configuring a keymap for tab actions51983 -Node: New target example in regular buffers - short Wikipedia links53774 -Node: How does Embark call the actions?56037 -Node: Non-interactive functions as actions60380 -Node: Embark Marginalia and Consult61737 -Node: Marginalia62468 -Node: Consult63975 -Node: Related Packages68737 -Node: Resources69834 -Node: Contributions70968 -Node: Acknowledgments71681 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: blob - e510368e691d46dab88442e0669de8afc9768eeb (mode 644) blob + /dev/null --- elpa/embark-1.1/embark.texi +++ /dev/null @@ -1,1563 +0,0 @@ -\input texinfo @c -*- texinfo -*- -@c %**start of header -@setfilename embark.info -@settitle Embark: Emacs Mini-Buffer Actions Rooted in Keymaps -@documentencoding UTF-8 -@documentlanguage en -@c %**end of header - -@dircategory Emacs misc features -@direntry -* Embark: (embark). Emacs Mini-Buffer Actions Rooted in Keymaps. -@end direntry - -@finalout -@titlepage -@title Embark: Emacs Mini-Buffer Actions Rooted in Keymaps -@author Omar Antolín Camarena -@end titlepage - -@contents - -@ifnottex -@node Top -@top Embark: Emacs Mini-Buffer Actions Rooted in Keymaps -@end ifnottex - -@menu -* Overview:: -* Quick start:: -* Advanced configuration:: -* How does Embark call the actions?:: -* Embark, Marginalia and Consult: Embark Marginalia and Consult. -* Related Packages:: -* Resources:: -* Contributions:: -* Acknowledgments:: - -@detailmenu ---- The Detailed Node Listing --- - -Overview - -* Acting on targets:: -* The default action on a target:: -* Working with sets of possible targets:: -* Switching to a different command without losing what you've typed:: - -Working with sets of possible targets - -* Selecting some targets to make an ad hoc candidate set:: -* @samp{embark-live} a live-updating variant of @samp{embark-collect}:: - -Advanced configuration - -* Showing information about available targets and actions:: -* Selecting commands via completions instead of key bindings:: -* Quitting the minibuffer after an action:: -* Running some setup after injecting the target:: -* Running hooks before, after or around an action: Running hooks before after or around an action. -* Creating your own keymaps:: -* Defining actions for new categories of targets:: - -Selecting commands via completions instead of key bindings - -* Selecting commands via completion outside of Embark:: - -Defining actions for new categories of targets - -* New minibuffer target example - tab-bar tabs:: -* New target example in regular buffers - short Wikipedia links:: - -How does Embark call the actions? - -* Non-interactive functions as actions:: - -Embark, Marginalia and Consult - -* Marginalia:: -* Consult:: - -@end detailmenu -@end menu - -@node Overview -@chapter Overview - -Embark makes it easy to choose a command to run based on what is near -point, both during a minibuffer completion session (in a way familiar -to Helm or Counsel users) and in normal buffers. Bind the command -@samp{embark-act} to a key and it acts like prefix-key for a keymap of -@emph{actions} (commands) relevant to the @emph{target} around point. With point on -an URL in a buffer you can open the URL in a browser or eww or -download the file it points to. If while switching buffers you spot an -old one, you can kill it right there and continue to select another. -Embark comes preconfigured with over a hundred actions for common -types of targets such as files, buffers, identifiers, s-expressions, -sentences; and it is easy to add more actions and more target types. -Embark can also collect all the candidates in a minibuffer to an -occur-like buffer or export them to a buffer in a major-mode specific -to the type of candidates, such as dired for a set of files, ibuffer -for a set of buffers, or customize for a set of variables. - -@menu -* Acting on targets:: -* The default action on a target:: -* Working with sets of possible targets:: -* Switching to a different command without losing what you've typed:: -@end menu - -@node Acting on targets -@section Acting on targets - -You can think of @samp{embark-act} as a keyboard-based version of a -right-click contextual menu. The @samp{embark-act} command (which you should -bind to a convenient key), acts as a prefix for a keymap offering you -relevant @emph{actions} to use on a @emph{target} determined by the context: - -@itemize -@item -In the minibuffer, the target is the current top completion -candidate. -@item -In the @samp{*Completions*} buffer the target is the completion at point. -@item -In a regular buffer, the target is the region if active, or else the -file, symbol, URL, s-expression or defun at point. -@end itemize - -Multiple targets can be present at the same location and you can cycle -between them by repeating the @samp{embark-act} key binding. The type of -actions offered depend on the type of the target. Here is a sample of -a few of the actions offered in the default configuration: - -@itemize -@item -For files you get offered actions like deleting, copying, -renaming, visiting in another window, running a shell command on the -file, etc. -@item -For buffers the actions include switching to or killing the buffer. -@item -For package names the actions include installing, removing or -visiting the homepage. -@item -For Emacs Lisp symbols the actions include finding the definition, -looking up documentation, evaluating (which for a variable -immediately shows the value, but for a function lets you pass it -some arguments first). There are some actions specific to variables, -such as setting the value directly or though the customize system, -and some actions specific to commands, such as binding it to a key. -@end itemize - -By default when you use @samp{embark-act} if you don't immediately select an -action, after a short delay Embark will pop up a buffer showing a list -of actions and their corresponding key bindings. If you are using -@samp{embark-act} outside the minibuffer, Embark will also highlight the -current target. These behaviors are configurable via the variable -@samp{embark-indicators}. Instead of selecting an action via its key binding, -you can select it by name with completion by typing @samp{C-h} after -@samp{embark-act}. - -Everything is easily configurable: determining the current target, -classifying it, and deciding which actions are offered for each type -in the classification. The above introduction just mentions part of -the default configuration. - -Configuring which actions are offered for a type is particularly easy -and requires no programming: the variable @samp{embark-keymap-alist} -associates target types with variables containing keymaps, and those -keymaps containing bindings for the actions. (To examine the available -categories and their associated keymaps, you can use @samp{C-h v -embark-keymap-alist} or customize that variable.) For example, in the -default configuration the type @samp{file} is associated with the symbol -@samp{embark-file-map}. That symbol names a keymap with single-letter key -bindings for common Emacs file commands, for instance @samp{c} is bound to -@samp{copy-file}. This means that if you are in the minibuffer after running -a command that prompts for a file, such as @samp{find-file} or @samp{rename-file}, -you can copy a file by running @samp{embark-act} and then pressing @samp{c}. - -These action keymaps are very convenient but not strictly necessary -when using @samp{embark-act}: you can use any command that reads from the -minibuffer as an action and the target of the action will be inserted -at the first minibuffer prompt. After running @samp{embark-act} all of your -key bindings and even @samp{execute-extended-command} can be used to run a -command. For example, if you want to replace all occurrences of the -symbol at point, just use @samp{M-%} as the action, there is no need to bind -@samp{query-replace} in one of Embark's keymaps. Also, those action keymaps -are normal Emacs keymaps and you should feel free to bind in them -whatever commands you find useful as actions and want to be available -through convenient bindings. - -The actions in @samp{embark-general-map} are available no matter what type -of completion you are in the middle of. By default this includes -bindings to save the current candidate in the kill ring and to insert -the current candidate in the previously selected buffer (the buffer -that was current when you executed a command that opened up the -minibuffer). - -Emacs's minibuffer completion system includes metadata indicating the -@emph{category} of what is being completed. For example, @samp{find-file}'s -metadata indicates a category of @samp{file} and @samp{switch-to-buffer}'s metadata -indicates a category of @samp{buffer}. Embark has the related notion of the -@emph{type} of a target for actions, and by default when category metadata -is present it is taken to be the type of minibuffer completion -candidates when used as targets. Emacs commands often do not set -useful category metadata so the @uref{https://github.com/minad/marginalia, Marginalia} package, which supplies -this missing metadata, is highly recommended for use with Embark. - -Embark's default configuration has actions for the following target -types: files, buffers, symbols, packages, URLs, bookmarks, and as a -somewhat special case, actions for when the region is active. You can -read about the @uref{https://github.com/oantolin/embark/wiki/Default-Actions, default actions and their key bindings} on the GitHub -project wiki. - -@node The default action on a target -@section The default action on a target - -Embark has a notion of default action for a target: - -@itemize -@item -If the target is a minibuffer completion candidate, then the default -action is whatever command opened the minibuffer in the first place. -For example if you run @samp{kill-buffer}, then the default action will be -to kill buffers. -@item -If the target comes from a regular buffer (i.e., not a minibuffer), -then the default action is whatever is bound to @samp{RET} in the keymap of -actions for that type of target. For example, in Embark's default -configuration for a URL found at point the default action is -@samp{browse-url}, because @samp{RET} is bound to @samp{browse-url} in the @samp{embark-url-map} -keymap. -@end itemize - -To run the default action you can press @samp{RET} after running @samp{embark-act}. -Note that if there are several different targets at a given location, -each has its own default action, so first cycle to the target you want -and then press @samp{RET} to run the corresponding default action. - -There is also @samp{embark-dwim} which runs the default action for the first -target found. It's pretty handy in non-minibuffer buffers: with -Embark's default configuration it will: - -@itemize -@item -Open the file at point. -@item -Open the URL at point in a web browser (using the @samp{browse-url} -command). -@item -Compose a new email to the email address at point. -@item -In an Emacs Lisp buffer, if point is on an opening parenthesis or -right after a closing one, it will evaluate the corresponding -expression. -@item -Go to the definition of an Emacs Lisp function, variable or macro at -point. -@item -Find the file corresponding to an Emacs Lisp library at point. -@end itemize - -@node Working with sets of possible targets -@section Working with sets of possible targets - -Besides acting individually on targets, Embark lets you work -collectively on a set of target @emph{candidates}. For example, while you are -in the minibuffer the candidates are simply the possible completions -of your input. Embark provides three main commands to work on candidate -sets: - -@itemize -@item -The @samp{embark-act-all} command runs the same action on each of the -current candidates. It is just like using @samp{embark-act} on each -candidate in turn. (Because you can easily act on many more -candidates than you meant to, by default Embark asks you to confirm -uses of @samp{embark-act-all}; you can turn this off by setting the user -option @samp{embark-confirm-act-all} to @samp{nil}.) - -@item -The @samp{embark-collect} command produces a buffer listing all the current -candidates, for you to peruse and run actions on at your leisure. -The candidates are displayed as a list showing additional -annotations. If any of the candidates contain newlines, then -horizontal lines are used to separate candidates. - -The Embark Collect buffer is somewhat ``dired-like'': you can select -and deselect candidates through @samp{embark-select} (available as an -action in @samp{embark-act}, bound to @samp{SPC}; but you could also give it a -global key binding). In an Embark Collect buffer @samp{embark-act} is bound -to @samp{a} and @samp{embark-act-all} is bound to @samp{A}; @samp{embark-act-all} will act on -all currently marked candidates if there any, and will act on all -candidates if none are marked. In particular, this means that @samp{a SPC} -will toggle whether the candidate at point is selected, and @samp{A SPC} -will select all candidates if none are selected, or deselect all -selected candidates if there are some. - -@item -The @samp{embark-export} command tries to open a buffer in an appropriate -major mode for the set of candidates. If the candidates are files -export produces a Dired buffer; if they are buffers, you get an -Ibuffer buffer; and if they are packages you get a buffer in -package menu mode. - -If you use the grepping commands from the @uref{https://github.com/minad/consult/, Consult} package, -@samp{consult-grep}, @samp{consult-git-grep} or @samp{consult-ripgrep}, then you should -install the @samp{embark-consult} package, which adds support for exporting a -list of grep results to an honest grep-mode buffer, on which you can -even use @uref{https://github.com/mhayashi1120/Emacs-wgrep, wgrep} if you wish. -@end itemize - -When in doubt choosing between exporting and collecting, a good rule -of thumb is to always prefer @samp{embark-export} since when an exporter to a -special major mode is available for a given type of target, it will be -more featureful than an Embark collect buffer, and if no such exporter -is configured the @samp{embark-export} command falls back to the generic -@samp{embark-collect}. - -These commands are always available as ``actions'' (although they do not -act on just the current target but on all candidates) for @samp{embark-act} -and are bound to @samp{A}, @samp{S} (for ``snapshot''), and @samp{E}, respectively, in -@samp{embark-general-map}. This means that you do not have to bind your own -key bindings for these (although you can, of course!), just a key -binding for @samp{embark-act}. - -In Embark Collect or Embark Export buffers that were obtained by -running @samp{embark-collect} or @samp{embark-export} from within a minibuffer -completion session, @samp{g} is bound to a command that restarts the -completion session, that is, the command that opened the minibuffer is -run again and the minibuffer contents restored. You can then interact -normally with the command, perhaps editing the minibuffer contents, -and, if you wish, you can rerun @samp{embark-collect} or @samp{embark-export} to get -an updated buffer. - -@menu -* Selecting some targets to make an ad hoc candidate set:: -* @samp{embark-live} a live-updating variant of @samp{embark-collect}:: -@end menu - -@node Selecting some targets to make an ad hoc candidate set -@subsection Selecting some targets to make an ad hoc candidate set - -The commands for working with sets of candidates just described, -namely @samp{embark-act-all}, @samp{embark-export} and @samp{embark-collect} by default -work with all candidates defined in the current context. For example, -in the minibuffer they operate on all currently completion candidates, -or in a dired buffer they work on all marked files (or all files if -none are marked). Embark also has a notion of @emph{selection}, where you can -accumulate an ad hoc list of targets for these commands to work on. - -The selection is controlled by using the @samp{embark-select} action, bound -to @samp{SPC} in @samp{embark-general-map} so that it is always available (you can -also give @samp{embark-select} a global key binding if you wish; when called -directly, not as an action for @samp{embark-act}, it will select the first -target at point). Calling this action on a target toggles its -membership in the current buffer's Embark selection; that is, it adds -it to selection if not selected and removes it from the selection if -it was selected. Whenever the selection for a buffer is non-empty, the -commands @samp{embark-act-all}, @samp{embark-export} and @samp{embark-collect} will act on -the selection. - -To deselect all selected targets, you can use the @samp{embark-select} action -through @samp{embark-act-all}, since this will run @samp{embark-select} on each -member of the current selection. Similarly if no targets are selected -and you are in a minibuffer completion session, running @samp{embark-select} -from @samp{embark-act-all} will select all the current completion candidates. - -By default, whenever some targets are selected in the current buffer, -a count of selected targets appears in the mode line. This can be -turned off or customized through the @samp{embark-selection-indicator} user -option. - -The selection functionality is supported in every buffer: - -@itemize -@item -In the minibuffer this gives a convenient way to act on several -completion candidates that don't follow any simple pattern: just go -through the completions selecting the ones you want, then use -@samp{embark-act-all}. For example, you could attach several files at once -to an email. -@item -For Embark Collect buffers this functionality enables a dired-like -workflow, in which you mark various candidates and apply an action -to all at once. (It supersedes a previous ad hoc dired-like -interface that was implemented only in Embark Collect buffers, with -a slightly different interface.) -@item -In a eww buffer you could use this to select various links you wish -to follow up on, and then collect them into a buffer. Similarly, -while reading Emacs's info manual you could select some symbols you -want to read more about and export them to an @samp{apropos-mode} buffer. -@item -You can use selections in regular text or programming buffers to do -complex editing operations. For example, if you have three -paragraphs scattered over a file and you want to bring them -together, you can select each one, insert them all somewhere and -finally delete all of them (from their original locations). -@end itemize - -@node @samp{embark-live} a live-updating variant of @samp{embark-collect} -@subsection @samp{embark-live} a live-updating variant of @samp{embark-collect} - -Finally, there is also an @samp{embark-live} variant of the @samp{embark-collect} -command which automatically updates the collection after each change -in the source buffer. Users of a completion UI that automatically -updates and displays the candidate list (such as Vertico, Icomplete, -Fido-mode, or MCT) will probably not want to use -@samp{embark-live} from the minibuffer as they will then have two live -updating displays of the completion candidates! - -A more likely use of @samp{embark-live} is to be called from a regular buffer -to display a sort of live updating ``table of contents'' for the buffer. -This depends on having appropriate candidate collectors configured in -@samp{embark-candidate-collectors}. There are not many in Embark's default -configuration, but you can try this experiment: open a dired buffer in -a directory that has very many files, mark a few, and run @samp{embark-live}. -You'll get an Embark Collect buffer containing only the marked files, -which updates as you mark or unmark files in dired. To make -@samp{embark-live} genuinely useful other candidate collectors are required. -The @samp{embark-consult} package (documented near the end of this manual) -contains a few: one for imenu items and one for outline headings as -used by @samp{outline-minor-mode}. Those collectors really do give -@samp{embark-live} a table-of-contents feel. - -@node Switching to a different command without losing what you've typed -@section Switching to a different command without losing what you've typed - -Embark also has the @samp{embark-become} command which is useful for when -you run a command, start typing at the minibuffer and realize you -meant a different command. The most common case for me is that I run -@samp{switch-to-buffer}, start typing a buffer name and realize I haven't -opened the file I had in mind yet! I'll use this situation as a -running example to illustrate @samp{embark-become}. When this happens I can, -of course, press @samp{C-g} and then run @samp{find-file} and open the file, but -this requires retyping the portion of the file name you already -typed. This process can be streamlined with @samp{embark-become}: while still -in the @samp{switch-to-buffer} you can run @samp{embark-become} and effectively -make the @samp{switch-to-buffer} command become @samp{find-file} for this run. - -You can bind @samp{embark-become} to a key in @samp{minibuffer-local-map}, but it is -also available as an action under the letter @samp{B} (uppercase), so you -don't need a binding if you already have one for @samp{embark-act}. So, -assuming I have @samp{embark-act} bound to, say, @samp{C-.}, once I realize I -haven't open the file I can type @samp{C-. B C-x C-f} to have -@samp{switch-to-buffer} become @samp{find-file} without losing what I have already -typed in the minibuffer. - -But for even more convenience, @samp{embark-become} offers shorter key -bindings for commands you are likely to want the current command to -become. When you use @samp{embark-become} it looks for the current command in -all keymaps named in the list @samp{embark-become-keymaps} and then activates -all keymaps that contain it. For example, the default value of -@samp{embark-become-keymaps} contains a keymap @samp{embark-become-file+buffer-map} -with bindings for several commands related to files and buffers, in -particular, it binds @samp{switch-to-buffer} to @samp{b} and @samp{find-file} to @samp{f}. So when -I accidentally try to switch to a buffer for a file I haven't opened -yet, @samp{embark-become} finds that the command I ran, @samp{switch-to-buffer}, is -in the keymap @samp{embark-become-file+buffer-map}, so it activates that -keymap (and any others that also contain a binding for -@samp{switch-to-buffer}). The end result is that I can type @samp{C-. B f} to -switch to @samp{find-file}. - -@node Quick start -@chapter Quick start - -The easiest way to install Embark is from GNU ELPA, just run @samp{M-x -package-install RET embark RET}. (It is also available on MELPA@.) It is -highly recommended to also install @uref{https://github.com/minad/marginalia, Marginalia} (also available on GNU -ELPA), so that Embark can offer you preconfigured actions in more -contexts. For @samp{use-package} users, the following is a very reasonable -starting configuration: - -@lisp -(use-package marginalia - :ensure t - :config - (marginalia-mode)) - -(use-package embark - :ensure t - - :bind - (("C-." . embark-act) ;; pick some comfortable binding - ("C-;" . embark-dwim) ;; good alternative: M-. - ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' - - :init - - ;; Optionally replace the key help with a completing-read interface - (setq prefix-help-command #'embark-prefix-help-command) - - ;; Show the Embark target at point via Eldoc. You may adjust the - ;; Eldoc strategy, if you want to see the documentation from - ;; multiple providers. Beware that using this can be a little - ;; jarring since the message shown in the minibuffer can be more - ;; than one line, causing the modeline to move up and down: - - ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) - ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) - - :config - - ;; Hide the mode line of the Embark live/completions buffers - (add-to-list 'display-buffer-alist - '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" - nil - (window-parameters (mode-line-format . none))))) - -;; Consult users will also want the embark-consult package. -(use-package embark-consult - :ensure t ; only need to install it, embark loads it after consult if found - :hook - (embark-collect-mode . consult-preview-at-point-mode)) -@end lisp - -About the suggested key bindings for @samp{embark-act} and @samp{embark-dwim}: -@itemize -@item -Those key bindings are unlikely to work in the terminal, but -terminal users are probably well aware of this and will know to -select different bindings. -@item -The suggested @samp{C-.} binding is used by default in (at least some -installations of) GNOME to input emojis, and Emacs doesn't even get -a chance to respond to the binding. You can select a different key -binding for @samp{embark-act} or use @samp{ibus-setup} to change the shortcut for -emoji insertion (Emacs 29 will likely use @samp{C-x 8 e e}, in case you -want to set the same one system-wide). -@item -The suggested alternative of @samp{M-.} for @samp{embark-dwim} is bound by default -to @samp{xref-find-definitions}. That is a very useful command but -overwriting it with @samp{embark-dwim} is sensible since in Embark's -default configuration, @samp{embark-dwim} will also find the definition of -the identifier at point. (Note that @samp{xref-find-definitions} with a -prefix argument prompts you for an identifier, @samp{embark-dwim} does not -cover this case). -@end itemize - -Other Embark commands such as @samp{embark-act-all}, @samp{embark-become}, -@samp{embark-collect}, and @samp{embark-export} can be run through @samp{embark-act} as -actions bound to @samp{A}, @samp{B}, @samp{S} (for ``snapshot''), and @samp{E} respectively, and -thus don't really need a dedicated key binding, but feel free to bind -them directly if you so wish. If you do choose to bind them directly, -you'll probably want to bind them in @samp{minibuffer-local-map}, since they -are most useful in the minibuffer (in fact, @samp{embark-become} only works -in the minibuffer). - -The command @samp{embark-dwim} executes the default action at point. Another good -keybinding for @samp{embark-dwim} is @samp{M-.} since @samp{embark-dwim} acts like -@samp{xref-find-definitions} on the symbol at point. @samp{C-.} can be seen as a -right-click context menu at point and @samp{M-.} acts like left-click. The -keybindings are mnemonic, both act at the point (@samp{.}). - -Embark needs to know what your minibuffer completion system considers -to be the list of candidates and which one is the current candidate. -Embark works out of the box if you use Emacs's default tab completion, -the built-in @samp{icomplete-mode} or @samp{fido-mode}, or the third-party packages -@uref{https://github.com/minad/vertico, Vertico} or @uref{https://github.com/abo-abo/swiper, Ivy}. - -If you are a @uref{https://emacs-helm.github.io/helm/, Helm} or @uref{https://github.com/abo-abo/swiper, Ivy} user you are unlikely to want Embark since -those packages include comprehensive functionality for acting on -minibuffer completion candidates. (Embark does come with Ivy -integration despite this.) - -@node Advanced configuration -@chapter Advanced configuration - -@menu -* Showing information about available targets and actions:: -* Selecting commands via completions instead of key bindings:: -* Quitting the minibuffer after an action:: -* Running some setup after injecting the target:: -* Running hooks before, after or around an action: Running hooks before after or around an action. -* Creating your own keymaps:: -* Defining actions for new categories of targets:: -@end menu - -@node Showing information about available targets and actions -@section Showing information about available targets and actions - -By default, if you run @samp{embark-act} and do not immediately select an -action, after a short delay Embark will pop up a buffer called @samp{*Embark -Actions*} containing a list of available actions with their key -bindings. You can scroll that buffer with the mouse of with the usual -commands @samp{scroll-other-window} and @samp{scroll-other-window-down} (bound by -default to @samp{C-M-v} and @samp{C-M-S-v}). - -That functionality is provided by the @samp{embark-mixed-indicator}, but -Embark has other indicators that can provide information about the -target and its type, what other targets you can cycle to, and which -actions have key bindings in the action map for the current type of -target. Any number of indicators can be active at once and the user -option @samp{embark-indicators} should be set to a list of the desired -indicators. - -Embark comes with the following indicators: - -@itemize -@item -@samp{embark-minimal-indicator}: shows a messages in the echo area or -minibuffer prompt showing the current target and the types of all -targets starting with the current one. - -@item -@samp{embark-highlight-indicator}: highlights the target at point; on by -default. - -@item -@samp{embark-verbose-indicator}: displays a table of actions and their key -bindings in a buffer; this is not on by default, in favor of the -mixed indicator described next. - -@item -@samp{embark-mixed-indicator}: starts out by behaving as the minimal -indicator but after a short delay acts as the verbose indicator; -this is on by default. - -@item -@samp{embark-isearch-highlight-indicator}: this only does something when -the current target is the symbol at point, in which case it -lazily highlights all occurrences of that symbol in the current -buffer, like isearch; also on by default. -@end itemize - -Users of the popular @uref{https://github.com/justbur/emacs-which-key, which-key} package may prefer to use the -@samp{embark-which-key-indicator} from the @uref{https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt, Embark wiki}. Just copy its -definition from the wiki into your configuration and customize the -@samp{embark-indicators} user option to exclude the mixed and verbose -indicators and to include @samp{embark-which-key-indicator}. - -If you use @uref{https://github.com/minad/vertico, Vertico}, there is an even easier way to get a -@samp{which-key}-like display that also lets you use completion to narrow -down the list of alternatives, described at the end of the next -section. - -@node Selecting commands via completions instead of key bindings -@section Selecting commands via completions instead of key bindings - -As an alternative to reading the list of actions in the verbose or -mixed indicators (see the previous section for a description of -these), you can press the @samp{embark-help-key}, which is @samp{C-h} by default -(but you may prefer @samp{?} to free up @samp{C-h} for use as a prefix) after -running @samp{embark-act}. Pressing the help key will prompt you for the name -of an action with completion (but feel free to enter a command that is -not among the offered candidates!), and will also remind you of the -key bindings. You can press @samp{embark-keymap-prompter-key}, which is @samp{@@} by -default, at the prompt and then one of the key bindings to enter the -name of the corresponding action. - -You may think that with the @samp{*Embark Actions*} buffer popping up to -remind you of the key bindings you'd never want to use completion to -select an action by name, but personally I find that typing a small -portion of the action name to narrow down the list of candidates feels -significantly faster than visually scanning the entire list of actions. - -If you find you prefer selecting actions that way, you can configure -embark to always prompt you for actions by setting the variable -@samp{embark-prompter} to @samp{embark-completing-read-prompter}. - -On the other hand, you may wish to continue using key bindings for the -actions you perform most often, and to use completion only to explore -what further actions are available or when you've forgotten a key -binding. In that case, you may prefer to use the minimal indicator, -which does not pop-up an @samp{*Embark Actions*} buffer at all, and to use -the @samp{embark-help-key} whenever you need help. This unobtrusive setup is -achieved with the following configuration: - -@lisp -(setq embark-indicators - '(embark-minimal-indicator ; default is embark-mixed-indicator - embark-highlight-indicator - embark-isearch-highlight-indicator)) -@end lisp - -@uref{https://github.com/minad/vertico, Vertico} users may wish to configure a grid display for the actions and -key-bindings, reminiscent of the popular package @uref{https://github.com/justbur/emacs-which-key, which-key}, but, of -course, enhanced by the use of completion to narrow the list of -commands. In order to get the grid display, put the following in your -Vertico configuration: - -@lisp -(add-to-list 'vertico-multiform-categories '(embark-keybinding grid)) -(vertico-multiform-mode) -@end lisp - -This will make the available keys be shown in a compact grid like in -@samp{which-key}. The @samp{vertico-multiform-mode} also enables keys such as @samp{M-V}, -@samp{M-G}, @samp{M-B}, and @samp{M-U} for manually switching between layouts in Vertico -buffers. - -@menu -* Selecting commands via completion outside of Embark:: -@end menu - -@node Selecting commands via completion outside of Embark -@subsection Selecting commands via completion outside of Embark - -If you like this completion interface for exploring key bindings for -Embark actions, you may want to use it elsewhere in Emacs. You can use -Embark's completion-based command prompter to list: - -@itemize -@item -key bindings under a prefix, -@item -local key bindings, or -@item -all key bindings. -@end itemize - -To use it for key bindings under a prefix (you can use this to replace -the @samp{which-key} package, for example), use this configuration: - -@lisp -(setq prefix-help-command #'embark-prefix-help-command) -@end lisp - -Now, when you have started on a prefix sequence such as @samp{C-x} or @samp{C-c}, -pressing @samp{C-h} will bring up the Embark version of the built-in -@samp{prefix-help-command}, which will list the keys under that prefix and -their bindings, and lets you select the one you wanted with completion, -or by key binding if you press @samp{embark-keymap-prompter-key}. - -To list local or global key bindings, use the command @samp{embark-bindings}. -You can bind that to @samp{C-h b}, which is the default key binding for the -built-in @samp{describe-bindings} command, which this command can replace. By -default, @samp{embark-bindings} lists local key bindings, typically those -bound in the major mode keymap; to get global bindings as well, call -it with a @samp{C-u} prefix argument. - -@node Quitting the minibuffer after an action -@section Quitting the minibuffer after an action - -By default, if you call @samp{embark-act} from the minibuffer it quits the -minibuffer after performing the action. You can change this by setting -the user option @samp{embark-quit-after-action} to @samp{nil}. Having @samp{embark-act} @emph{not} -quit the minibuffer can be useful to turn commands into little ``thing -managers''. For example, you can use @samp{find-file} as a little file manager -or @samp{describe-package} as a little package manager: you can run those -commands, perform a series of actions, and then quit the command. - -If you want to control the quitting behavior in a fine-grained manner -depending on the action, you can set @samp{embark-quit-after-action} to an -alist, associating commands to either @samp{t} for quitting or @samp{nil} for not -quitting. When using an alist, you can use the special key @samp{t} to -specify the default behavior. For example, to specify that by default -actions should not quit the minibuffer but that using @samp{kill-buffer} as -an action should quit, you can use the following configuration: - -@lisp -(setq embark-quit-after-action '((kill-buffer . t) (t . nil))) -@end lisp - -The variable @samp{embark-quit-after-action} only specifies a default, that -is, it only controls whether or not @samp{embark-act} quits the minibuffer -when you call it without a prefix argument, and you can select the -opposite behavior to what the variable says by calling @samp{embark-act} with -@samp{C-u}. Also note that both the variable @samp{embark-quit-after-action} and @samp{C-u} -have no effect when you call @samp{embark-act} outside the minibuffer. - -If you find yourself using the quitting and non-quitting variants of -@samp{embark-act} about equally often, independently of the action, you may -prefer to simply have separate commands for them instead of a single -command that you call with @samp{C-u} half the time. You could, for example, -keep the default exiting behavior of @samp{embark-act} and define a -non-quitting version as follows: - -@lisp -(defun embark-act-noquit () - "Run action but don't quit the minibuffer afterwards." - (interactive) - (let ((embark-quit-after-action nil)) - (embark-act))) -@end lisp - -@node Running some setup after injecting the target -@section Running some setup after injecting the target - -You can customize what happens after the target is inserted at the -minibuffer prompt of an action. There are -@samp{embark-target-injection-hooks}, that are run by default after injecting -the target into the minibuffer. The variable -@samp{embark-target-injection-hooks} is an alist associating commands to -their setup hooks. There are two special keys: if no setup hook is -specified for a given action, the hook associated to @samp{t} is run; and the -hook associated to @samp{:always} is run regardless of the action. (This -variable used to have the less explicit name of -@samp{embark-setup-action-hooks}, so please update your configuration.) - -For example, consider using @samp{shell-command} as an action during file -completion. It would be useful to insert a space before the target -file name and to leave the point at the beginning, so you can -immediately type the shell command to run on that file. That's why in -Embark's default configuration there is an entry in -@samp{embark-target-injection-hooks} associating @samp{shell-command} to a hook that -includes @samp{embark--shell-prep}, a simple helper function that quotes all -the spaces in the file name, inserts an extra space at the beginning -of the line and leaves point to the left of it. - -Now, the preparation that @samp{embark--shell-prep} does would be useless if -Embark did what it normally does after it inserts the target of the -action at the minibuffer prompt, which is to ``press @samp{RET}'' for you, -accepting the target as is; if Embark did that for @samp{shell-command} you -wouldn't get a chance to type in the command to execute! That is why -in Embark's default configuration the entry for @samp{shell-command} in -@samp{embark-target-injection-hooks} also contains the function -@samp{embark--allow-edit}. - -Embark used to have a dedicated variable @samp{embark-allow-edit-actions} to -which you could add commands for which Embark should forgo pressing -@samp{RET} for you after inserting the target. Since its effect can also be -achieved via the general @samp{embark-target-injection-hooks} mechanism, that -variable has been removed to simplify Embark. Be sure to update your -configuration; if you had something like: - -@lisp -(add-to-list 'embark-allow-edit-actions 'my-command) -@end lisp - -you should replace it with: - -@lisp -(push 'embark--allow-edit - (alist-get 'my-command embark-target-injection-hooks)) -@end lisp - - -Also note that while you could abuse @samp{embark--allow-edit} so that you -have to confirm ``dangerous'' actions such as @samp{delete-file}, it is better -to implement confirmation by adding the @samp{embark--confirm} function to -the appropriate entry of a different hook alist, namely, -@samp{embark-pre-action-hooks}. - -Besides @samp{embark--allow-edit}, Embark comes with another function that is -of general utility in action setup hooks: @samp{embark--ignore-target}. Use -it for commands that do prompt you in the minibuffer but for which -inserting the target would be inappropriate. This is not a common -situation but does occasionally arise. For example it is used by -default for @samp{shell-command-on-region}: that command is used as an action -for region targets, and it prompts you for a shell command; you -typically do @emph{not} want the target, that is the contents of the region, -to be entered at that prompt! - -@node Running hooks before after or around an action -@section Running hooks before, after or around an action - -Embark has three variables, @samp{embark-pre-action-hooks}, -@samp{embark-post-action-hooks} and @samp{embark-around-action-hooks}, which are -alists associating commands to hooks that should run before or after -or as around advice for the command when used as an action. As with -@samp{embark-target-injection-hooks}, there are two special keys for the -alists: @samp{t} designates the default hook to run when no specific hook is -specified for a command; and the hook associated to @samp{:always} runs -regardless. - -The default values of those variables are fairly extensive, adding -creature comforts to make running actions a smooth experience. Embark -comes with several functions intended to be added to these hooks, and -used in the default values of @samp{embark-pre-action-hooks}, -@samp{embark-post-action-hooks} and @samp{embark-around-action-hooks}. - -For pre-action hooks: - -@table @asis -@item @samp{embark--confirm} -Prompt the user for confirmation before executing -the action. This is used be default for commands deemed ``dangerous'', -or, more accurately, hard to undo, such as @samp{delete-file} and -@samp{kill-buffer}. - -@item @samp{embark--unmark-target} -Unmark the active region. Use this for -commands you want to act on the region contents but without the -region being active. The default configuration uses this function as -a pre-action hook for @samp{occur} and @samp{query-replace}, for example, so that -you can use them as actions with region targets to search the whole -buffer for the text contained in the region. Without this pre-action -hook using @samp{occur} as an action for a region target would be -pointless: it would search for the the region contents @emph{in the -region}, (typically, due to the details of regexps) finding only one -match! - -@item @samp{embark--beginning-of-target} -Move to the beginning of the target -(for targets that report bounds). This is used by default for -backward motion commands such as @samp{backward-sexp}, so that they don't -accidentally leave you on the current target. - -@item @samp{embark--end-of-target} -Move to the end of the target. This is used -similarly to the previous function, but also for commands that act -on the last s-expression like @samp{eval-last-sexp}. This allow you to act -on an s-expression from anywhere inside it and still use -@samp{eval-last-sexp} as an action. - -@item @samp{embark--xref-push-markers} -Push the current location on the xref -marker stack. Use this for commands that take you somewhere and for -which you'd like to be able to come back to where you were using -@samp{xref-pop-marker-stack}. This is used by default for @samp{find-library}. -@end table - -For post-action hooks: - -@table @asis -@item @samp{embark--restart} -Restart the command currently prompting in the -minibuffer, so that the list of completion candidates is updated. -This is useful as a post action hook for commands that delete or -rename a completion candidate; for example the default value of -@samp{embark-post-action-hooks} uses it for @samp{delete-file}, @samp{kill-buffer}, -@samp{rename-file}, @samp{rename-buffer}, etc. -@end table - -For around-action hooks: - -@table @asis -@item @samp{embark--mark-target} -Save existing mark and point location, mark -the target and run the action. Most targets at point outside the -minibuffer report which region of the buffer they correspond to -(this is the information used by @samp{embark-highlight-indicator} to -know what portion of the buffer to highlight); this function marks -that region. It is useful as an around action hook for commands that -expect a region to be marked, for example, it is used by default for -@samp{indent-region} so that it works on s-expression targets, or for -@samp{fill-region} so that it works on paragraph targets. - -@item @samp{embark--cd} -Run the action with @samp{default-directory} set to the -directory associated to the current target. The target should be of -type @samp{file}, @samp{buffer}, @samp{bookmark} or @samp{library}, and the associated directory -is what you'd expect in each case. - -@item @samp{embark--narrow-to-target} -Run the action with buffer narrowed to -current target. Use this as an around hook to localize the effect of -actions that don't already work on just the region. In the default -configuration it is used for @samp{repunctuate-sentences}. - -@item @samp{embark--save-excursion} -Run the action restoring point at the end. -The current default configuration doesn't use this but it is -available for users. -@end table - -@node Creating your own keymaps -@section Creating your own keymaps - -All internal keymaps are defined with the standard helper macro -@samp{defvar-keymap}. For example a simple version of the file action keymap -could be defined as follows: - -@lisp -(defvar-keymap embark-file-map - :doc "Example keymap with a few file actions" - :parent embark-general-map - "d" #'delete-file - "r" #'rename-file - "c" #'copy-file) -@end lisp - -These action keymaps are perfectly normal Emacs -keymaps. You may want to inherit from the @samp{embark-general-map} if you -want to access the default Embark actions. Note that @samp{embark-collect} -and @samp{embark-export} are also made available via @samp{embark-general-map}. - -@node Defining actions for new categories of targets -@section Defining actions for new categories of targets - -It is easy to configure Embark to provide actions for new types of -targets, either in the minibuffer or outside it. I present below two -very detailed examples of how to do this. At several points I'll -explain more than one way to proceed, typically with the easiest -option first. I include the alternative options since there will be -similar situations where the easiest option is not available. - -@menu -* New minibuffer target example - tab-bar tabs:: -* New target example in regular buffers - short Wikipedia links:: -@end menu - -@node New minibuffer target example - tab-bar tabs -@subsection New minibuffer target example - tab-bar tabs - -As an example, take the new @uref{https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html, tab bars} from Emacs 27. I'll explain how -to configure Embark to offer tab-specific actions when you use the -tab-bar-mode commands that mention tabs by name. The configuration -explained here is now built-in to Embark (and Marginalia), but it's -still a good self-contained example. In order to setup up tab actions -you would need to: (1) make sure Embark knows those commands deal with -tabs, (2) define a keymap for tab actions and configure Embark so it -knows that's the keymap you want. - -@enumerate -@item -@anchor{Telling Embark about commands that prompt for tabs by name}Telling Embark about commands that prompt for tabs by name - - -For step (1), it would be great if the @samp{tab-bar-mode} commands reported -the completion category @samp{tab} when asking you for a tab with -completion. (All built-in Emacs commands that prompt for file names, -for example, do have metadata indicating that they want a @samp{file}.) They -do not, unfortunately, and I will describe a couple of ways to deal -with this. - -Maybe the easiest thing is to configure @uref{https://github.com/minad/marginalia, Marginalia} to enhance those -commands. All of the @samp{tab-bar-*-tab-by-name} commands have the words -``tab by name'' in the minibuffer prompt, so you can use: - -@lisp -(add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) -@end lisp - -That's it! But in case you are ever in a situation where you don't -already have commands that prompt for the targets you want, I'll -describe how writing your own command with appropriate @samp{category} -metadata looks: - -@lisp -(defun my-select-tab-by-name (tab) - (interactive - (list - (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - (tab-bar-tabs)) - (user-error "No tabs found")))) - (completing-read - "Tabs: " - (lambda (string predicate action) - (if (eq action 'metadata) - '(metadata (category . tab)) - (complete-with-action - action tab-list string predicate))))))) - (tab-bar-select-tab-by-name tab)) -@end lisp - -As you can see, the built-in support for setting the category -meta-datum is not very easy to use or pretty to look at. To help with -this I recommend the @samp{consult--read} function from the excellent -@uref{https://github.com/minad/consult/, Consult} package. With that function we can rewrite the command as -follows: - -@lisp -(defun my-select-tab-by-name (tab) - (interactive - (list - (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - (tab-bar-tabs)) - (user-error "No tabs found")))) - (consult--read tab-list - :prompt "Tabs: " - :category 'tab)))) - (tab-bar-select-tab-by-name tab)) -@end lisp - -Much nicer! No matter how you define the @samp{my-select-tab-by-name} -command, the first approach with Marginalia and prompt detection has -the following advantages: you get the @samp{tab} category for all the -@samp{tab-bar-*-bar-by-name} commands at once, also, you enhance built-in -commands, instead of defining new ones. - -@item -@anchor{Defining and configuring a keymap for tab actions}Defining and configuring a keymap for tab actions - - -Let's say we want to offer select, rename and close actions for tabs -(in addition to Embark general actions, such as saving the tab name to -the kill-ring, which you get for free). Then this will do: - -@lisp -(defvar-keymap embark-tab-actions - :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." - :parent embark-general-map - "s" #'tab-bar-select-tab-by-name - "r" #'tab-bar-rename-tab-by-name - "k" #'tab-bar-close-tab-by-name) - -(add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) -@end lisp - -What if after using this for a while you feel closing the tab -without confirmation is dangerous? You have a couple of options: - -@enumerate -@item -You can keep using the @samp{tab-bar-close-tab-by-name} command, but have -Embark ask you for confirmation: -@lisp -(push #'embark--confirm - (alist-get 'tab-bar-close-tab-by-name - embark-pre-action-hooks)) -@end lisp - -@item -You can write your own command that prompts for confirmation and -use that instead of @samp{tab-bar-close-tab-by-name} in the above keymap: -@lisp -(defun my-confirm-close-tab-by-name (tab) - (interactive "sTab to close: ") - (when (y-or-n-p (format "Close tab '%s'? " tab)) - (tab-bar-close-tab-by-name tab))) -@end lisp - -Notice that this is a command you can also use directly from @samp{M-x} -independently of Embark. Using it from @samp{M-x} leaves something to be -desired, though, since you don't get completion for the tab names. -You can fix this if you wish as described in the previous section. -@end enumerate -@end enumerate - -@node New target example in regular buffers - short Wikipedia links -@subsection New target example in regular buffers - short Wikipedia links - -Say you want to teach Embark to treat text of the form -@samp{wikipedia:Garry_Kasparov} in any regular buffer as a link to Wikipedia, -with actions to open the Wikipedia page in eww or an external browser -or to save the URL of the page in the kill-ring. We can take advantage -of the actions that Embark has preconfigured for URLs, so all we need -to do is teach Embark that @samp{wikipedia:Garry_Kasparov} stands for the URL -@samp{https://en.wikipedia.org/wiki/Garry_Kasparov}. - -You can be as fancy as you want with the recognized syntax. Here, to -keep the example simple, I'll assume the link matches the regexp -@samp{wikipedia:[[:alnum:]_]+}. We will write a function that looks for a -match surrounding point, and returns a dotted list of the form @samp{'(url -URL-OF-THE-PAGE START . END)} where @samp{START} and @samp{END} are the buffer -positions bounding the target, and are used by Embark to highlight it -if you have @samp{embark-highlight-indicator} included in the list -@samp{embark-indicators}. (There are a couple of other options for the return -value of a target finder: the bounding positions are optional and a -single target finder is allowed to return multiple targets; see the -documentation for @samp{embark-target-finders} for details.) - -@lisp -(defun my-short-wikipedia-link () - "Target a link at point of the form wikipedia:Page_Name." - (save-excursion - (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) - (end (progn (skip-chars-forward "[:alnum:]_:") (point))) - (str (buffer-substring-no-properties start end))) - (save-match-data - (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) - `(url - ,(format "https://en.wikipedia.org/wiki/%s" - (match-string 1 str)) - ,start . ,end)))))) - -(add-to-list 'embark-target-finders 'my-short-wikipedia-link) -@end lisp - -@node How does Embark call the actions? -@chapter How does Embark call the actions? - -Embark actions are normal Emacs commands, that is, functions with an -interactive specification. In order to execute an action, Embark -calls the command with @samp{call-interactively}, so the command reads user -input exactly as if run directly by the user. For example the -command may open a minibuffer and read a string -(@samp{read-from-minibuffer}) or open a completion interface -(@samp{completing-read}). If this happens, Embark takes the target string -and inserts it automatically into the minibuffer, simulating user -input this way. After inserting the string, Embark exits the -minibuffer, submitting the input. (The immediate minibuffer exit can -be disabled for specific actions in order to allow editing the -input; this is done by adding the @samp{embark--allow-edit} function to the -appropriate entry of @samp{embark-target-injection-hooks}). Embark inserts -the target string at the first minibuffer opened by the action -command, and if the command happens to prompt the user for input -more than once, the user still interacts with the second and further -prompts in the normal fashion. Note that if a command does not -prompt the user for input in the minibuffer, Embark still allows you -to use it as an action, but of course, never inserts the target -anywhere. (There are plenty of examples in the default configuration -of commands that do not prompt the user bound to keys in the action -maps, most of the region actions, for instance.) - -This is how Embark manages to reuse normal commands as actions. The -mechanism allows you to use as Embark actions commands that were not -written with Embark in mind (and indeed almost all actions that are -bound by default in Embark's action keymaps are standard Emacs -commands). It also allows you to write new custom actions in such a -way that they are useful even without Embark. - -Staring from version 28.1, Emacs has a variable -@samp{y-or-n-p-use-read-key}, which when set to @samp{t} causes @samp{y-or-n-p} to use -@samp{read-key} instead of @samp{read-from-minibuffer}. Setting -@samp{y-or-n-p-use-read-key} to @samp{t} is recommended for Embark users because -it keeps Embark from attempting to insert the target at a @samp{y-or-n-p} -prompt, which would almost never be sensible. Also consider this as -a warning to structure your own action commands so that if they use -@samp{y-or-n-p}, they do so only after the prompting for the target. - -Here is a simple example illustrating the various ways of reading -input from the user mentioned above. Bind the following commands to -the @samp{embark-symbol-map} to be used as actions, then put the point on -some symbol and run them with @samp{embark-act}: - -@lisp -(defun example-action-command1 () - (interactive) - (message "The input was `%s'." (read-from-minibuffer "Input: "))) - -(defun example-action-command2 (arg input1 input2) - (interactive "P\nsInput 1: \nsInput 2: ") - (message "The first input %swas `%s', and the second was `%s'." - (if arg "truly " "") - input1 - input2)) - -(defun example-action-command3 () - (interactive) - (message "Your selection was `%s'." - (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) - -(defun example-action-command4 () - (interactive) - (message "I don't prompt you for input and thus ignore the target!")) - -(keymap-set embark-symbol-map "X 1" #'example-action-command1) -(keymap-set embark-symbol-map "X 2" #'example-action-command2) -(keymap-set embark-symbol-map "X 3" #'example-action-command3) -(keymap-set embark-symbol-map "X 4" #'example-action-command4) -@end lisp - -Also note that if you are using the key bindings to call actions, -you can pass prefix arguments to actions in the normal way. For -example, you can use @samp{C-u X2} with the above demonstration actions to -make the message printed by @samp{example-action-command2} more emphatic. -This ability to pass prefix arguments to actions is useful for some -actions in the default configuration, such as -@samp{embark-shell-command-on-buffer}. - -@menu -* Non-interactive functions as actions:: -@end menu - -@node Non-interactive functions as actions -@section Non-interactive functions as actions - -Alternatively, Embark does support one other type of action: a -non-interactive function of a single argument. The target is passed -as argument to the function. For example: - -@lisp -(defun example-action-function (target) - (message "The target was `%s'." target)) - -(keymap-set embark-symbol-map "X 4" #'example-action-function) -@end lisp - -Note that normally binding non-interactive functions in a keymap is -useless, since when attempting to run them using the key binding you -get an error message similar to ``Wrong type argument: commandp, -example-action-function''. In general it is more flexible to write -any new Embark actions as commands, that is, as interactive -functions, because that way you can also run them directly, without -Embark. But there are a couple of reasons to use non-interactive -functions as actions: - -@enumerate -@item -You may already have the function lying around, and it is -convenient to simply reuse it. - -@item -For command actions the targets can only be simple string, with -no text properties. For certain advanced uses you may want the -action to receive a string @emph{with} some text properties, or even a -non-string target. -@end enumerate - -@node Embark Marginalia and Consult -@chapter Embark, Marginalia and Consult - -Embark cooperates well with the @uref{https://github.com/minad/marginalia, Marginalia} and @uref{https://github.com/minad/consult, Consult} packages. -Neither of those packages is a dependency of Embark, but both are -highly recommended companions to Embark, for opposite reasons: -Marginalia greatly enhances Embark's usefulness, while Embark can help -enhance Consult. - -In the remainder of this section I'll explain what exactly Marginalia -does for Embark, and what Embark can do for Consult. - -@menu -* Marginalia:: -* Consult:: -@end menu - -@node Marginalia -@section Marginalia - -Embark comes with actions for symbols (commands, functions, variables -with actions such as finding the definition, looking up the -documentation, evaluating, etc.) in the @samp{embark-symbol-map} keymap, and -for packages (actions like install, delete, browse url, etc.) in the -@samp{embark-package-keymap}. - -Unfortunately Embark does not automatically offers you these keymaps -when relevant, because many built-in Emacs commands don't report -accurate category metadata. For example, a command like -@samp{describe-package}, which reads a package name from the minibuffer, -does not have metadata indicating this fact. - -In an earlier Embark version, there were functions to supply this -missing metadata, but they have been moved to Marginalia, which -augments many Emacs command to report accurate category metadata. -Simply activating @samp{marginalia-mode} allows Embark to offer you the -package and symbol actions when appropriate again. Candidate -annotations in the Embark collect buffer are also provided by the -Marginalia package: - -@itemize -@item -If you install Marginalia and activate @samp{marginalia-mode}, Embark -Collect buffers will use the Marginalia annotations automatically. - -@item -If you don't install Marginalia, you will see only the annotations -that come with Emacs (such as key bindings in @samp{M-x}, or the unicode -characters in @samp{C-x 8 RET}). -@end itemize - -@node Consult -@section Consult - -The excellent Consult package provides many commands that use -minibuffer completion, via the @samp{completing-read} function; plenty of its -commands can be considered enhanced versions of built-in Emacs -commands, and some are completely new functionality. One common -enhancement provided in all commands for which it makes sense is -preview functionality, for example @samp{consult-buffer} will show you a -quick preview of a buffer before you actually switch to it. - -If you use both Consult and Embark you should install the -@samp{embark-consult} package which provides integration between the two. It -provides exporters for several Consult commands and also tweaks the -behavior of many Consult commands when used as actions with @samp{embark-act} -in subtle ways that you may not even notice, but make for a smoother -experience. You need only install it to get these benefits: Embark -will automatically load it after Consult if found. - -The @samp{embark-consult} package provides the following exporters: - -@itemize -@item -You can use @samp{embark-export} from @samp{consult-line}, @samp{consult-outline}, or -@samp{consult-mark} to obtain an @samp{occur-mode} buffer. As with the built-in -@samp{occur} command you use that buffer to jump to a match and after that, -you can then use @samp{next-error} and @samp{previous-error} to navigate to other -matches. You can also press @samp{e} to activate @samp{occur-edit-mode} and edit -the matches in place! - -@item -You can export from any of the Consult asynchronous search commands, -@samp{consult-grep}, @samp{consult-git-grep}, or @samp{consult-ripgrep} to get a -@samp{grep-mode} buffer. Here too you can use @samp{next-error} and @samp{previous-error} -to navigate among matches, and, if you install the @uref{http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el , wgrep} package, -you can use it to edit the matches in place. -@end itemize - -In both cases, pressing @samp{g} will rerun the Consult command you had -exported from and re-enter the input you had typed (which is similar -to reverting but a little more flexible). You can then proceed to -re-export if that's what you want, but you can also edit the input -changing the search terms or simply cancel if you see you are done -with that search. - -The @samp{embark-consult} also contains some candidates collectors that allow -you to run @samp{embark-live} to get a live-updating table of contents for -your buffer: - -@itemize -@item -@samp{embark-consult-outline-candidates} produces the outline headings of -the current buffer, using @samp{consult-outline}. -@item -@samp{embark-consult-imenu-candidates} produces the imenu items of -the current buffer, using @samp{consult-imenu}. -@item -@samp{embark-consult-imenu-or-outline-candidates} is a simple combination -of the two previous functions: it produces imenu items in buffers -deriving from @samp{prog-mode} and otherwise outline headings. -@end itemize - -The way to configure @samp{embark-live} (or @samp{embark-collect} and @samp{embark-export} -for that matter) to use one of these function is to add it at the end -of the @samp{embark-candidate-collectors} list. The @samp{embark-consult} package by -default adds the last one, which seems to be the most sensible -default. - -Besides those exporters and candidate collectors, the @samp{embark-consult} -package provides many subtle tweaks and small integrations between -Embark and Consult. Some examples are: - -@itemize -@item -When used as actions, the asynchronous search commands will search -only the files associated to the targets: if the targets @emph{are} files, -it searches those files; for buffers it will search either the -associated file if there is one, else all files in the buffer's -@samp{default-directory}; for bookmarks it will search the file they point -to, same for Emacs Lisp libraries. This is particularly powerful -when using @samp{embark-act-all} to act on multiple files at once, for -example you can use @samp{consult-find} to search among file @emph{names} and then -@samp{embark-act-all} and @samp{consult-grep} to search within the matching files. - -@itemize -@item -For all other target types, those that do not have a sensible -notion of associated file, a Consult search command (asynchronous -or not) will search for the text of the target but leave the -minibuffer open so you can interact with the Consult command. -@end itemize - -@item -@samp{consult-imenu} will search for the target and take you directly to -the location if it matches a unique imenu entry, otherwise it will -leave the minibuffer open so you can navigate among the matches. -@end itemize - -@node Related Packages -@chapter Related Packages - -There are several packages that offer functionality similar -to Embark's. - -@table @asis -@item Acting on minibuffer completion candidates -The popular Ivy and -Helm packages have support for acting on the completion candidates -of commands written using their APIs, and there is an extensive -ecosystem of packages meant for Helm and for Ivy (the Ivy ones -usually have ``counsel'' in the name) providing commands and -appropriate actions. -@item Acting on things at point -The built-in @samp{context-menu-mode} provides -a mouse-driven context-sensitive configurable menu. The @samp{do-at-point} -package by Philip Kaludercic (available on GNU ELPA), on the other -hand is keyboard-driven. -@item Collecting completion candidates into a buffer -The Ivy package -has the command @samp{ivy-occur} which is similar to @samp{embark-collect}. As -with Ivy actions, @samp{ivy-occur} only works for commands written using -the Ivy API@. -@end table - -@node Resources -@chapter Resources - -If you want to learn more about how others have used Embark here are -some links to read: - -@itemize -@item -@uref{https://karthinks.com/software/fifteen-ways-to-use-embark/, Fifteen ways to use Embark}, a blog post by Karthik Chikmagalur. -@item -@uref{https://protesilaos.com/dotemacs/, Protesilaos Stavrou's dotemacs}, look for the section called -``Extended minibuffer actions and more (embark.el and -prot-embark.el)'' -@end itemize - -And some videos to watch: - -@itemize -@item -@uref{https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/, Embark and my extras} by Protesilaos Stavrou. -@item -@uref{https://youtu.be/qpoQiiinCtY, Embark -- Key features and tweaks} by Raoul Comninos on the -Emacs-Elements YouTube channel. -@item -@uref{https://youtu.be/WsxXr1ncukY, Livestreamed: Adding an Embark context action to send a stream -message} by Sacha Chua. -@item -@uref{https://youtu.be/qk2Is_sC8Lk, System Crafters Live! - The Many Uses of Embark} by David Wilson. -@item -@uref{https://youtu.be/5ffb2at2d7w, Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark} by -Mike Zamansky. -@end itemize - -@node Contributions -@chapter Contributions - -Contributions to Embark are very welcome. There is a @uref{https://github.com/oantolin/embark/issues/95, wish list} for -actions, target finders, candidate collectors and exporters. For other -ideas you have for Embark, feel free to open an issue on the @uref{https://github.com/oantolin/embark/issues, issue -tracker}. Any neat configuration tricks you find might be a good fit -for the @uref{https://github.com/oantolin/embark/wiki, wiki}. - -Code contributions are very welcome too, but since Embark is now on -GNU ELPA, copyright assignment to the FSF is required before you can -contribute code. - -@node Acknowledgments -@chapter Acknowledgments - -While I, Omar Antolín Camarena, have written most of the Embark code -and remain very stubborn about some of the design decisions, Embark -has received substantial help from a number of other people which this -document has neglected to mention for far too long. In particular, -Daniel Mendler has been absolutely invaluable, implementing several -important features, and providing a lot of useful advice. - -Code contributions: - -@itemize -@item -@uref{https://github.com/minad, Daniel Mendler} -@item -@uref{https://github.com/clemera/, Clemens Radermacher} -@item -@uref{https://codeberg.org/jao/, José Antonio Ortega Ruiz} -@item -@uref{https://github.com/iyefrat, Itai Y@. Efrat} -@item -@uref{https://github.com/a13, a13} -@item -@uref{https://github.com/jakanakaevangeli, jakanakaevangeli} -@item -@uref{https://github.com/mihakam, mihakam} -@item -@uref{https://github.com/leungbk, Brian Leung} -@item -@uref{https://github.com/karthink, Karthik Chikmagalur} -@item -@uref{https://github.com/roshanshariff, Roshan Shariff} -@item -@uref{https://github.com/condy0919, condy0919} -@item -@uref{https://github.com/DamienCassou, Damien Cassou} -@item -@uref{https://github.com/JimDBh, JimDBh} -@end itemize - -Advice and useful discussions: - -@itemize -@item -@uref{https://github.com/minad, Daniel Mendler} -@item -@uref{https://gitlab.com/protesilaos/, Protesilaos Stavrou} -@item -@uref{https://github.com/clemera/, Clemens Radermacher} -@item -@uref{https://github.com/hmelman/, Howard Melman} -@item -@uref{https://github.com/astoff, Augusto Stoffel} -@item -@uref{https://github.com/bdarcus, Bruce d'Arcus} -@item -@uref{https://github.com/jdtsmith, JD Smith} -@item -@uref{https://github.com/karthink, Karthik Chikmagalur} -@item -@uref{https://github.com/jakanakaevangeli, jakanakaevangeli} -@item -@uref{https://github.com/iyefrat, Itai Y@. Efrat} -@item -@uref{https://github.com/mohkale, Mohsin Kaleem} -@end itemize - -@bye blob - 9454dcdaf042e9b6a204910f906b51b857614d6b (mode 644) blob + /dev/null --- elpa/embark-1.1.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2024-04-19T11:05:03+0200 using EDDSA \ No newline at end of file blob - 6d22de33f570450fc5a630a3b4d331ce4aeccc53 (mode 644) blob + /dev/null --- elpa/embark-consult-1.1/.dir-locals.el +++ /dev/null @@ -1,6 +0,0 @@ -;;; Directory Local Variables -;;; For more information see (info "(emacs) Directory Variables") - -((emacs-lisp-mode - (show-trailing-whitespace . t) - (indent-tabs-mode . nil))) blob - 7a694c9699a986b9adf1f6cb8a18a6e923e47ed9 (mode 644) blob + /dev/null --- elpa/embark-consult-1.1/.elpaignore +++ /dev/null @@ -1 +0,0 @@ -LICENSE \ No newline at end of file blob - 058be1ce20770245b36f435560955810398007cc (mode 644) blob + /dev/null --- elpa/embark-consult-1.1/CHANGELOG.org +++ /dev/null @@ -1,105 +0,0 @@ -#+title: Embark changelog - -* Version 1.1 (2024-04-18) -- The =embark-consult= package contains a new exporter for - =consult-location= targets (produced by several =consult= commands such - as =consult-line=), which exports to a grep mode buffer. Users wishing - to use the new grep mode exporter can use the following - configuration: - #+begin_src emacs-lisp - (setf (alist-get 'consult-location embark-exporters-alist) - #'embark-consult-export-location-grep) - #+end_src - The main reason for adding the new exporter is that users of the - =wgrep= package will be able to make use of a feature that =wgrep= has - and the built-in =occur-edit-mode= lacks: when editing search results - you can add new lines to a result location. There are also some - disadvantages of grep mode compared to occur mode (which is why the - previously existing occur mode exporter continues to be the - default): (1) =wgrep= is a third party package while =occur-edit-mode= - is built-in; (2) occur mode buffers can list lines in any kind of - buffer, but grep mode and =wgrep= are meant for lines of files - exclusively. -* Version 1.0 (2023-12-08) -- You can now use around action hooks with multitarget actions (that - you couldn't previously was an oversight). -- Users of the =embark-consult= package can now use consult async search - commands such as =consult-grep= as multitarget actions (through - =embark-act-all=) to search a list of files. For example, you can use - =consult-find= to search among file /names/ and once you have the - relevant files in the minibuffer, you can use =embark-act-all= to - search for some text in those files. When acting on buffers consult - async search commands will search the associated file if there is - one, or else the =default-directory= of the buffer. -- =embark-bindings= and similar commands now show definition of keyboard - macros. -- =embark-org= now recognizes Org links in non-org buffers. -- Now pressing RET in an =embark-collect= on a selection made by - using =embark-select= in a normal buffer will take you to the location - each target was collected from. -- Some functions renamed for greater consistency (these functions are - unlikely to be referred to in user's configuration): - - =embark-target-completion-at-point= → =embark-target-completion-list-candidate= - - =embark-target-top-minibuffer-completion= → =embark-target-top-minibuffer-candidate= - - =embark-completions-buffer-candidates= → =embark-completion-list-candidates= -* Version 0.23 (2023-09-19) -- Added a mode line indicator showing the number of selected targets in - the current buffer (contributed by @minad, thanks!) -- Now =embark-select= can also be called as a top-level command, from - outside =embark-act=. When called that way, it will select the first - target at point. -- =embark-org= now has support for acting on references to org headings - in other buffers, by jumping to the heading first and then running - the action. One source of references to org headings in other - buffers are agenda views: each agenda item is such a reference. But - this feature also supports some great third party commands which - produce references to org headings, such as =org-ql-find= from the - =org-ql= package or =consult-org-heading= from =consult=. -- Renamed =embark-isearch= to =embark-isearch-forward= and added - =embark-isearch-backward=. -- =embark-become= now removes any invisible text from the minibuffer - input on the grounds that users probably expect the target command - to receive exactly the input they can see. -- The meaning of the prefix argument in =embark-bindings= has flipped: - now by default global key bindings are excluded and you can use =C-u= - to include them. -- If any candidate in an embark-collect buffer contains a newline, - then candidates will be separated by horizontal lines. This is handy - for the kill-ring, which you can browse by calling =embark-collect= - from =yank-pop=. -* Version 0.22.1 (2023-04-20) -** New feature: selections -Now users can select several targets to make an ad hoc collection. The -commands =embark-act-all=, =embark-export= and =embark-collect= will act on -the selection if it is non-empty. To select or deselect a target use -the =embark-select= action (bound to =SPC= in =embark-general-map=). If you -have some targets selected, then using =embark-select= through -=embark-act-all= will deselect them. - -Before this change the Embark Collect buffers had their own -implementation of selections which has been removed. This is how to -translate the old bindings to the new feature (which is available in -all buffers, not just Embark Collect buffers!): - -| Task | Old binding | New binding | -|--------------------+-------------+---------------| -| Mark a candidate | m | a SPC | -| Unmark a candidate | u | a SPC | -| Unmark all | U | A SPC | -| Mark all [1] | t | A SPC | -| Toggle all marks | t | not available | - -[1] Marking all candidates (with either the old =t= or the new =A SPC=) -requires that there are no marked candidates to begin with. - -In order to make room for the binding of =embark-select= to -=SPC=, some other key bindings were moved: - -- =mark= in =embark-general-map= was moved to =C-SPC=. -- =outline-mark-subtree= in =embark-heading-map= was moved to =C-SPC=. -- =whitespace-cleanup-region= in =embark-region-map= was moved to =F=. - -* Version 0.21.1 (2020-01-30) -- Finally started this changelog on 2023-04-20. Known issues with the - changelog: it started very late, the first entry is not very - informative. blob - 0ac8be62137458349c1935f910593e8d6529a826 (mode 644) blob + /dev/null --- elpa/embark-consult-1.1/README-elpa +++ /dev/null @@ -1,1471 +0,0 @@ - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - EMBARK: EMACS MINI-BUFFER ACTIONS ROOTED IN - KEYMAPS - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - - - - -1 Overview -══════════ - - Embark makes it easy to choose a command to run based on what is near - point, both during a minibuffer completion session (in a way familiar - to Helm or Counsel users) and in normal buffers. Bind the command - `embark-act' to a key and it acts like prefix-key for a keymap of - /actions/ (commands) relevant to the /target/ around point. With point - on an URL in a buffer you can open the URL in a browser or eww or - download the file it points to. If while switching buffers you spot an - old one, you can kill it right there and continue to select another. - Embark comes preconfigured with over a hundred actions for common - types of targets such as files, buffers, identifiers, s-expressions, - sentences; and it is easy to add more actions and more target types. - Embark can also collect all the candidates in a minibuffer to an - occur-like buffer or export them to a buffer in a major-mode specific - to the type of candidates, such as dired for a set of files, ibuffer - for a set of buffers, or customize for a set of variables. - - -1.1 Acting on targets -───────────────────── - - You can think of `embark-act' as a keyboard-based version of a - right-click contextual menu. The `embark-act' command (which you - should bind to a convenient key), acts as a prefix for a keymap - offering you relevant /actions/ to use on a /target/ determined by the - context: - - • In the minibuffer, the target is the current top completion - candidate. - • In the `*Completions*' buffer the target is the completion at point. - • In a regular buffer, the target is the region if active, or else the - file, symbol, URL, s-expression or defun at point. - - Multiple targets can be present at the same location and you can cycle - between them by repeating the `embark-act' key binding. The type of - actions offered depend on the type of the target. Here is a sample of - a few of the actions offered in the default configuration: - - • For files you get offered actions like deleting, copying, renaming, - visiting in another window, running a shell command on the file, - etc. - • For buffers the actions include switching to or killing the buffer. - • For package names the actions include installing, removing or - visiting the homepage. - • For Emacs Lisp symbols the actions include finding the definition, - looking up documentation, evaluating (which for a variable - immediately shows the value, but for a function lets you pass it - some arguments first). There are some actions specific to variables, - such as setting the value directly or though the customize system, - and some actions specific to commands, such as binding it to a key. - - By default when you use `embark-act' if you don't immediately select - an action, after a short delay Embark will pop up a buffer showing a - list of actions and their corresponding key bindings. If you are using - `embark-act' outside the minibuffer, Embark will also highlight the - current target. These behaviors are configurable via the variable - `embark-indicators'. Instead of selecting an action via its key - binding, you can select it by name with completion by typing `C-h' - after `embark-act'. - - Everything is easily configurable: determining the current target, - classifying it, and deciding which actions are offered for each type - in the classification. The above introduction just mentions part of - the default configuration. - - Configuring which actions are offered for a type is particularly easy - and requires no programming: the variable `embark-keymap-alist' - associates target types with variables containing keymaps, and those - keymaps containing bindings for the actions. (To examine the available - categories and their associated keymaps, you can use `C-h v - embark-keymap-alist' or customize that variable.) For example, in the - default configuration the type `file' is associated with the symbol - `embark-file-map'. That symbol names a keymap with single-letter key - bindings for common Emacs file commands, for instance `c' is bound to - `copy-file'. This means that if you are in the minibuffer after - running a command that prompts for a file, such as `find-file' or - `rename-file', you can copy a file by running `embark-act' and then - pressing `c'. - - These action keymaps are very convenient but not strictly necessary - when using `embark-act': you can use any command that reads from the - minibuffer as an action and the target of the action will be inserted - at the first minibuffer prompt. After running `embark-act' all of your - key bindings and even `execute-extended-command' can be used to run a - command. For example, if you want to replace all occurrences of the - symbol at point, just use `M-%' as the action, there is no need to - bind `query-replace' in one of Embark's keymaps. Also, those action - keymaps are normal Emacs keymaps and you should feel free to bind in - them whatever commands you find useful as actions and want to be - available through convenient bindings. - - The actions in `embark-general-map' are available no matter what type - of completion you are in the middle of. By default this includes - bindings to save the current candidate in the kill ring and to insert - the current candidate in the previously selected buffer (the buffer - that was current when you executed a command that opened up the - minibuffer). - - Emacs's minibuffer completion system includes metadata indicating the - /category/ of what is being completed. For example, `find-file''s - metadata indicates a category of `file' and `switch-to-buffer''s - metadata indicates a category of `buffer'. Embark has the related - notion of the /type/ of a target for actions, and by default when - category metadata is present it is taken to be the type of minibuffer - completion candidates when used as targets. Emacs commands often do - not set useful category metadata so the [Marginalia] package, which - supplies this missing metadata, is highly recommended for use with - Embark. - - Embark's default configuration has actions for the following target - types: files, buffers, symbols, packages, URLs, bookmarks, and as a - somewhat special case, actions for when the region is active. You can - read about the [default actions and their key bindings] on the GitHub - project wiki. - - -[Marginalia] - -[default actions and their key bindings] - - - -1.2 The default action on a target -────────────────────────────────── - - Embark has a notion of default action for a target: - - • If the target is a minibuffer completion candidate, then the default - action is whatever command opened the minibuffer in the first place. - For example if you run `kill-buffer', then the default action will - be to kill buffers. - • If the target comes from a regular buffer (i.e., not a minibuffer), - then the default action is whatever is bound to `RET' in the keymap - of actions for that type of target. For example, in Embark's default - configuration for a URL found at point the default action is - `browse-url', because `RET' is bound to `browse-url' in the - `embark-url-map' keymap. - - To run the default action you can press `RET' after running - `embark-act'. Note that if there are several different targets at a - given location, each has its own default action, so first cycle to the - target you want and then press `RET' to run the corresponding default - action. - - There is also `embark-dwim' which runs the default action for the - first target found. It's pretty handy in non-minibuffer buffers: with - Embark's default configuration it will: - - • Open the file at point. - • Open the URL at point in a web browser (using the `browse-url' - command). - • Compose a new email to the email address at point. - • In an Emacs Lisp buffer, if point is on an opening parenthesis or - right after a closing one, it will evaluate the corresponding - expression. - • Go to the definition of an Emacs Lisp function, variable or macro at - point. - • Find the file corresponding to an Emacs Lisp library at point. - - -1.3 Working with sets of possible targets -───────────────────────────────────────── - - Besides acting individually on targets, Embark lets you work - collectively on a set of target /candidates/. For example, while you - are in the minibuffer the candidates are simply the possible - completions of your input. Embark provides three main commands to work - on candidate sets: - - • The `embark-act-all' command runs the same action on each of the - current candidates. It is just like using `embark-act' on each - candidate in turn. (Because you can easily act on many more - candidates than you meant to, by default Embark asks you to confirm - uses of `embark-act-all'; you can turn this off by setting the user - option `embark-confirm-act-all' to `nil'.) - - • The `embark-collect' command produces a buffer listing all the - current candidates, for you to peruse and run actions on at your - leisure. The candidates are displayed as a list showing additional - annotations. If any of the candidates contain newlines, then - horizontal lines are used to separate candidates. - - The Embark Collect buffer is somewhat "dired-like": you can select - and deselect candidates through `embark-select' (available as an - action in `embark-act', bound to `SPC'; but you could also give it a - global key binding). In an Embark Collect buffer `embark-act' is - bound to `a' and `embark-act-all' is bound to `A'; `embark-act-all' - will act on all currently marked candidates if there any, and will - act on all candidates if none are marked. In particular, this means - that `a SPC' will toggle whether the candidate at point is selected, - and `A SPC' will select all candidates if none are selected, or - deselect all selected candidates if there are some. - - • The `embark-export' command tries to open a buffer in an appropriate - major mode for the set of candidates. If the candidates are files - export produces a Dired buffer; if they are buffers, you get an - Ibuffer buffer; and if they are packages you get a buffer in package - menu mode. - - If you use the grepping commands from the [Consult] package, - `consult-grep', `consult-git-grep' or `consult-ripgrep', then you - should install the `embark-consult' package, which adds support for - exporting a list of grep results to an honest grep-mode buffer, on - which you can even use [wgrep] if you wish. - - When in doubt choosing between exporting and collecting, a good rule - of thumb is to always prefer `embark-export' since when an exporter to - a special major mode is available for a given type of target, it will - be more featureful than an Embark collect buffer, and if no such - exporter is configured the `embark-export' command falls back to the - generic `embark-collect'. - - These commands are always available as "actions" (although they do not - act on just the current target but on all candidates) for `embark-act' - and are bound to `A', `S' (for "snapshot"), and `E', respectively, in - `embark-general-map'. This means that you do not have to bind your own - key bindings for these (although you can, of course!), just a key - binding for `embark-act'. - - In Embark Collect or Embark Export buffers that were obtained by - running `embark-collect' or `embark-export' from within a minibuffer - completion session, `g' is bound to a command that restarts the - completion session, that is, the command that opened the minibuffer is - run again and the minibuffer contents restored. You can then interact - normally with the command, perhaps editing the minibuffer contents, - and, if you wish, you can rerun `embark-collect' or `embark-export' to - get an updated buffer. - - -[Consult] - -[wgrep] - -1.3.1 Selecting some targets to make an ad hoc candidate set -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - The commands for working with sets of candidates just described, - namely `embark-act-all', `embark-export' and `embark-collect' by - default work with all candidates defined in the current context. For - example, in the minibuffer they operate on all currently completion - candidates, or in a dired buffer they work on all marked files (or all - files if none are marked). Embark also has a notion of /selection/, - where you can accumulate an ad hoc list of targets for these commands - to work on. - - The selection is controlled by using the `embark-select' action, bound - to `SPC' in `embark-general-map' so that it is always available (you - can also give `embark-select' a global key binding if you wish; when - called directly, not as an action for `embark-act', it will select the - first target at point). Calling this action on a target toggles its - membership in the current buffer's Embark selection; that is, it adds - it to selection if not selected and removes it from the selection if - it was selected. Whenever the selection for a buffer is non-empty, the - commands `embark-act-all', `embark-export' and `embark-collect' will - act on the selection. - - To deselect all selected targets, you can use the `embark-select' - action through `embark-act-all', since this will run `embark-select' - on each member of the current selection. Similarly if no targets are - selected and you are in a minibuffer completion session, running - `embark-select' from `embark-act-all' will select all the current - completion candidates. - - By default, whenever some targets are selected in the current buffer, - a count of selected targets appears in the mode line. This can be - turned off or customized through the `embark-selection-indicator' user - option. - - The selection functionality is supported in every buffer: - - • In the minibuffer this gives a convenient way to act on several - completion candidates that don't follow any simple pattern: just go - through the completions selecting the ones you want, then use - `embark-act-all'. For example, you could attach several files at - once to an email. - • For Embark Collect buffers this functionality enables a dired-like - workflow, in which you mark various candidates and apply an action - to all at once. (It supersedes a previous ad hoc dired-like - interface that was implemented only in Embark Collect buffers, with - a slightly different interface.) - • In a eww buffer you could use this to select various links you wish - to follow up on, and then collect them into a buffer. Similarly, - while reading Emacs's info manual you could select some symbols you - want to read more about and export them to an `apropos-mode' buffer. - • You can use selections in regular text or programming buffers to do - complex editing operations. For example, if you have three - paragraphs scattered over a file and you want to bring them - together, you can select each one, insert them all somewhere and - finally delete all of them (from their original locations). - - -1.3.2 `embark-live' a live-updating variant of `embark-collect' -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - Finally, there is also an `embark-live' variant of the - `embark-collect' command which automatically updates the collection - after each change in the source buffer. Users of a completion UI that - automatically updates and displays the candidate list (such as - Vertico, Icomplete, Fido-mode, or MCT) will probably not want to use - `embark-live' from the minibuffer as they will then have two live - updating displays of the completion candidates! - - A more likely use of `embark-live' is to be called from a regular - buffer to display a sort of live updating "table of contents" for the - buffer. This depends on having appropriate candidate collectors - configured in `embark-candidate-collectors'. There are not many in - Embark's default configuration, but you can try this experiment: open - a dired buffer in a directory that has very many files, mark a few, - and run `embark-live'. You'll get an Embark Collect buffer containing - only the marked files, which updates as you mark or unmark files in - dired. To make `embark-live' genuinely useful other candidate - collectors are required. The `embark-consult' package (documented - near the end of this manual) contains a few: one for imenu items and - one for outline headings as used by `outline-minor-mode'. Those - collectors really do give `embark-live' a table-of-contents feel. - - -1.4 Switching to a different command without losing what you've typed -───────────────────────────────────────────────────────────────────── - - Embark also has the `embark-become' command which is useful for when - you run a command, start typing at the minibuffer and realize you - meant a different command. The most common case for me is that I run - `switch-to-buffer', start typing a buffer name and realize I haven't - opened the file I had in mind yet! I'll use this situation as a - running example to illustrate `embark-become'. When this happens I - can, of course, press `C-g' and then run `find-file' and open the - file, but this requires retyping the portion of the file name you - already typed. This process can be streamlined with `embark-become': - while still in the `switch-to-buffer' you can run `embark-become' and - effectively make the `switch-to-buffer' command become `find-file' for - this run. - - You can bind `embark-become' to a key in `minibuffer-local-map', but - it is also available as an action under the letter `B' (uppercase), so - you don't need a binding if you already have one for `embark-act'. So, - assuming I have `embark-act' bound to, say, `C-.', once I realize I - haven't open the file I can type `C-. B C-x C-f' to have - `switch-to-buffer' become `find-file' without losing what I have - already typed in the minibuffer. - - But for even more convenience, `embark-become' offers shorter key - bindings for commands you are likely to want the current command to - become. When you use `embark-become' it looks for the current command - in all keymaps named in the list `embark-become-keymaps' and then - activates all keymaps that contain it. For example, the default value - of `embark-become-keymaps' contains a keymap - `embark-become-file+buffer-map' with bindings for several commands - related to files and buffers, in particular, it binds - `switch-to-buffer' to `b' and `find-file' to `f'. So when I - accidentally try to switch to a buffer for a file I haven't opened - yet, `embark-become' finds that the command I ran, `switch-to-buffer', - is in the keymap `embark-become-file+buffer-map', so it activates that - keymap (and any others that also contain a binding for - `switch-to-buffer'). The end result is that I can type `C-. B f' to - switch to `find-file'. - - -2 Quick start -═════════════ - - The easiest way to install Embark is from GNU ELPA, just run `M-x - package-install RET embark RET'. (It is also available on MELPA.) It - is highly recommended to also install [Marginalia] (also available on - GNU ELPA), so that Embark can offer you preconfigured actions in more - contexts. For `use-package' users, the following is a very reasonable - starting configuration: - - ┌──── - │ (use-package marginalia - │ :ensure t - │ :config - │ (marginalia-mode)) - │ - │ (use-package embark - │ :ensure t - │ - │ :bind - │ (("C-." . embark-act) ;; pick some comfortable binding - │ ("C-;" . embark-dwim) ;; good alternative: M-. - │ ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' - │ - │ :init - │ - │ ;; Optionally replace the key help with a completing-read interface - │ (setq prefix-help-command #'embark-prefix-help-command) - │ - │ ;; Show the Embark target at point via Eldoc. You may adjust the - │ ;; Eldoc strategy, if you want to see the documentation from - │ ;; multiple providers. Beware that using this can be a little - │ ;; jarring since the message shown in the minibuffer can be more - │ ;; than one line, causing the modeline to move up and down: - │ - │ ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) - │ ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) - │ - │ :config - │ - │ ;; Hide the mode line of the Embark live/completions buffers - │ (add-to-list 'display-buffer-alist - │ '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" - │ nil - │ (window-parameters (mode-line-format . none))))) - │ - │ ;; Consult users will also want the embark-consult package. - │ (use-package embark-consult - │ :ensure t ; only need to install it, embark loads it after consult if found - │ :hook - │ (embark-collect-mode . consult-preview-at-point-mode)) - └──── - - About the suggested key bindings for `embark-act' and `embark-dwim': - • Those key bindings are unlikely to work in the terminal, but - terminal users are probably well aware of this and will know to - select different bindings. - • The suggested `C-.' binding is used by default in (at least some - installations of) GNOME to input emojis, and Emacs doesn't even get - a chance to respond to the binding. You can select a different key - binding for `embark-act' or use `ibus-setup' to change the shortcut - for emoji insertion (Emacs 29 will likely use `C-x 8 e e', in case - you want to set the same one system-wide). - • The suggested alternative of `M-.' for `embark-dwim' is bound by - default to `xref-find-definitions'. That is a very useful command - but overwriting it with `embark-dwim' is sensible since in Embark's - default configuration, `embark-dwim' will also find the definition - of the identifier at point. (Note that `xref-find-definitions' with - a prefix argument prompts you for an identifier, `embark-dwim' does - not cover this case). - - Other Embark commands such as `embark-act-all', `embark-become', - `embark-collect', and `embark-export' can be run through `embark-act' - as actions bound to `A', `B', `S' (for "snapshot"), and `E' - respectively, and thus don't really need a dedicated key binding, but - feel free to bind them directly if you so wish. If you do choose to - bind them directly, you'll probably want to bind them in - `minibuffer-local-map', since they are most useful in the minibuffer - (in fact, `embark-become' only works in the minibuffer). - - The command `embark-dwim' executes the default action at - point. Another good keybinding for `embark-dwim' is `M-.' since - `embark-dwim' acts like `xref-find-definitions' on the symbol at - point. `C-.' can be seen as a right-click context menu at point and - `M-.' acts like left-click. The keybindings are mnemonic, both act at - the point (`.'). - - Embark needs to know what your minibuffer completion system considers - to be the list of candidates and which one is the current candidate. - Embark works out of the box if you use Emacs's default tab completion, - the built-in `icomplete-mode' or `fido-mode', or the third-party - packages [Vertico] or [Ivy]. - - If you are a [Helm] or [Ivy] user you are unlikely to want Embark - since those packages include comprehensive functionality for acting on - minibuffer completion candidates. (Embark does come with Ivy - integration despite this.) - - -[Marginalia] - -[Vertico] - -[Ivy] - -[Helm] - - -3 Advanced configuration -════════════════════════ - -3.1 Showing information about available targets and actions -─────────────────────────────────────────────────────────── - - By default, if you run `embark-act' and do not immediately select an - action, after a short delay Embark will pop up a buffer called - `*Embark Actions*' containing a list of available actions with their - key bindings. You can scroll that buffer with the mouse of with the - usual commands `scroll-other-window' and `scroll-other-window-down' - (bound by default to `C-M-v' and `C-M-S-v'). - - That functionality is provided by the `embark-mixed-indicator', but - Embark has other indicators that can provide information about the - target and its type, what other targets you can cycle to, and which - actions have key bindings in the action map for the current type of - target. Any number of indicators can be active at once and the user - option `embark-indicators' should be set to a list of the desired - indicators. - - Embark comes with the following indicators: - - • `embark-minimal-indicator': shows a messages in the echo area or - minibuffer prompt showing the current target and the types of all - targets starting with the current one. - - • `embark-highlight-indicator': highlights the target at point; on by - default. - - • `embark-verbose-indicator': displays a table of actions and their - key bindings in a buffer; this is not on by default, in favor of the - mixed indicator described next. - - • `embark-mixed-indicator': starts out by behaving as the minimal - indicator but after a short delay acts as the verbose indicator; - this is on by default. - - • `embark-isearch-highlight-indicator': this only does something when - the current target is the symbol at point, in which case it lazily - highlights all occurrences of that symbol in the current buffer, - like isearch; also on by default. - - Users of the popular [which-key] package may prefer to use the - `embark-which-key-indicator' from the [Embark wiki]. Just copy its - definition from the wiki into your configuration and customize the - `embark-indicators' user option to exclude the mixed and verbose - indicators and to include `embark-which-key-indicator'. - - If you use [Vertico], there is an even easier way to get a - `which-key'-like display that also lets you use completion to narrow - down the list of alternatives, described at the end of the next - section. - - -[which-key] - -[Embark wiki] - - -[Vertico] - - -3.2 Selecting commands via completions instead of key bindings -────────────────────────────────────────────────────────────── - - As an alternative to reading the list of actions in the verbose or - mixed indicators (see the previous section for a description of - these), you can press the `embark-help-key', which is `C-h' by default - (but you may prefer `?' to free up `C-h' for use as a prefix) after - running `embark-act'. Pressing the help key will prompt you for the - name of an action with completion (but feel free to enter a command - that is not among the offered candidates!), and will also remind you - of the key bindings. You can press `embark-keymap-prompter-key', which - is `@' by default, at the prompt and then one of the key bindings to - enter the name of the corresponding action. - - You may think that with the `*Embark Actions*' buffer popping up to - remind you of the key bindings you'd never want to use completion to - select an action by name, but personally I find that typing a small - portion of the action name to narrow down the list of candidates feels - significantly faster than visually scanning the entire list of - actions. - - If you find you prefer selecting actions that way, you can configure - embark to always prompt you for actions by setting the variable - `embark-prompter' to `embark-completing-read-prompter'. - - On the other hand, you may wish to continue using key bindings for the - actions you perform most often, and to use completion only to explore - what further actions are available or when you've forgotten a key - binding. In that case, you may prefer to use the minimal indicator, - which does not pop-up an `*Embark Actions*' buffer at all, and to use - the `embark-help-key' whenever you need help. This unobtrusive setup - is achieved with the following configuration: - - ┌──── - │ (setq embark-indicators - │ '(embark-minimal-indicator ; default is embark-mixed-indicator - │ embark-highlight-indicator - │ embark-isearch-highlight-indicator)) - └──── - - [Vertico] users may wish to configure a grid display for the actions - and key-bindings, reminiscent of the popular package [which-key], but, - of course, enhanced by the use of completion to narrow the list of - commands. In order to get the grid display, put the following in your - Vertico configuration: - - ┌──── - │ (add-to-list 'vertico-multiform-categories '(embark-keybinding grid)) - │ (vertico-multiform-mode) - └──── - - This will make the available keys be shown in a compact grid like in - `which-key'. The `vertico-multiform-mode' also enables keys such as - `M-V', `M-G', `M-B', and `M-U' for manually switching between layouts - in Vertico buffers. - - -[Vertico] - -[which-key] - -3.2.1 Selecting commands via completion outside of Embark -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - If you like this completion interface for exploring key bindings for - Embark actions, you may want to use it elsewhere in Emacs. You can use - Embark's completion-based command prompter to list: - - • key bindings under a prefix, - • local key bindings, or - • all key bindings. - - To use it for key bindings under a prefix (you can use this to replace - the `which-key' package, for example), use this configuration: - - ┌──── - │ (setq prefix-help-command #'embark-prefix-help-command) - └──── - - Now, when you have started on a prefix sequence such as `C-x' or - `C-c', pressing `C-h' will bring up the Embark version of the built-in - `prefix-help-command', which will list the keys under that prefix and - their bindings, and lets you select the one you wanted with - completion, or by key binding if you press - `embark-keymap-prompter-key'. - - To list local or global key bindings, use the command - `embark-bindings'. You can bind that to `C-h b', which is the default - key binding for the built-in `describe-bindings' command, which this - command can replace. By default, `embark-bindings' lists local key - bindings, typically those bound in the major mode keymap; to get - global bindings as well, call it with a `C-u' prefix argument. - - -3.3 Quitting the minibuffer after an action -─────────────────────────────────────────── - - By default, if you call `embark-act' from the minibuffer it quits the - minibuffer after performing the action. You can change this by setting - the user option `embark-quit-after-action' to `nil'. Having - `embark-act' /not/ quit the minibuffer can be useful to turn commands - into little "thing managers". For example, you can use `find-file' as - a little file manager or `describe-package' as a little package - manager: you can run those commands, perform a series of actions, and - then quit the command. - - If you want to control the quitting behavior in a fine-grained manner - depending on the action, you can set `embark-quit-after-action' to an - alist, associating commands to either `t' for quitting or `nil' for - not quitting. When using an alist, you can use the special key `t' to - specify the default behavior. For example, to specify that by default - actions should not quit the minibuffer but that using `kill-buffer' as - an action should quit, you can use the following configuration: - - ┌──── - │ (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) - └──── - - The variable `embark-quit-after-action' only specifies a default, that - is, it only controls whether or not `embark-act' quits the minibuffer - when you call it without a prefix argument, and you can select the - opposite behavior to what the variable says by calling `embark-act' - with `C-u'. Also note that both the variable - `embark-quit-after-action' and `C-u' have no effect when you call - `embark-act' outside the minibuffer. - - If you find yourself using the quitting and non-quitting variants of - `embark-act' about equally often, independently of the action, you may - prefer to simply have separate commands for them instead of a single - command that you call with `C-u' half the time. You could, for - example, keep the default exiting behavior of `embark-act' and define - a non-quitting version as follows: - - ┌──── - │ (defun embark-act-noquit () - │ "Run action but don't quit the minibuffer afterwards." - │ (interactive) - │ (let ((embark-quit-after-action nil)) - │ (embark-act))) - └──── - - -3.4 Running some setup after injecting the target -───────────────────────────────────────────────── - - You can customize what happens after the target is inserted at the - minibuffer prompt of an action. There are - `embark-target-injection-hooks', that are run by default after - injecting the target into the minibuffer. The variable - `embark-target-injection-hooks' is an alist associating commands to - their setup hooks. There are two special keys: if no setup hook is - specified for a given action, the hook associated to `t' is run; and - the hook associated to `:always' is run regardless of the - action. (This variable used to have the less explicit name of - `embark-setup-action-hooks', so please update your configuration.) - - For example, consider using `shell-command' as an action during file - completion. It would be useful to insert a space before the target - file name and to leave the point at the beginning, so you can - immediately type the shell command to run on that file. That's why in - Embark's default configuration there is an entry in - `embark-target-injection-hooks' associating `shell-command' to a hook - that includes `embark--shell-prep', a simple helper function that - quotes all the spaces in the file name, inserts an extra space at the - beginning of the line and leaves point to the left of it. - - Now, the preparation that `embark--shell-prep' does would be useless - if Embark did what it normally does after it inserts the target of the - action at the minibuffer prompt, which is to "press `RET'" for you, - accepting the target as is; if Embark did that for `shell-command' you - wouldn't get a chance to type in the command to execute! That is why - in Embark's default configuration the entry for `shell-command' in - `embark-target-injection-hooks' also contains the function - `embark--allow-edit'. - - Embark used to have a dedicated variable `embark-allow-edit-actions' - to which you could add commands for which Embark should forgo pressing - `RET' for you after inserting the target. Since its effect can also be - achieved via the general `embark-target-injection-hooks' mechanism, - that variable has been removed to simplify Embark. Be sure to update - your configuration; if you had something like: - - ┌──── - │ (add-to-list 'embark-allow-edit-actions 'my-command) - └──── - - you should replace it with: - - ┌──── - │ (push 'embark--allow-edit - │ (alist-get 'my-command embark-target-injection-hooks)) - └──── - - - Also note that while you could abuse `embark--allow-edit' so that you - have to confirm "dangerous" actions such as `delete-file', it is - better to implement confirmation by adding the `embark--confirm' - function to the appropriate entry of a different hook alist, namely, - `embark-pre-action-hooks'. - - Besides `embark--allow-edit', Embark comes with another function that - is of general utility in action setup hooks: - `embark--ignore-target'. Use it for commands that do prompt you in the - minibuffer but for which inserting the target would be - inappropriate. This is not a common situation but does occasionally - arise. For example it is used by default for - `shell-command-on-region': that command is used as an action for - region targets, and it prompts you for a shell command; you typically - do /not/ want the target, that is the contents of the region, to be - entered at that prompt! - - -3.5 Running hooks before, after or around an action -─────────────────────────────────────────────────── - - Embark has three variables, `embark-pre-action-hooks', - `embark-post-action-hooks' and `embark-around-action-hooks', which are - alists associating commands to hooks that should run before or after - or as around advice for the command when used as an action. As with - `embark-target-injection-hooks', there are two special keys for the - alists: `t' designates the default hook to run when no specific hook - is specified for a command; and the hook associated to `:always' runs - regardless. - - The default values of those variables are fairly extensive, adding - creature comforts to make running actions a smooth experience. Embark - comes with several functions intended to be added to these hooks, and - used in the default values of `embark-pre-action-hooks', - `embark-post-action-hooks' and `embark-around-action-hooks'. - - For pre-action hooks: - - `embark--confirm' - Prompt the user for confirmation before executing the - action. This is used be default for commands deemed "dangerous", - or, more accurately, hard to undo, such as `delete-file' and - `kill-buffer'. - - `embark--unmark-target' - Unmark the active region. Use this for commands you want to act - on the region contents but without the region being active. The - default configuration uses this function as a pre-action hook - for `occur' and `query-replace', for example, so that you can - use them as actions with region targets to search the whole - buffer for the text contained in the region. Without this - pre-action hook using `occur' as an action for a region target - would be pointless: it would search for the the region contents - /in the region/, (typically, due to the details of regexps) - finding only one match! - - `embark--beginning-of-target' - Move to the beginning of the target (for targets that report - bounds). This is used by default for backward motion commands - such as `backward-sexp', so that they don't accidentally leave - you on the current target. - - `embark--end-of-target' - Move to the end of the target. This is used similarly to the - previous function, but also for commands that act on the last - s-expression like `eval-last-sexp'. This allow you to act on an - s-expression from anywhere inside it and still use - `eval-last-sexp' as an action. - - `embark--xref-push-markers' - Push the current location on the xref marker stack. Use this for - commands that take you somewhere and for which you'd like to be - able to come back to where you were using - `xref-pop-marker-stack'. This is used by default for - `find-library'. - - For post-action hooks: - - `embark--restart' - Restart the command currently prompting in the minibuffer, so - that the list of completion candidates is updated. This is - useful as a post action hook for commands that delete or rename - a completion candidate; for example the default value of - `embark-post-action-hooks' uses it for `delete-file', - `kill-buffer', `rename-file', `rename-buffer', etc. - - For around-action hooks: - - `embark--mark-target' - Save existing mark and point location, mark the target and run - the action. Most targets at point outside the minibuffer report - which region of the buffer they correspond to (this is the - information used by `embark-highlight-indicator' to know what - portion of the buffer to highlight); this function marks that - region. It is useful as an around action hook for commands that - expect a region to be marked, for example, it is used by default - for `indent-region' so that it works on s-expression targets, or - for `fill-region' so that it works on paragraph targets. - - `embark--cd' - Run the action with `default-directory' set to the directory - associated to the current target. The target should be of type - `file', `buffer', `bookmark' or `library', and the associated - directory is what you'd expect in each case. - - `embark--narrow-to-target' - Run the action with buffer narrowed to current target. Use this - as an around hook to localize the effect of actions that don't - already work on just the region. In the default configuration it - is used for `repunctuate-sentences'. - - `embark--save-excursion' - Run the action restoring point at the end. The current default - configuration doesn't use this but it is available for users. - - -3.6 Creating your own keymaps -───────────────────────────── - - All internal keymaps are defined with the standard helper macro - `defvar-keymap'. For example a simple version of the file action - keymap could be defined as follows: - - ┌──── - │ (defvar-keymap embark-file-map - │ :doc "Example keymap with a few file actions" - │ :parent embark-general-map - │ "d" #'delete-file - │ "r" #'rename-file - │ "c" #'copy-file) - └──── - - These action keymaps are perfectly normal Emacs keymaps. You may want - to inherit from the `embark-general-map' if you want to access the - default Embark actions. Note that `embark-collect' and `embark-export' - are also made available via `embark-general-map'. - - -3.7 Defining actions for new categories of targets -────────────────────────────────────────────────── - - It is easy to configure Embark to provide actions for new types of - targets, either in the minibuffer or outside it. I present below two - very detailed examples of how to do this. At several points I'll - explain more than one way to proceed, typically with the easiest - option first. I include the alternative options since there will be - similar situations where the easiest option is not available. - - -3.7.1 New minibuffer target example - tab-bar tabs -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - As an example, take the new [tab bars] from Emacs 27. I'll explain how - to configure Embark to offer tab-specific actions when you use the - tab-bar-mode commands that mention tabs by name. The configuration - explained here is now built-in to Embark (and Marginalia), but it's - still a good self-contained example. In order to setup up tab actions - you would need to: (1) make sure Embark knows those commands deal with - tabs, (2) define a keymap for tab actions and configure Embark so it - knows that's the keymap you want. - - -[tab bars] - - -◊ 3.7.1.1 Telling Embark about commands that prompt for tabs by name - - For step (1), it would be great if the `tab-bar-mode' commands - reported the completion category `tab' when asking you for a tab with - completion. (All built-in Emacs commands that prompt for file names, - for example, do have metadata indicating that they want a `file'.) - They do not, unfortunately, and I will describe a couple of ways to - deal with this. - - Maybe the easiest thing is to configure [Marginalia] to enhance those - commands. All of the `tab-bar-*-tab-by-name' commands have the words - "tab by name" in the minibuffer prompt, so you can use: - - ┌──── - │ (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) - └──── - - That's it! But in case you are ever in a situation where you don't - already have commands that prompt for the targets you want, I'll - describe how writing your own command with appropriate `category' - metadata looks: - - ┌──── - │ (defun my-select-tab-by-name (tab) - │ (interactive - │ (list - │ (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - │ (tab-bar-tabs)) - │ (user-error "No tabs found")))) - │ (completing-read - │ "Tabs: " - │ (lambda (string predicate action) - │ (if (eq action 'metadata) - │ '(metadata (category . tab)) - │ (complete-with-action - │ action tab-list string predicate))))))) - │ (tab-bar-select-tab-by-name tab)) - └──── - - As you can see, the built-in support for setting the category - meta-datum is not very easy to use or pretty to look at. To help with - this I recommend the `consult--read' function from the excellent - [Consult] package. With that function we can rewrite the command as - follows: - - ┌──── - │ (defun my-select-tab-by-name (tab) - │ (interactive - │ (list - │ (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - │ (tab-bar-tabs)) - │ (user-error "No tabs found")))) - │ (consult--read tab-list - │ :prompt "Tabs: " - │ :category 'tab)))) - │ (tab-bar-select-tab-by-name tab)) - └──── - - Much nicer! No matter how you define the `my-select-tab-by-name' - command, the first approach with Marginalia and prompt detection has - the following advantages: you get the `tab' category for all the - `tab-bar-*-bar-by-name' commands at once, also, you enhance built-in - commands, instead of defining new ones. - - - [Marginalia] - - [Consult] - - -◊ 3.7.1.2 Defining and configuring a keymap for tab actions - - Let's say we want to offer select, rename and close actions for tabs - (in addition to Embark general actions, such as saving the tab name to - the kill-ring, which you get for free). Then this will do: - - ┌──── - │ (defvar-keymap embark-tab-actions - │ :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." - │ :parent embark-general-map - │ "s" #'tab-bar-select-tab-by-name - │ "r" #'tab-bar-rename-tab-by-name - │ "k" #'tab-bar-close-tab-by-name) - │ - │ (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) - └──── - - What if after using this for a while you feel closing the tab without - confirmation is dangerous? You have a couple of options: - - 1. You can keep using the `tab-bar-close-tab-by-name' command, but - have Embark ask you for confirmation: - ┌──── - │ (push #'embark--confirm - │ (alist-get 'tab-bar-close-tab-by-name - │ embark-pre-action-hooks)) - └──── - - 2. You can write your own command that prompts for confirmation and - use that instead of `tab-bar-close-tab-by-name' in the above - keymap: - ┌──── - │ (defun my-confirm-close-tab-by-name (tab) - │ (interactive "sTab to close: ") - │ (when (y-or-n-p (format "Close tab '%s'? " tab)) - │ (tab-bar-close-tab-by-name tab))) - └──── - - Notice that this is a command you can also use directly from `M-x' - independently of Embark. Using it from `M-x' leaves something to be - desired, though, since you don't get completion for the tab names. - You can fix this if you wish as described in the previous section. - - -3.7.2 New target example in regular buffers - short Wikipedia links -╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - - Say you want to teach Embark to treat text of the form - `wikipedia:Garry_Kasparov' in any regular buffer as a link to - Wikipedia, with actions to open the Wikipedia page in eww or an - external browser or to save the URL of the page in the kill-ring. We - can take advantage of the actions that Embark has preconfigured for - URLs, so all we need to do is teach Embark that - `wikipedia:Garry_Kasparov' stands for the URL - `https://en.wikipedia.org/wiki/Garry_Kasparov'. - - You can be as fancy as you want with the recognized syntax. Here, to - keep the example simple, I'll assume the link matches the regexp - `wikipedia:[[:alnum:]_]+'. We will write a function that looks for a - match surrounding point, and returns a dotted list of the form `'(url - URL-OF-THE-PAGE START . END)' where `START' and `END' are the buffer - positions bounding the target, and are used by Embark to highlight it - if you have `embark-highlight-indicator' included in the list - `embark-indicators'. (There are a couple of other options for the - return value of a target finder: the bounding positions are optional - and a single target finder is allowed to return multiple targets; see - the documentation for `embark-target-finders' for details.) - - ┌──── - │ (defun my-short-wikipedia-link () - │ "Target a link at point of the form wikipedia:Page_Name." - │ (save-excursion - │ (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) - │ (end (progn (skip-chars-forward "[:alnum:]_:") (point))) - │ (str (buffer-substring-no-properties start end))) - │ (save-match-data - │ (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) - │ `(url - │ ,(format "https://en.wikipedia.org/wiki/%s" - │ (match-string 1 str)) - │ ,start . ,end)))))) - │ - │ (add-to-list 'embark-target-finders 'my-short-wikipedia-link) - └──── - - -4 How does Embark call the actions? -═══════════════════════════════════ - - Embark actions are normal Emacs commands, that is, functions with an - interactive specification. In order to execute an action, Embark calls - the command with `call-interactively', so the command reads user input - exactly as if run directly by the user. For example the command may - open a minibuffer and read a string (`read-from-minibuffer') or open a - completion interface (`completing-read'). If this happens, Embark - takes the target string and inserts it automatically into the - minibuffer, simulating user input this way. After inserting the - string, Embark exits the minibuffer, submitting the input. (The - immediate minibuffer exit can be disabled for specific actions in - order to allow editing the input; this is done by adding the - `embark--allow-edit' function to the appropriate entry of - `embark-target-injection-hooks'). Embark inserts the target string at - the first minibuffer opened by the action command, and if the command - happens to prompt the user for input more than once, the user still - interacts with the second and further prompts in the normal - fashion. Note that if a command does not prompt the user for input in - the minibuffer, Embark still allows you to use it as an action, but of - course, never inserts the target anywhere. (There are plenty of - examples in the default configuration of commands that do not prompt - the user bound to keys in the action maps, most of the region actions, - for instance.) - - This is how Embark manages to reuse normal commands as actions. The - mechanism allows you to use as Embark actions commands that were not - written with Embark in mind (and indeed almost all actions that are - bound by default in Embark's action keymaps are standard Emacs - commands). It also allows you to write new custom actions in such a - way that they are useful even without Embark. - - Staring from version 28.1, Emacs has a variable - `y-or-n-p-use-read-key', which when set to `t' causes `y-or-n-p' to - use `read-key' instead of `read-from-minibuffer'. Setting - `y-or-n-p-use-read-key' to `t' is recommended for Embark users because - it keeps Embark from attempting to insert the target at a `y-or-n-p' - prompt, which would almost never be sensible. Also consider this as a - warning to structure your own action commands so that if they use - `y-or-n-p', they do so only after the prompting for the target. - - Here is a simple example illustrating the various ways of reading - input from the user mentioned above. Bind the following commands to - the `embark-symbol-map' to be used as actions, then put the point on - some symbol and run them with `embark-act': - - ┌──── - │ (defun example-action-command1 () - │ (interactive) - │ (message "The input was `%s'." (read-from-minibuffer "Input: "))) - │ - │ (defun example-action-command2 (arg input1 input2) - │ (interactive "P\nsInput 1: \nsInput 2: ") - │ (message "The first input %swas `%s', and the second was `%s'." - │ (if arg "truly " "") - │ input1 - │ input2)) - │ - │ (defun example-action-command3 () - │ (interactive) - │ (message "Your selection was `%s'." - │ (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) - │ - │ (defun example-action-command4 () - │ (interactive) - │ (message "I don't prompt you for input and thus ignore the target!")) - │ - │ (keymap-set embark-symbol-map "X 1" #'example-action-command1) - │ (keymap-set embark-symbol-map "X 2" #'example-action-command2) - │ (keymap-set embark-symbol-map "X 3" #'example-action-command3) - │ (keymap-set embark-symbol-map "X 4" #'example-action-command4) - └──── - - Also note that if you are using the key bindings to call actions, you - can pass prefix arguments to actions in the normal way. For example, - you can use `C-u X2' with the above demonstration actions to make the - message printed by `example-action-command2' more emphatic. This - ability to pass prefix arguments to actions is useful for some actions - in the default configuration, such as - `embark-shell-command-on-buffer'. - - -4.1 Non-interactive functions as actions -──────────────────────────────────────── - - Alternatively, Embark does support one other type of action: a - non-interactive function of a single argument. The target is passed as - argument to the function. For example: - - ┌──── - │ (defun example-action-function (target) - │ (message "The target was `%s'." target)) - │ - │ (keymap-set embark-symbol-map "X 4" #'example-action-function) - └──── - - Note that normally binding non-interactive functions in a keymap is - useless, since when attempting to run them using the key binding you - get an error message similar to "Wrong type argument: commandp, - example-action-function". In general it is more flexible to write any - new Embark actions as commands, that is, as interactive functions, - because that way you can also run them directly, without Embark. But - there are a couple of reasons to use non-interactive functions as - actions: - - 1. You may already have the function lying around, and it is - convenient to simply reuse it. - - 2. For command actions the targets can only be simple string, with no - text properties. For certain advanced uses you may want the action - to receive a string /with/ some text properties, or even a - non-string target. - - -5 Embark, Marginalia and Consult -════════════════════════════════ - - Embark cooperates well with the [Marginalia] and [Consult] packages. - Neither of those packages is a dependency of Embark, but both are - highly recommended companions to Embark, for opposite reasons: - Marginalia greatly enhances Embark's usefulness, while Embark can help - enhance Consult. - - In the remainder of this section I'll explain what exactly Marginalia - does for Embark, and what Embark can do for Consult. - - -[Marginalia] - -[Consult] - -5.1 Marginalia -────────────── - - Embark comes with actions for symbols (commands, functions, variables - with actions such as finding the definition, looking up the - documentation, evaluating, etc.) in the `embark-symbol-map' keymap, - and for packages (actions like install, delete, browse url, etc.) in - the `embark-package-keymap'. - - Unfortunately Embark does not automatically offers you these keymaps - when relevant, because many built-in Emacs commands don't report - accurate category metadata. For example, a command like - `describe-package', which reads a package name from the minibuffer, - does not have metadata indicating this fact. - - In an earlier Embark version, there were functions to supply this - missing metadata, but they have been moved to Marginalia, which - augments many Emacs command to report accurate category metadata. - Simply activating `marginalia-mode' allows Embark to offer you the - package and symbol actions when appropriate again. Candidate - annotations in the Embark collect buffer are also provided by the - Marginalia package: - - • If you install Marginalia and activate `marginalia-mode', Embark - Collect buffers will use the Marginalia annotations automatically. - - • If you don't install Marginalia, you will see only the annotations - that come with Emacs (such as key bindings in `M-x', or the unicode - characters in `C-x 8 RET'). - - -5.2 Consult -─────────── - - The excellent Consult package provides many commands that use - minibuffer completion, via the `completing-read' function; plenty of - its commands can be considered enhanced versions of built-in Emacs - commands, and some are completely new functionality. One common - enhancement provided in all commands for which it makes sense is - preview functionality, for example `consult-buffer' will show you a - quick preview of a buffer before you actually switch to it. - - If you use both Consult and Embark you should install the - `embark-consult' package which provides integration between the - two. It provides exporters for several Consult commands and also - tweaks the behavior of many Consult commands when used as actions with - `embark-act' in subtle ways that you may not even notice, but make for - a smoother experience. You need only install it to get these benefits: - Embark will automatically load it after Consult if found. - - The `embark-consult' package provides the following exporters: - - • You can use `embark-export' from `consult-line', `consult-outline', - or `consult-mark' to obtain an `occur-mode' buffer. As with the - built-in `occur' command you use that buffer to jump to a match and - after that, you can then use `next-error' and `previous-error' to - navigate to other matches. You can also press `e' to activate - `occur-edit-mode' and edit the matches in place! - - • You can export from any of the Consult asynchronous search commands, - `consult-grep', `consult-git-grep', or `consult-ripgrep' to get a - `grep-mode' buffer. Here too you can use `next-error' and - `previous-error' to navigate among matches, and, if you install the - [wgrep] package, you can use it to edit the matches in place. - - In both cases, pressing `g' will rerun the Consult command you had - exported from and re-enter the input you had typed (which is similar - to reverting but a little more flexible). You can then proceed to - re-export if that's what you want, but you can also edit the input - changing the search terms or simply cancel if you see you are done - with that search. - - The `embark-consult' also contains some candidates collectors that - allow you to run `embark-live' to get a live-updating table of - contents for your buffer: - - • `embark-consult-outline-candidates' produces the outline headings of - the current buffer, using `consult-outline'. - • `embark-consult-imenu-candidates' produces the imenu items of the - current buffer, using `consult-imenu'. - • `embark-consult-imenu-or-outline-candidates' is a simple combination - of the two previous functions: it produces imenu items in buffers - deriving from `prog-mode' and otherwise outline headings. - - The way to configure `embark-live' (or `embark-collect' and - `embark-export' for that matter) to use one of these function is to - add it at the end of the `embark-candidate-collectors' list. The - `embark-consult' package by default adds the last one, which seems to - be the most sensible default. - - Besides those exporters and candidate collectors, the `embark-consult' - package provides many subtle tweaks and small integrations between - Embark and Consult. Some examples are: - - • When used as actions, the asynchronous search commands will search - only the files associated to the targets: if the targets /are/ - files, it searches those files; for buffers it will search either - the associated file if there is one, else all files in the buffer's - `default-directory'; for bookmarks it will search the file they - point to, same for Emacs Lisp libraries. This is particularly - powerful when using `embark-act-all' to act on multiple files at - once, for example you can use `consult-find' to search among file - /names/ and then `embark-act-all' and `consult-grep' to search - within the matching files. - - • For all other target types, those that do not have a sensible - notion of associated file, a Consult search command (asynchronous - or not) will search for the text of the target but leave the - minibuffer open so you can interact with the Consult command. - - • `consult-imenu' will search for the target and take you directly to - the location if it matches a unique imenu entry, otherwise it will - leave the minibuffer open so you can navigate among the matches. - - -[wgrep] - - -6 Related Packages -══════════════════ - - There are several packages that offer functionality similar to - Embark's. - - Acting on minibuffer completion candidates - The popular Ivy and Helm packages have support for acting on the - completion candidates of commands written using their APIs, and - there is an extensive ecosystem of packages meant for Helm and - for Ivy (the Ivy ones usually have "counsel" in the name) - providing commands and appropriate actions. - Acting on things at point - The built-in `context-menu-mode' provides a mouse-driven - context-sensitive configurable menu. The `do-at-point' package - by Philip Kaludercic (available on GNU ELPA), on the other hand - is keyboard-driven. - Collecting completion candidates into a buffer - The Ivy package has the command `ivy-occur' which is similar to - `embark-collect'. As with Ivy actions, `ivy-occur' only works - for commands written using the Ivy API. - - -7 Resources -═══════════ - - If you want to learn more about how others have used Embark here are - some links to read: - - • [Fifteen ways to use Embark], a blog post by Karthik Chikmagalur. - • [Protesilaos Stavrou's dotemacs], look for the section called - "Extended minibuffer actions and more (embark.el and - prot-embark.el)" - - And some videos to watch: - - • [Embark and my extras] by Protesilaos Stavrou. - • [Embark – Key features and tweaks] by Raoul Comninos on the - Emacs-Elements YouTube channel. - • [Livestreamed: Adding an Embark context action to send a stream - message] by Sacha Chua. - • [System Crafters Live! - The Many Uses of Embark] by David Wilson. - • [Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark] - by Mike Zamansky. - - -[Fifteen ways to use Embark] - - -[Protesilaos Stavrou's dotemacs] - -[Embark and my extras] - - -[Embark – Key features and tweaks] - -[Livestreamed: Adding an Embark context action to send a stream message] - - -[System Crafters Live! - The Many Uses of Embark] - - -[Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark] - - - -8 Contributions -═══════════════ - - Contributions to Embark are very welcome. There is a [wish list] for - actions, target finders, candidate collectors and exporters. For other - ideas you have for Embark, feel free to open an issue on the [issue - tracker]. Any neat configuration tricks you find might be a good fit - for the [wiki]. - - Code contributions are very welcome too, but since Embark is now on - GNU ELPA, copyright assignment to the FSF is required before you can - contribute code. - - -[wish list] - -[issue tracker] - -[wiki] - - -9 Acknowledgments -═════════════════ - - While I, Omar Antolín Camarena, have written most of the Embark code - and remain very stubborn about some of the design decisions, Embark - has received substantial help from a number of other people which this - document has neglected to mention for far too long. In particular, - Daniel Mendler has been absolutely invaluable, implementing several - important features, and providing a lot of useful advice. - - Code contributions: - - • [Daniel Mendler] - • [Clemens Radermacher] - • [José Antonio Ortega Ruiz] - • [Itai Y. Efrat] - • [a13] - • [jakanakaevangeli] - • [mihakam] - • [Brian Leung] - • [Karthik Chikmagalur] - • [Roshan Shariff] - • [condy0919] - • [Damien Cassou] - • [JimDBh] - - Advice and useful discussions: - - • [Daniel Mendler] - • [Protesilaos Stavrou] - • [Clemens Radermacher] - • [Howard Melman] - • [Augusto Stoffel] - • [Bruce d'Arcus] - • [JD Smith] - • [Karthik Chikmagalur] - • [jakanakaevangeli] - • [Itai Y. Efrat] - • [Mohsin Kaleem] - - -[Daniel Mendler] - -[Clemens Radermacher] - -[José Antonio Ortega Ruiz] - -[Itai Y. Efrat] - -[a13] - -[jakanakaevangeli] - -[mihakam] - -[Brian Leung] - -[Karthik Chikmagalur] - -[Roshan Shariff] - -[condy0919] - -[Damien Cassou] - -[JimDBh] - -[Protesilaos Stavrou] - -[Howard Melman] - -[Augusto Stoffel] - -[Bruce d'Arcus] - -[JD Smith] - -[Mohsin Kaleem] blob - 7af6a2f487aac027b13b6f61f94a81fcf8776255 (mode 644) blob + /dev/null --- elpa/embark-consult-1.1/README.org +++ /dev/null @@ -1,1276 +0,0 @@ -#+TITLE: Embark: Emacs Mini-Buffer Actions Rooted in Keymaps -#+OPTIONS: d:nil -#+EXPORT_FILE_NAME: embark.texi -#+TEXINFO_DIR_CATEGORY: Emacs misc features -#+TEXINFO_DIR_TITLE: Embark: (embark). -#+TEXINFO_DIR_DESC: Emacs Mini-Buffer Actions Rooted in Keymaps - -#+html: GNU ELPA -#+html: GNU-devel ELPA -#+html: MELPA -#+html: MELPA Stable - -* Overview - -Embark makes it easy to choose a command to run based on what is near -point, both during a minibuffer completion session (in a way familiar -to Helm or Counsel users) and in normal buffers. Bind the command -=embark-act= to a key and it acts like prefix-key for a keymap of -/actions/ (commands) relevant to the /target/ around point. With point on -an URL in a buffer you can open the URL in a browser or eww or -download the file it points to. If while switching buffers you spot an -old one, you can kill it right there and continue to select another. -Embark comes preconfigured with over a hundred actions for common -types of targets such as files, buffers, identifiers, s-expressions, -sentences; and it is easy to add more actions and more target types. -Embark can also collect all the candidates in a minibuffer to an -occur-like buffer or export them to a buffer in a major-mode specific -to the type of candidates, such as dired for a set of files, ibuffer -for a set of buffers, or customize for a set of variables. - -** Acting on targets - -You can think of =embark-act= as a keyboard-based version of a -right-click contextual menu. The =embark-act= command (which you should -bind to a convenient key), acts as a prefix for a keymap offering you -relevant /actions/ to use on a /target/ determined by the context: - -- In the minibuffer, the target is the current top completion - candidate. -- In the =*Completions*= buffer the target is the completion at point. -- In a regular buffer, the target is the region if active, or else the - file, symbol, URL, s-expression or defun at point. - -Multiple targets can be present at the same location and you can cycle -between them by repeating the =embark-act= key binding. The type of -actions offered depend on the type of the target. Here is a sample of -a few of the actions offered in the default configuration: - -- For files you get offered actions like deleting, copying, - renaming, visiting in another window, running a shell command on the - file, etc. -- For buffers the actions include switching to or killing the buffer. -- For package names the actions include installing, removing or - visiting the homepage. -- For Emacs Lisp symbols the actions include finding the definition, - looking up documentation, evaluating (which for a variable - immediately shows the value, but for a function lets you pass it - some arguments first). There are some actions specific to variables, - such as setting the value directly or though the customize system, - and some actions specific to commands, such as binding it to a key. - -By default when you use =embark-act= if you don't immediately select an -action, after a short delay Embark will pop up a buffer showing a list -of actions and their corresponding key bindings. If you are using -=embark-act= outside the minibuffer, Embark will also highlight the -current target. These behaviors are configurable via the variable -=embark-indicators=. Instead of selecting an action via its key binding, -you can select it by name with completion by typing =C-h= after -=embark-act=. - -Everything is easily configurable: determining the current target, -classifying it, and deciding which actions are offered for each type -in the classification. The above introduction just mentions part of -the default configuration. - -Configuring which actions are offered for a type is particularly easy -and requires no programming: the variable =embark-keymap-alist= -associates target types with variables containing keymaps, and those -keymaps containing bindings for the actions. (To examine the available -categories and their associated keymaps, you can use =C-h v -embark-keymap-alist= or customize that variable.) For example, in the -default configuration the type =file= is associated with the symbol -=embark-file-map=. That symbol names a keymap with single-letter key -bindings for common Emacs file commands, for instance =c= is bound to -=copy-file=. This means that if you are in the minibuffer after running -a command that prompts for a file, such as =find-file= or =rename-file=, -you can copy a file by running =embark-act= and then pressing =c=. - -These action keymaps are very convenient but not strictly necessary -when using =embark-act=: you can use any command that reads from the -minibuffer as an action and the target of the action will be inserted -at the first minibuffer prompt. After running =embark-act= all of your -key bindings and even =execute-extended-command= can be used to run a -command. For example, if you want to replace all occurrences of the -symbol at point, just use =M-%= as the action, there is no need to bind -=query-replace= in one of Embark's keymaps. Also, those action keymaps -are normal Emacs keymaps and you should feel free to bind in them -whatever commands you find useful as actions and want to be available -through convenient bindings. - -The actions in =embark-general-map= are available no matter what type -of completion you are in the middle of. By default this includes -bindings to save the current candidate in the kill ring and to insert -the current candidate in the previously selected buffer (the buffer -that was current when you executed a command that opened up the -minibuffer). - -Emacs's minibuffer completion system includes metadata indicating the -/category/ of what is being completed. For example, =find-file='s -metadata indicates a category of =file= and =switch-to-buffer='s metadata -indicates a category of =buffer=. Embark has the related notion of the -/type/ of a target for actions, and by default when category metadata -is present it is taken to be the type of minibuffer completion -candidates when used as targets. Emacs commands often do not set -useful category metadata so the [[https://github.com/minad/marginalia][Marginalia]] package, which supplies -this missing metadata, is highly recommended for use with Embark. - -Embark's default configuration has actions for the following target -types: files, buffers, symbols, packages, URLs, bookmarks, and as a -somewhat special case, actions for when the region is active. You can -read about the [[https://github.com/oantolin/embark/wiki/Default-Actions][default actions and their key bindings]] on the GitHub -project wiki. - -** The default action on a target - -Embark has a notion of default action for a target: - -- If the target is a minibuffer completion candidate, then the default - action is whatever command opened the minibuffer in the first place. - For example if you run =kill-buffer=, then the default action will be - to kill buffers. -- If the target comes from a regular buffer (i.e., not a minibuffer), - then the default action is whatever is bound to =RET= in the keymap of - actions for that type of target. For example, in Embark's default - configuration for a URL found at point the default action is - =browse-url=, because =RET= is bound to =browse-url= in the =embark-url-map= - keymap. - -To run the default action you can press =RET= after running =embark-act=. -Note that if there are several different targets at a given location, -each has its own default action, so first cycle to the target you want -and then press =RET= to run the corresponding default action. - -There is also =embark-dwim= which runs the default action for the first -target found. It's pretty handy in non-minibuffer buffers: with -Embark's default configuration it will: - -- Open the file at point. -- Open the URL at point in a web browser (using the =browse-url= - command). -- Compose a new email to the email address at point. -- In an Emacs Lisp buffer, if point is on an opening parenthesis or - right after a closing one, it will evaluate the corresponding - expression. -- Go to the definition of an Emacs Lisp function, variable or macro at - point. -- Find the file corresponding to an Emacs Lisp library at point. - -** Working with sets of possible targets - -Besides acting individually on targets, Embark lets you work -collectively on a set of target /candidates/. For example, while you are -in the minibuffer the candidates are simply the possible completions -of your input. Embark provides three main commands to work on candidate -sets: - -- The =embark-act-all= command runs the same action on each of the - current candidates. It is just like using =embark-act= on each - candidate in turn. (Because you can easily act on many more - candidates than you meant to, by default Embark asks you to confirm - uses of =embark-act-all=; you can turn this off by setting the user - option =embark-confirm-act-all= to =nil=.) - -- The =embark-collect= command produces a buffer listing all the current - candidates, for you to peruse and run actions on at your leisure. - The candidates are displayed as a list showing additional - annotations. If any of the candidates contain newlines, then - horizontal lines are used to separate candidates. - - The Embark Collect buffer is somewhat "dired-like": you can select - and deselect candidates through =embark-select= (available as an - action in =embark-act=, bound to =SPC=; but you could also give it a - global key binding). In an Embark Collect buffer =embark-act= is bound - to =a= and =embark-act-all= is bound to =A=; =embark-act-all= will act on - all currently marked candidates if there any, and will act on all - candidates if none are marked. In particular, this means that =a SPC= - will toggle whether the candidate at point is selected, and =A SPC= - will select all candidates if none are selected, or deselect all - selected candidates if there are some. - -- The =embark-export= command tries to open a buffer in an appropriate - major mode for the set of candidates. If the candidates are files - export produces a Dired buffer; if they are buffers, you get an - Ibuffer buffer; and if they are packages you get a buffer in - package menu mode. - - If you use the grepping commands from the [[https://github.com/minad/consult/][Consult]] package, - =consult-grep=, =consult-git-grep= or =consult-ripgrep=, then you should - install the =embark-consult= package, which adds support for exporting a - list of grep results to an honest grep-mode buffer, on which you can - even use [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep]] if you wish. - -When in doubt choosing between exporting and collecting, a good rule -of thumb is to always prefer =embark-export= since when an exporter to a -special major mode is available for a given type of target, it will be -more featureful than an Embark collect buffer, and if no such exporter -is configured the =embark-export= command falls back to the generic -=embark-collect=. - -These commands are always available as "actions" (although they do not -act on just the current target but on all candidates) for =embark-act= -and are bound to =A=, =S= (for "snapshot"), and =E=, respectively, in -=embark-general-map=. This means that you do not have to bind your own -key bindings for these (although you can, of course!), just a key -binding for =embark-act=. - -In Embark Collect or Embark Export buffers that were obtained by -running =embark-collect= or =embark-export= from within a minibuffer -completion session, =g= is bound to a command that restarts the -completion session, that is, the command that opened the minibuffer is -run again and the minibuffer contents restored. You can then interact -normally with the command, perhaps editing the minibuffer contents, -and, if you wish, you can rerun =embark-collect= or =embark-export= to get -an updated buffer. - -*** Selecting some targets to make an ad hoc candidate set - -The commands for working with sets of candidates just described, -namely =embark-act-all=, =embark-export= and =embark-collect= by default -work with all candidates defined in the current context. For example, -in the minibuffer they operate on all currently completion candidates, -or in a dired buffer they work on all marked files (or all files if -none are marked). Embark also has a notion of /selection/, where you can -accumulate an ad hoc list of targets for these commands to work on. - -The selection is controlled by using the =embark-select= action, bound -to =SPC= in =embark-general-map= so that it is always available (you can -also give =embark-select= a global key binding if you wish; when called -directly, not as an action for =embark-act=, it will select the first -target at point). Calling this action on a target toggles its -membership in the current buffer's Embark selection; that is, it adds -it to selection if not selected and removes it from the selection if -it was selected. Whenever the selection for a buffer is non-empty, the -commands =embark-act-all=, =embark-export= and =embark-collect= will act on -the selection. - -To deselect all selected targets, you can use the =embark-select= action -through =embark-act-all=, since this will run =embark-select= on each -member of the current selection. Similarly if no targets are selected -and you are in a minibuffer completion session, running =embark-select= -from =embark-act-all= will select all the current completion candidates. - -By default, whenever some targets are selected in the current buffer, -a count of selected targets appears in the mode line. This can be -turned off or customized through the =embark-selection-indicator= user -option. - -The selection functionality is supported in every buffer: - -- In the minibuffer this gives a convenient way to act on several - completion candidates that don't follow any simple pattern: just go - through the completions selecting the ones you want, then use - =embark-act-all=. For example, you could attach several files at once - to an email. -- For Embark Collect buffers this functionality enables a dired-like - workflow, in which you mark various candidates and apply an action - to all at once. (It supersedes a previous ad hoc dired-like - interface that was implemented only in Embark Collect buffers, with - a slightly different interface.) -- In a eww buffer you could use this to select various links you wish - to follow up on, and then collect them into a buffer. Similarly, - while reading Emacs's info manual you could select some symbols you - want to read more about and export them to an =apropos-mode= buffer. -- You can use selections in regular text or programming buffers to do - complex editing operations. For example, if you have three - paragraphs scattered over a file and you want to bring them - together, you can select each one, insert them all somewhere and - finally delete all of them (from their original locations). - -*** =embark-live= a live-updating variant of =embark-collect= - -Finally, there is also an =embark-live= variant of the =embark-collect= -command which automatically updates the collection after each change -in the source buffer. Users of a completion UI that automatically -updates and displays the candidate list (such as Vertico, Icomplete, -Fido-mode, or MCT) will probably not want to use -=embark-live= from the minibuffer as they will then have two live -updating displays of the completion candidates! - -A more likely use of =embark-live= is to be called from a regular buffer -to display a sort of live updating "table of contents" for the buffer. -This depends on having appropriate candidate collectors configured in -=embark-candidate-collectors=. There are not many in Embark's default -configuration, but you can try this experiment: open a dired buffer in -a directory that has very many files, mark a few, and run =embark-live=. -You'll get an Embark Collect buffer containing only the marked files, -which updates as you mark or unmark files in dired. To make -=embark-live= genuinely useful other candidate collectors are required. -The =embark-consult= package (documented near the end of this manual) -contains a few: one for imenu items and one for outline headings as -used by =outline-minor-mode=. Those collectors really do give -=embark-live= a table-of-contents feel. - -** Switching to a different command without losing what you've typed - -Embark also has the =embark-become= command which is useful for when -you run a command, start typing at the minibuffer and realize you -meant a different command. The most common case for me is that I run -=switch-to-buffer=, start typing a buffer name and realize I haven't -opened the file I had in mind yet! I'll use this situation as a -running example to illustrate =embark-become=. When this happens I can, -of course, press =C-g= and then run =find-file= and open the file, but -this requires retyping the portion of the file name you already -typed. This process can be streamlined with =embark-become=: while still -in the =switch-to-buffer= you can run =embark-become= and effectively -make the =switch-to-buffer= command become =find-file= for this run. - -You can bind =embark-become= to a key in =minibuffer-local-map=, but it is -also available as an action under the letter =B= (uppercase), so you -don't need a binding if you already have one for =embark-act=. So, -assuming I have =embark-act= bound to, say, =C-.=, once I realize I -haven't open the file I can type =C-. B C-x C-f= to have -=switch-to-buffer= become =find-file= without losing what I have already -typed in the minibuffer. - -But for even more convenience, =embark-become= offers shorter key -bindings for commands you are likely to want the current command to -become. When you use =embark-become= it looks for the current command in -all keymaps named in the list =embark-become-keymaps= and then activates -all keymaps that contain it. For example, the default value of -=embark-become-keymaps= contains a keymap =embark-become-file+buffer-map= -with bindings for several commands related to files and buffers, in -particular, it binds =switch-to-buffer= to =b= and =find-file= to =f=. So when -I accidentally try to switch to a buffer for a file I haven't opened -yet, =embark-become= finds that the command I ran, =switch-to-buffer=, is -in the keymap =embark-become-file+buffer-map=, so it activates that -keymap (and any others that also contain a binding for -=switch-to-buffer=). The end result is that I can type =C-. B f= to -switch to =find-file=. - -* Quick start - -The easiest way to install Embark is from GNU ELPA, just run =M-x -package-install RET embark RET=. (It is also available on MELPA.) It is -highly recommended to also install [[https://github.com/minad/marginalia][Marginalia]] (also available on GNU -ELPA), so that Embark can offer you preconfigured actions in more -contexts. For =use-package= users, the following is a very reasonable -starting configuration: - -#+begin_src emacs-lisp - (use-package marginalia - :ensure t - :config - (marginalia-mode)) - - (use-package embark - :ensure t - - :bind - (("C-." . embark-act) ;; pick some comfortable binding - ("C-;" . embark-dwim) ;; good alternative: M-. - ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' - - :init - - ;; Optionally replace the key help with a completing-read interface - (setq prefix-help-command #'embark-prefix-help-command) - - ;; Show the Embark target at point via Eldoc. You may adjust the - ;; Eldoc strategy, if you want to see the documentation from - ;; multiple providers. Beware that using this can be a little - ;; jarring since the message shown in the minibuffer can be more - ;; than one line, causing the modeline to move up and down: - - ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) - ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) - - :config - - ;; Hide the mode line of the Embark live/completions buffers - (add-to-list 'display-buffer-alist - '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" - nil - (window-parameters (mode-line-format . none))))) - - ;; Consult users will also want the embark-consult package. - (use-package embark-consult - :ensure t ; only need to install it, embark loads it after consult if found - :hook - (embark-collect-mode . consult-preview-at-point-mode)) -#+end_src - -About the suggested key bindings for =embark-act= and =embark-dwim=: -- Those key bindings are unlikely to work in the terminal, but - terminal users are probably well aware of this and will know to - select different bindings. -- The suggested =C-.= binding is used by default in (at least some - installations of) GNOME to input emojis, and Emacs doesn't even get - a chance to respond to the binding. You can select a different key - binding for =embark-act= or use =ibus-setup= to change the shortcut for - emoji insertion (Emacs 29 will likely use =C-x 8 e e=, in case you - want to set the same one system-wide). -- The suggested alternative of =M-.= for =embark-dwim= is bound by default - to =xref-find-definitions=. That is a very useful command but - overwriting it with =embark-dwim= is sensible since in Embark's - default configuration, =embark-dwim= will also find the definition of - the identifier at point. (Note that =xref-find-definitions= with a - prefix argument prompts you for an identifier, =embark-dwim= does not - cover this case). - -Other Embark commands such as =embark-act-all=, =embark-become=, -=embark-collect=, and =embark-export= can be run through =embark-act= as -actions bound to =A=, =B=, =S= (for "snapshot"), and =E= respectively, and -thus don't really need a dedicated key binding, but feel free to bind -them directly if you so wish. If you do choose to bind them directly, -you'll probably want to bind them in =minibuffer-local-map=, since they -are most useful in the minibuffer (in fact, =embark-become= only works -in the minibuffer). - -The command =embark-dwim= executes the default action at point. Another good -keybinding for =embark-dwim= is =M-.= since =embark-dwim= acts like -=xref-find-definitions= on the symbol at point. =C-.= can be seen as a -right-click context menu at point and =M-.= acts like left-click. The -keybindings are mnemonic, both act at the point (=.=). - -Embark needs to know what your minibuffer completion system considers -to be the list of candidates and which one is the current candidate. -Embark works out of the box if you use Emacs's default tab completion, -the built-in =icomplete-mode= or =fido-mode=, or the third-party packages -[[https://github.com/minad/vertico][Vertico]] or [[https://github.com/abo-abo/swiper][Ivy]]. - -If you are a [[https://emacs-helm.github.io/helm/][Helm]] or [[https://github.com/abo-abo/swiper][Ivy]] user you are unlikely to want Embark since -those packages include comprehensive functionality for acting on -minibuffer completion candidates. (Embark does come with Ivy -integration despite this.) - -* Advanced configuration -** Showing information about available targets and actions - -By default, if you run =embark-act= and do not immediately select an -action, after a short delay Embark will pop up a buffer called =*Embark -Actions*= containing a list of available actions with their key -bindings. You can scroll that buffer with the mouse of with the usual -commands =scroll-other-window= and =scroll-other-window-down= (bound by -default to =C-M-v= and =C-M-S-v=). - -That functionality is provided by the =embark-mixed-indicator=, but -Embark has other indicators that can provide information about the -target and its type, what other targets you can cycle to, and which -actions have key bindings in the action map for the current type of -target. Any number of indicators can be active at once and the user -option =embark-indicators= should be set to a list of the desired -indicators. - -Embark comes with the following indicators: - -- =embark-minimal-indicator=: shows a messages in the echo area or - minibuffer prompt showing the current target and the types of all - targets starting with the current one. - -- =embark-highlight-indicator=: highlights the target at point; on by - default. - -- =embark-verbose-indicator=: displays a table of actions and their key - bindings in a buffer; this is not on by default, in favor of the - mixed indicator described next. - -- =embark-mixed-indicator=: starts out by behaving as the minimal - indicator but after a short delay acts as the verbose indicator; - this is on by default. - -- =embark-isearch-highlight-indicator=: this only does something when - the current target is the symbol at point, in which case it - lazily highlights all occurrences of that symbol in the current - buffer, like isearch; also on by default. - -Users of the popular [[https://github.com/justbur/emacs-which-key][which-key]] package may prefer to use the -=embark-which-key-indicator= from the [[https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt][Embark wiki]]. Just copy its -definition from the wiki into your configuration and customize the -=embark-indicators= user option to exclude the mixed and verbose -indicators and to include =embark-which-key-indicator=. - -If you use [[https://github.com/minad/vertico][Vertico]], there is an even easier way to get a -=which-key=-like display that also lets you use completion to narrow -down the list of alternatives, described at the end of the next -section. - -** Selecting commands via completions instead of key bindings - -As an alternative to reading the list of actions in the verbose or -mixed indicators (see the previous section for a description of -these), you can press the =embark-help-key=, which is =C-h= by default -(but you may prefer =?= to free up =C-h= for use as a prefix) after -running =embark-act=. Pressing the help key will prompt you for the name -of an action with completion (but feel free to enter a command that is -not among the offered candidates!), and will also remind you of the -key bindings. You can press =embark-keymap-prompter-key=, which is =@= by -default, at the prompt and then one of the key bindings to enter the -name of the corresponding action. - -You may think that with the =*Embark Actions*= buffer popping up to -remind you of the key bindings you'd never want to use completion to -select an action by name, but personally I find that typing a small -portion of the action name to narrow down the list of candidates feels -significantly faster than visually scanning the entire list of actions. - -If you find you prefer selecting actions that way, you can configure -embark to always prompt you for actions by setting the variable -=embark-prompter= to =embark-completing-read-prompter=. - -On the other hand, you may wish to continue using key bindings for the -actions you perform most often, and to use completion only to explore -what further actions are available or when you've forgotten a key -binding. In that case, you may prefer to use the minimal indicator, -which does not pop-up an =*Embark Actions*= buffer at all, and to use -the =embark-help-key= whenever you need help. This unobtrusive setup is -achieved with the following configuration: - -#+begin_src emacs-lisp - (setq embark-indicators - '(embark-minimal-indicator ; default is embark-mixed-indicator - embark-highlight-indicator - embark-isearch-highlight-indicator)) -#+end_src - -[[https://github.com/minad/vertico][Vertico]] users may wish to configure a grid display for the actions and -key-bindings, reminiscent of the popular package [[https://github.com/justbur/emacs-which-key][which-key]], but, of -course, enhanced by the use of completion to narrow the list of -commands. In order to get the grid display, put the following in your -Vertico configuration: - -#+begin_src emacs-lisp - (add-to-list 'vertico-multiform-categories '(embark-keybinding grid)) - (vertico-multiform-mode) -#+end_src - -This will make the available keys be shown in a compact grid like in -=which-key=. The =vertico-multiform-mode= also enables keys such as =M-V=, -=M-G=, =M-B=, and =M-U= for manually switching between layouts in Vertico -buffers. - -*** Selecting commands via completion outside of Embark - -If you like this completion interface for exploring key bindings for -Embark actions, you may want to use it elsewhere in Emacs. You can use -Embark's completion-based command prompter to list: - -- key bindings under a prefix, -- local key bindings, or -- all key bindings. - -To use it for key bindings under a prefix (you can use this to replace -the =which-key= package, for example), use this configuration: - -#+begin_src emacs-lisp - (setq prefix-help-command #'embark-prefix-help-command) -#+end_src - -Now, when you have started on a prefix sequence such as =C-x= or =C-c=, -pressing =C-h= will bring up the Embark version of the built-in -=prefix-help-command=, which will list the keys under that prefix and -their bindings, and lets you select the one you wanted with completion, -or by key binding if you press =embark-keymap-prompter-key=. - -To list local or global key bindings, use the command =embark-bindings=. -You can bind that to =C-h b=, which is the default key binding for the -built-in =describe-bindings= command, which this command can replace. By -default, =embark-bindings= lists local key bindings, typically those -bound in the major mode keymap; to get global bindings as well, call -it with a =C-u= prefix argument. - -** Quitting the minibuffer after an action - -By default, if you call =embark-act= from the minibuffer it quits the -minibuffer after performing the action. You can change this by setting -the user option =embark-quit-after-action= to =nil=. Having =embark-act= /not/ -quit the minibuffer can be useful to turn commands into little "thing -managers". For example, you can use =find-file= as a little file manager -or =describe-package= as a little package manager: you can run those -commands, perform a series of actions, and then quit the command. - -If you want to control the quitting behavior in a fine-grained manner -depending on the action, you can set =embark-quit-after-action= to an -alist, associating commands to either =t= for quitting or =nil= for not -quitting. When using an alist, you can use the special key =t= to -specify the default behavior. For example, to specify that by default -actions should not quit the minibuffer but that using =kill-buffer= as -an action should quit, you can use the following configuration: - -#+begin_src emacs-lisp - (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) -#+end_src - -The variable =embark-quit-after-action= only specifies a default, that -is, it only controls whether or not =embark-act= quits the minibuffer -when you call it without a prefix argument, and you can select the -opposite behavior to what the variable says by calling =embark-act= with -=C-u=. Also note that both the variable =embark-quit-after-action= and =C-u= -have no effect when you call =embark-act= outside the minibuffer. - -If you find yourself using the quitting and non-quitting variants of -=embark-act= about equally often, independently of the action, you may -prefer to simply have separate commands for them instead of a single -command that you call with =C-u= half the time. You could, for example, -keep the default exiting behavior of =embark-act= and define a -non-quitting version as follows: - -#+begin_src emacs-lisp - (defun embark-act-noquit () - "Run action but don't quit the minibuffer afterwards." - (interactive) - (let ((embark-quit-after-action nil)) - (embark-act))) -#+end_src - -** Running some setup after injecting the target - -You can customize what happens after the target is inserted at the -minibuffer prompt of an action. There are -=embark-target-injection-hooks=, that are run by default after injecting -the target into the minibuffer. The variable -=embark-target-injection-hooks= is an alist associating commands to -their setup hooks. There are two special keys: if no setup hook is -specified for a given action, the hook associated to =t= is run; and the -hook associated to =:always= is run regardless of the action. (This -variable used to have the less explicit name of -=embark-setup-action-hooks=, so please update your configuration.) - -For example, consider using =shell-command= as an action during file -completion. It would be useful to insert a space before the target -file name and to leave the point at the beginning, so you can -immediately type the shell command to run on that file. That's why in -Embark's default configuration there is an entry in -=embark-target-injection-hooks= associating =shell-command= to a hook that -includes =embark--shell-prep=, a simple helper function that quotes all -the spaces in the file name, inserts an extra space at the beginning -of the line and leaves point to the left of it. - -Now, the preparation that =embark--shell-prep= does would be useless if -Embark did what it normally does after it inserts the target of the -action at the minibuffer prompt, which is to "press =RET=" for you, -accepting the target as is; if Embark did that for =shell-command= you -wouldn't get a chance to type in the command to execute! That is why -in Embark's default configuration the entry for =shell-command= in -=embark-target-injection-hooks= also contains the function -=embark--allow-edit=. - -Embark used to have a dedicated variable =embark-allow-edit-actions= to -which you could add commands for which Embark should forgo pressing -=RET= for you after inserting the target. Since its effect can also be -achieved via the general =embark-target-injection-hooks= mechanism, that -variable has been removed to simplify Embark. Be sure to update your -configuration; if you had something like: - -#+begin_src emacs-lisp - (add-to-list 'embark-allow-edit-actions 'my-command) -#+end_src - -you should replace it with: - -#+begin_src emacs-lisp - (push 'embark--allow-edit - (alist-get 'my-command embark-target-injection-hooks)) -#+end_src - - -Also note that while you could abuse =embark--allow-edit= so that you -have to confirm "dangerous" actions such as =delete-file=, it is better -to implement confirmation by adding the =embark--confirm= function to -the appropriate entry of a different hook alist, namely, -=embark-pre-action-hooks=. - -Besides =embark--allow-edit=, Embark comes with another function that is -of general utility in action setup hooks: =embark--ignore-target=. Use -it for commands that do prompt you in the minibuffer but for which -inserting the target would be inappropriate. This is not a common -situation but does occasionally arise. For example it is used by -default for =shell-command-on-region=: that command is used as an action -for region targets, and it prompts you for a shell command; you -typically do /not/ want the target, that is the contents of the region, -to be entered at that prompt! - -** Running hooks before, after or around an action - -Embark has three variables, =embark-pre-action-hooks=, -=embark-post-action-hooks= and =embark-around-action-hooks=, which are -alists associating commands to hooks that should run before or after -or as around advice for the command when used as an action. As with -=embark-target-injection-hooks=, there are two special keys for the -alists: =t= designates the default hook to run when no specific hook is -specified for a command; and the hook associated to =:always= runs -regardless. - -The default values of those variables are fairly extensive, adding -creature comforts to make running actions a smooth experience. Embark -comes with several functions intended to be added to these hooks, and -used in the default values of =embark-pre-action-hooks=, -=embark-post-action-hooks= and =embark-around-action-hooks=. - -For pre-action hooks: - -- =embark--confirm= :: Prompt the user for confirmation before executing - the action. This is used be default for commands deemed "dangerous", - or, more accurately, hard to undo, such as =delete-file= and - =kill-buffer=. - -- =embark--unmark-target= :: Unmark the active region. Use this for - commands you want to act on the region contents but without the - region being active. The default configuration uses this function as - a pre-action hook for =occur= and =query-replace=, for example, so that - you can use them as actions with region targets to search the whole - buffer for the text contained in the region. Without this pre-action - hook using =occur= as an action for a region target would be - pointless: it would search for the the region contents /in the - region/, (typically, due to the details of regexps) finding only one - match! - -- =embark--beginning-of-target= :: Move to the beginning of the target - (for targets that report bounds). This is used by default for - backward motion commands such as =backward-sexp=, so that they don't - accidentally leave you on the current target. - -- =embark--end-of-target= :: Move to the end of the target. This is used - similarly to the previous function, but also for commands that act - on the last s-expression like =eval-last-sexp=. This allow you to act - on an s-expression from anywhere inside it and still use - =eval-last-sexp= as an action. - -- =embark--xref-push-markers= :: Push the current location on the xref - marker stack. Use this for commands that take you somewhere and for - which you'd like to be able to come back to where you were using - =xref-pop-marker-stack=. This is used by default for =find-library=. - -For post-action hooks: - -- =embark--restart= :: Restart the command currently prompting in the - minibuffer, so that the list of completion candidates is updated. - This is useful as a post action hook for commands that delete or - rename a completion candidate; for example the default value of - =embark-post-action-hooks= uses it for =delete-file=, =kill-buffer=, - =rename-file=, =rename-buffer=, etc. - -For around-action hooks: - -- =embark--mark-target= :: Save existing mark and point location, mark - the target and run the action. Most targets at point outside the - minibuffer report which region of the buffer they correspond to - (this is the information used by =embark-highlight-indicator= to - know what portion of the buffer to highlight); this function marks - that region. It is useful as an around action hook for commands that - expect a region to be marked, for example, it is used by default for - =indent-region= so that it works on s-expression targets, or for - =fill-region= so that it works on paragraph targets. - -- =embark--cd= :: Run the action with =default-directory= set to the - directory associated to the current target. The target should be of - type =file=, =buffer=, =bookmark= or =library=, and the associated directory - is what you'd expect in each case. - -- =embark--narrow-to-target= :: Run the action with buffer narrowed to - current target. Use this as an around hook to localize the effect of - actions that don't already work on just the region. In the default - configuration it is used for =repunctuate-sentences=. - -- =embark--save-excursion= :: Run the action restoring point at the end. - The current default configuration doesn't use this but it is - available for users. - -** Creating your own keymaps - -All internal keymaps are defined with the standard helper macro -=defvar-keymap=. For example a simple version of the file action keymap -could be defined as follows: - -#+BEGIN_SRC emacs-lisp - (defvar-keymap embark-file-map - :doc "Example keymap with a few file actions" - :parent embark-general-map - "d" #'delete-file - "r" #'rename-file - "c" #'copy-file) -#+END_SRC - -These action keymaps are perfectly normal Emacs -keymaps. You may want to inherit from the =embark-general-map= if you -want to access the default Embark actions. Note that =embark-collect= -and =embark-export= are also made available via =embark-general-map=. - -** Defining actions for new categories of targets - -It is easy to configure Embark to provide actions for new types of -targets, either in the minibuffer or outside it. I present below two -very detailed examples of how to do this. At several points I'll -explain more than one way to proceed, typically with the easiest -option first. I include the alternative options since there will be -similar situations where the easiest option is not available. - -*** New minibuffer target example - tab-bar tabs - -As an example, take the new [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html][tab bars]] from Emacs 27. I'll explain how -to configure Embark to offer tab-specific actions when you use the -tab-bar-mode commands that mention tabs by name. The configuration -explained here is now built-in to Embark (and Marginalia), but it's -still a good self-contained example. In order to setup up tab actions -you would need to: (1) make sure Embark knows those commands deal with -tabs, (2) define a keymap for tab actions and configure Embark so it -knows that's the keymap you want. - -**** Telling Embark about commands that prompt for tabs by name - -For step (1), it would be great if the =tab-bar-mode= commands reported -the completion category =tab= when asking you for a tab with -completion. (All built-in Emacs commands that prompt for file names, -for example, do have metadata indicating that they want a =file=.) They -do not, unfortunately, and I will describe a couple of ways to deal -with this. - -Maybe the easiest thing is to configure [[https://github.com/minad/marginalia][Marginalia]] to enhance those -commands. All of the =tab-bar-*-tab-by-name= commands have the words -"tab by name" in the minibuffer prompt, so you can use: - -#+begin_src emacs-lisp - (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) -#+end_src - -That's it! But in case you are ever in a situation where you don't -already have commands that prompt for the targets you want, I'll -describe how writing your own command with appropriate =category= -metadata looks: - -#+begin_src emacs-lisp - (defun my-select-tab-by-name (tab) - (interactive - (list - (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - (tab-bar-tabs)) - (user-error "No tabs found")))) - (completing-read - "Tabs: " - (lambda (string predicate action) - (if (eq action 'metadata) - '(metadata (category . tab)) - (complete-with-action - action tab-list string predicate))))))) - (tab-bar-select-tab-by-name tab)) -#+end_src - -As you can see, the built-in support for setting the category -meta-datum is not very easy to use or pretty to look at. To help with -this I recommend the =consult--read= function from the excellent -[[https://github.com/minad/consult/][Consult]] package. With that function we can rewrite the command as -follows: - -#+begin_src emacs-lisp - (defun my-select-tab-by-name (tab) - (interactive - (list - (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) - (tab-bar-tabs)) - (user-error "No tabs found")))) - (consult--read tab-list - :prompt "Tabs: " - :category 'tab)))) - (tab-bar-select-tab-by-name tab)) -#+end_src - -Much nicer! No matter how you define the =my-select-tab-by-name= -command, the first approach with Marginalia and prompt detection has -the following advantages: you get the =tab= category for all the -=tab-bar-*-bar-by-name= commands at once, also, you enhance built-in -commands, instead of defining new ones. - -**** Defining and configuring a keymap for tab actions - - Let's say we want to offer select, rename and close actions for tabs - (in addition to Embark general actions, such as saving the tab name to - the kill-ring, which you get for free). Then this will do: - - #+begin_src emacs-lisp - (defvar-keymap embark-tab-actions - :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." - :parent embark-general-map - "s" #'tab-bar-select-tab-by-name - "r" #'tab-bar-rename-tab-by-name - "k" #'tab-bar-close-tab-by-name) - - (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) - #+end_src - - What if after using this for a while you feel closing the tab - without confirmation is dangerous? You have a couple of options: - - 1. You can keep using the =tab-bar-close-tab-by-name= command, but have - Embark ask you for confirmation: - #+begin_src emacs-lisp - (push #'embark--confirm - (alist-get 'tab-bar-close-tab-by-name - embark-pre-action-hooks)) - #+end_src - - 2. You can write your own command that prompts for confirmation and - use that instead of =tab-bar-close-tab-by-name= in the above keymap: - #+begin_src emacs-lisp - (defun my-confirm-close-tab-by-name (tab) - (interactive "sTab to close: ") - (when (y-or-n-p (format "Close tab '%s'? " tab)) - (tab-bar-close-tab-by-name tab))) - #+end_src - - Notice that this is a command you can also use directly from =M-x= - independently of Embark. Using it from =M-x= leaves something to be - desired, though, since you don't get completion for the tab names. - You can fix this if you wish as described in the previous section. - -*** New target example in regular buffers - short Wikipedia links - -Say you want to teach Embark to treat text of the form -=wikipedia:Garry_Kasparov= in any regular buffer as a link to Wikipedia, -with actions to open the Wikipedia page in eww or an external browser -or to save the URL of the page in the kill-ring. We can take advantage -of the actions that Embark has preconfigured for URLs, so all we need -to do is teach Embark that =wikipedia:Garry_Kasparov= stands for the URL -=https://en.wikipedia.org/wiki/Garry_Kasparov=. - -You can be as fancy as you want with the recognized syntax. Here, to -keep the example simple, I'll assume the link matches the regexp -=wikipedia:[[:alnum:]_]+=. We will write a function that looks for a -match surrounding point, and returns a dotted list of the form ='(url -URL-OF-THE-PAGE START . END)= where =START= and =END= are the buffer -positions bounding the target, and are used by Embark to highlight it -if you have =embark-highlight-indicator= included in the list -=embark-indicators=. (There are a couple of other options for the return -value of a target finder: the bounding positions are optional and a -single target finder is allowed to return multiple targets; see the -documentation for =embark-target-finders= for details.) - -#+begin_src emacs-lisp - (defun my-short-wikipedia-link () - "Target a link at point of the form wikipedia:Page_Name." - (save-excursion - (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) - (end (progn (skip-chars-forward "[:alnum:]_:") (point))) - (str (buffer-substring-no-properties start end))) - (save-match-data - (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) - `(url - ,(format "https://en.wikipedia.org/wiki/%s" - (match-string 1 str)) - ,start . ,end)))))) - - (add-to-list 'embark-target-finders 'my-short-wikipedia-link) -#+end_src - -* How does Embark call the actions? - - Embark actions are normal Emacs commands, that is, functions with an - interactive specification. In order to execute an action, Embark - calls the command with =call-interactively=, so the command reads user - input exactly as if run directly by the user. For example the - command may open a minibuffer and read a string - (=read-from-minibuffer=) or open a completion interface - (=completing-read=). If this happens, Embark takes the target string - and inserts it automatically into the minibuffer, simulating user - input this way. After inserting the string, Embark exits the - minibuffer, submitting the input. (The immediate minibuffer exit can - be disabled for specific actions in order to allow editing the - input; this is done by adding the =embark--allow-edit= function to the - appropriate entry of =embark-target-injection-hooks=). Embark inserts - the target string at the first minibuffer opened by the action - command, and if the command happens to prompt the user for input - more than once, the user still interacts with the second and further - prompts in the normal fashion. Note that if a command does not - prompt the user for input in the minibuffer, Embark still allows you - to use it as an action, but of course, never inserts the target - anywhere. (There are plenty of examples in the default configuration - of commands that do not prompt the user bound to keys in the action - maps, most of the region actions, for instance.) - - This is how Embark manages to reuse normal commands as actions. The - mechanism allows you to use as Embark actions commands that were not - written with Embark in mind (and indeed almost all actions that are - bound by default in Embark's action keymaps are standard Emacs - commands). It also allows you to write new custom actions in such a - way that they are useful even without Embark. - - Staring from version 28.1, Emacs has a variable - =y-or-n-p-use-read-key=, which when set to =t= causes =y-or-n-p= to use - =read-key= instead of =read-from-minibuffer=. Setting - =y-or-n-p-use-read-key= to =t= is recommended for Embark users because - it keeps Embark from attempting to insert the target at a =y-or-n-p= - prompt, which would almost never be sensible. Also consider this as - a warning to structure your own action commands so that if they use - =y-or-n-p=, they do so only after the prompting for the target. - - Here is a simple example illustrating the various ways of reading - input from the user mentioned above. Bind the following commands to - the =embark-symbol-map= to be used as actions, then put the point on - some symbol and run them with =embark-act=: - - #+begin_src emacs-lisp - (defun example-action-command1 () - (interactive) - (message "The input was `%s'." (read-from-minibuffer "Input: "))) - - (defun example-action-command2 (arg input1 input2) - (interactive "P\nsInput 1: \nsInput 2: ") - (message "The first input %swas `%s', and the second was `%s'." - (if arg "truly " "") - input1 - input2)) - - (defun example-action-command3 () - (interactive) - (message "Your selection was `%s'." - (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) - - (defun example-action-command4 () - (interactive) - (message "I don't prompt you for input and thus ignore the target!")) - - (keymap-set embark-symbol-map "X 1" #'example-action-command1) - (keymap-set embark-symbol-map "X 2" #'example-action-command2) - (keymap-set embark-symbol-map "X 3" #'example-action-command3) - (keymap-set embark-symbol-map "X 4" #'example-action-command4) - #+end_src - - Also note that if you are using the key bindings to call actions, - you can pass prefix arguments to actions in the normal way. For - example, you can use =C-u X2= with the above demonstration actions to - make the message printed by =example-action-command2= more emphatic. - This ability to pass prefix arguments to actions is useful for some - actions in the default configuration, such as - =embark-shell-command-on-buffer=. - -** Non-interactive functions as actions - - Alternatively, Embark does support one other type of action: a - non-interactive function of a single argument. The target is passed - as argument to the function. For example: - - #+begin_src emacs-lisp - (defun example-action-function (target) - (message "The target was `%s'." target)) - - (keymap-set embark-symbol-map "X 4" #'example-action-function) - #+end_src - - Note that normally binding non-interactive functions in a keymap is - useless, since when attempting to run them using the key binding you - get an error message similar to "Wrong type argument: commandp, - example-action-function". In general it is more flexible to write - any new Embark actions as commands, that is, as interactive - functions, because that way you can also run them directly, without - Embark. But there are a couple of reasons to use non-interactive - functions as actions: - - 1. You may already have the function lying around, and it is - convenient to simply reuse it. - - 2. For command actions the targets can only be simple string, with - no text properties. For certain advanced uses you may want the - action to receive a string /with/ some text properties, or even a - non-string target. - -* Embark, Marginalia and Consult - -Embark cooperates well with the [[https://github.com/minad/marginalia][Marginalia]] and [[https://github.com/minad/consult][Consult]] packages. -Neither of those packages is a dependency of Embark, but both are -highly recommended companions to Embark, for opposite reasons: -Marginalia greatly enhances Embark's usefulness, while Embark can help -enhance Consult. - -In the remainder of this section I'll explain what exactly Marginalia -does for Embark, and what Embark can do for Consult. - -** Marginalia - -Embark comes with actions for symbols (commands, functions, variables -with actions such as finding the definition, looking up the -documentation, evaluating, etc.) in the =embark-symbol-map= keymap, and -for packages (actions like install, delete, browse url, etc.) in the -=embark-package-keymap=. - -Unfortunately Embark does not automatically offers you these keymaps -when relevant, because many built-in Emacs commands don't report -accurate category metadata. For example, a command like -=describe-package=, which reads a package name from the minibuffer, -does not have metadata indicating this fact. - -In an earlier Embark version, there were functions to supply this -missing metadata, but they have been moved to Marginalia, which -augments many Emacs command to report accurate category metadata. -Simply activating =marginalia-mode= allows Embark to offer you the -package and symbol actions when appropriate again. Candidate -annotations in the Embark collect buffer are also provided by the -Marginalia package: - -- If you install Marginalia and activate =marginalia-mode=, Embark - Collect buffers will use the Marginalia annotations automatically. - -- If you don't install Marginalia, you will see only the annotations - that come with Emacs (such as key bindings in =M-x=, or the unicode - characters in =C-x 8 RET=). - -** Consult - -The excellent Consult package provides many commands that use -minibuffer completion, via the =completing-read= function; plenty of its -commands can be considered enhanced versions of built-in Emacs -commands, and some are completely new functionality. One common -enhancement provided in all commands for which it makes sense is -preview functionality, for example =consult-buffer= will show you a -quick preview of a buffer before you actually switch to it. - -If you use both Consult and Embark you should install the -=embark-consult= package which provides integration between the two. It -provides exporters for several Consult commands and also tweaks the -behavior of many Consult commands when used as actions with =embark-act= -in subtle ways that you may not even notice, but make for a smoother -experience. You need only install it to get these benefits: Embark -will automatically load it after Consult if found. - -The =embark-consult= package provides the following exporters: - -- You can use =embark-export= from =consult-line=, =consult-outline=, or - =consult-mark= to obtain an =occur-mode= buffer. As with the built-in - =occur= command you use that buffer to jump to a match and after that, - you can then use =next-error= and =previous-error= to navigate to other - matches. You can also press =e= to activate =occur-edit-mode= and edit - the matches in place! - -- You can export from any of the Consult asynchronous search commands, - =consult-grep=, =consult-git-grep=, or =consult-ripgrep= to get a - =grep-mode= buffer. Here too you can use =next-error= and =previous-error= - to navigate among matches, and, if you install the [[http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el ][wgrep]] package, - you can use it to edit the matches in place. - -In both cases, pressing =g= will rerun the Consult command you had -exported from and re-enter the input you had typed (which is similar -to reverting but a little more flexible). You can then proceed to -re-export if that's what you want, but you can also edit the input -changing the search terms or simply cancel if you see you are done -with that search. - -The =embark-consult= also contains some candidates collectors that allow -you to run =embark-live= to get a live-updating table of contents for -your buffer: - -- =embark-consult-outline-candidates= produces the outline headings of - the current buffer, using =consult-outline=. -- =embark-consult-imenu-candidates= produces the imenu items of - the current buffer, using =consult-imenu=. -- =embark-consult-imenu-or-outline-candidates= is a simple combination - of the two previous functions: it produces imenu items in buffers - deriving from =prog-mode= and otherwise outline headings. - -The way to configure =embark-live= (or =embark-collect= and =embark-export= -for that matter) to use one of these function is to add it at the end -of the =embark-candidate-collectors= list. The =embark-consult= package by -default adds the last one, which seems to be the most sensible -default. - -Besides those exporters and candidate collectors, the =embark-consult= -package provides many subtle tweaks and small integrations between -Embark and Consult. Some examples are: - -- When used as actions, the asynchronous search commands will search - only the files associated to the targets: if the targets /are/ files, - it searches those files; for buffers it will search either the - associated file if there is one, else all files in the buffer's - =default-directory=; for bookmarks it will search the file they point - to, same for Emacs Lisp libraries. This is particularly powerful - when using =embark-act-all= to act on multiple files at once, for - example you can use =consult-find= to search among file /names/ and then - =embark-act-all= and =consult-grep= to search within the matching files. - - - For all other target types, those that do not have a sensible - notion of associated file, a Consult search command (asynchronous - or not) will search for the text of the target but leave the - minibuffer open so you can interact with the Consult command. - -- =consult-imenu= will search for the target and take you directly to - the location if it matches a unique imenu entry, otherwise it will - leave the minibuffer open so you can navigate among the matches. - -* Related Packages - -There are several packages that offer functionality similar -to Embark's. - -- Acting on minibuffer completion candidates :: The popular Ivy and - Helm packages have support for acting on the completion candidates - of commands written using their APIs, and there is an extensive - ecosystem of packages meant for Helm and for Ivy (the Ivy ones - usually have "counsel" in the name) providing commands and - appropriate actions. -- Acting on things at point :: The built-in =context-menu-mode= provides - a mouse-driven context-sensitive configurable menu. The =do-at-point= - package by Philip Kaludercic (available on GNU ELPA), on the other - hand is keyboard-driven. -- Collecting completion candidates into a buffer :: The Ivy package - has the command =ivy-occur= which is similar to =embark-collect=. As - with Ivy actions, =ivy-occur= only works for commands written using - the Ivy API. - -* Resources - -If you want to learn more about how others have used Embark here are -some links to read: - -- [[https://karthinks.com/software/fifteen-ways-to-use-embark/][Fifteen ways to use Embark]], a blog post by Karthik Chikmagalur. -- [[https://protesilaos.com/dotemacs/][Protesilaos Stavrou's dotemacs]], look for the section called - "Extended minibuffer actions and more (embark.el and - prot-embark.el)" - -And some videos to watch: - -- [[https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/][Embark and my extras]] by Protesilaos Stavrou. -- [[https://youtu.be/qpoQiiinCtY][Embark -- Key features and tweaks]] by Raoul Comninos on the - Emacs-Elements YouTube channel. -- [[https://youtu.be/WsxXr1ncukY][Livestreamed: Adding an Embark context action to send a stream - message]] by Sacha Chua. -- [[https://youtu.be/qk2Is_sC8Lk][System Crafters Live! - The Many Uses of Embark]] by David Wilson. -- [[https://youtu.be/5ffb2at2d7w][Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark]] by - Mike Zamansky. - -* Contributions - -Contributions to Embark are very welcome. There is a [[https://github.com/oantolin/embark/issues/95][wish list]] for -actions, target finders, candidate collectors and exporters. For other -ideas you have for Embark, feel free to open an issue on the [[https://github.com/oantolin/embark/issues][issue -tracker]]. Any neat configuration tricks you find might be a good fit -for the [[https://github.com/oantolin/embark/wiki][wiki]]. - -Code contributions are very welcome too, but since Embark is now on -GNU ELPA, copyright assignment to the FSF is required before you can -contribute code. - -* Acknowledgments - -While I, Omar Antolín Camarena, have written most of the Embark code -and remain very stubborn about some of the design decisions, Embark -has received substantial help from a number of other people which this -document has neglected to mention for far too long. In particular, -Daniel Mendler has been absolutely invaluable, implementing several -important features, and providing a lot of useful advice. - -Code contributions: - -- [[https://github.com/minad][Daniel Mendler]] -- [[https://github.com/clemera/][Clemens Radermacher]] -- [[https://codeberg.org/jao/][José Antonio Ortega Ruiz]] -- [[https://github.com/iyefrat][Itai Y. Efrat]] -- [[https://github.com/a13][a13]] -- [[https://github.com/jakanakaevangeli][jakanakaevangeli]] -- [[https://github.com/mihakam][mihakam]] -- [[https://github.com/leungbk][Brian Leung]] -- [[https://github.com/karthink][Karthik Chikmagalur]] -- [[https://github.com/roshanshariff][Roshan Shariff]] -- [[https://github.com/condy0919][condy0919]] -- [[https://github.com/DamienCassou][Damien Cassou]] -- [[https://github.com/JimDBh][JimDBh]] - -Advice and useful discussions: - -- [[https://github.com/minad][Daniel Mendler]] -- [[https://gitlab.com/protesilaos/][Protesilaos Stavrou]] -- [[https://github.com/clemera/][Clemens Radermacher]] -- [[https://github.com/hmelman/][Howard Melman]] -- [[https://github.com/astoff][Augusto Stoffel]] -- [[https://github.com/bdarcus][Bruce d'Arcus]] -- [[https://github.com/jdtsmith][JD Smith]] -- [[https://github.com/karthink][Karthik Chikmagalur]] -- [[https://github.com/jakanakaevangeli][jakanakaevangeli]] -- [[https://github.com/iyefrat][Itai Y. Efrat]] -- [[https://github.com/mohkale][Mohsin Kaleem]] blob - 8bc9ea9a4406cb2149b341c21c0a20aa31ce4cba (mode 644) blob + /dev/null --- elpa/embark-consult-1.1/embark-consult-autoloads.el +++ /dev/null @@ -1,28 +0,0 @@ -;;; embark-consult-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from embark-consult.el - -(register-definition-prefixes "embark-consult" '("embark-consult-")) - -;;; End of scraped data - -(provide 'embark-consult-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; embark-consult-autoloads.el ends here blob - 943847c2ba4e2c70af821ac08ec0e3d034b2bdf6 (mode 644) blob + /dev/null --- elpa/embark-consult-1.1/embark-consult-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from embark-consult.el -*- no-byte-compile: t -*- -(define-package "embark-consult" "1.1" "Consult integration for Embark" '((emacs "27.1") (compat "29.1.4.0") (embark "1.0") (consult "1.0")) :commit "195add1f1ccd1059472c9df7334c97c4d155425e" :authors '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) :maintainer '("Omar Antolín Camarena" . "omar@matem.unam.mx") :keywords '("convenience") :url "https://github.com/oantolin/embark") blob - 0bdbc728799f0597f3d1fa4d1300ee344b252a3e (mode 644) blob + /dev/null --- elpa/embark-consult-1.1/embark-consult.el +++ /dev/null @@ -1,487 +0,0 @@ -;;; embark-consult.el --- Consult integration for Embark -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2023 Free Software Foundation, Inc. - -;; Author: Omar Antolín Camarena -;; Maintainer: Omar Antolín Camarena -;; Keywords: convenience -;; Version: 1.1 -;; Homepage: https://github.com/oantolin/embark -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.0") (embark "1.0") (consult "1.0")) - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This package provides integration between Embark and Consult. The package -;; will be loaded automatically by Embark. - -;; Some of the functionality here was previously contained in Embark -;; itself: - -;; - Support for consult-buffer, so that you get the correct actions -;; for each type of entry in consult-buffer's list. - -;; - Support for consult-line, consult-outline, consult-mark and -;; consult-global-mark, so that the insert and save actions don't -;; include a weird unicode character at the start of the line, and so -;; you can export from them to an occur buffer (where occur-edit-mode -;; works!). - -;; Just load this package to get the above functionality, no further -;; configuration is necessary. - -;; Additionally this package contains some functionality that has -;; never been in Embark: access to Consult preview from auto-updating -;; Embark Collect buffer that is associated to an active minibuffer -;; for a Consult command. For information on Consult preview, see -;; Consult's info manual or its readme on GitHub. - -;; If you always want the minor mode enabled whenever it possible use: - -;; (add-hook 'embark-collect-mode-hook #'consult-preview-at-point-mode) - -;; If you don't want the minor mode automatically on and prefer to -;; trigger the consult previews manually use this instead: - -;; (keymap-set embark-collect-mode-map "C-j" -;; #'consult-preview-at-point) - -;;; Code: - -(require 'embark) -(require 'consult) - -(eval-when-compile - (require 'cl-lib)) - -;;; Consult preview from Embark Collect buffers - -(defun embark-consult--collect-candidate () - "Return candidate at point in collect buffer." - (cadr (embark-target-collect-candidate))) - -(add-hook 'consult--completion-candidate-hook #'embark-consult--collect-candidate) - -;;; Support for consult-location - -(defun embark-consult--strip (string) - "Strip substrings marked with the `consult-strip' property from STRING." - (if (text-property-not-all 0 (length string) 'consult-strip nil string) - (let ((end (length string)) (pos 0) (chunks)) - (while (< pos end) - (let ((next (next-single-property-change pos 'consult-strip string end))) - (unless (get-text-property pos 'consult-strip string) - (push (substring string pos next) chunks)) - (setq pos next))) - (apply #'concat (nreverse chunks))) - string)) - -(defun embark-consult--target-strip (type target) - "Remove the unicode suffix character from a TARGET of TYPE." - (cons type (embark-consult--strip target))) - -(setf (alist-get 'consult-location embark-transformer-alist) - #'embark-consult--target-strip) - -(defun embark-consult-goto-location (target) - "Jump to consult location TARGET." - (consult--jump (car (consult--get-location target))) - (pulse-momentary-highlight-one-line (point))) - -(setf (alist-get 'consult-location embark-default-action-overrides) - #'embark-consult-goto-location) - -(defun embark-consult-export-location-occur (lines) - "Create an occur mode buffer listing LINES. -The elements of LINES should be completion candidates with -category `consult-line'." - (let ((buf (generate-new-buffer "*Embark Export Occur*")) - (mouse-msg "mouse-2: go to this occurrence") - (inhibit-read-only t) - last-buf) - (with-current-buffer buf - (dolist (line lines) - (pcase-let* - ((`(,loc . ,num) (consult--get-location line)) - ;; the text properties added to the following strings are - ;; taken from occur-engine - (lineno (propertize - (format "%7d:" num) - 'occur-prefix t - ;; Allow insertion of text at the end - ;; of the prefix (for Occur Edit mode). - 'front-sticky t - 'rear-nonsticky t - 'read-only t - 'occur-target loc - 'follow-link t - 'help-echo mouse-msg - 'font-lock-face list-matching-lines-prefix-face - 'mouse-face 'highlight)) - (contents (propertize (embark-consult--strip line) - 'occur-target loc - 'occur-match t - 'follow-link t - 'help-echo mouse-msg - 'mouse-face 'highlight)) - (nl (propertize "\n" 'occur-target loc)) - (this-buf (marker-buffer loc))) - (unless (eq this-buf last-buf) - (insert (propertize - (format "lines from buffer: %s\n" this-buf) - 'face list-matching-lines-buffer-name-face - 'read-only t)) - (setq last-buf this-buf)) - (insert lineno contents nl))) - (goto-char (point-min)) - (occur-mode)) - (pop-to-buffer buf))) - -(defun embark-consult-export-location-grep (lines) - "Create a grep mode buffer listing LINES. -Any LINES that come from a buffer which is not visiting a file -will be excluded from the grep buffer, since grep mode only works -with files. The elements of LINES should be completion -candidates with category `consult-location'. No matches will be -highlighted in the exported buffer, since the `consult-location' -candidates do not carry that information." - (let (non-file-buffers) - (embark-consult--export-grep - :header "Exported line search results (file-backed buffers only):\n\n" - :lines lines - :insert - (lambda (lines) - (let ((count 0)) - (dolist (line lines) - (pcase-let* ((`(,loc . ,num) (consult--get-location line)) - (lineno (format "%d" num)) - (contents (embark-consult--strip line)) - (buffer (marker-buffer loc)) - (file (buffer-file-name buffer))) - (if (null file) - (cl-pushnew buffer non-file-buffers) - (insert (file-relative-name file) ":" lineno ":" contents "\n") - (cl-incf count)))) - count)) - :footer - (lambda () - (when non-file-buffers - (let ((start (goto-char (point-max)))) - (insert "\nSome results were in buffers with no associated file" - " and are missing\nfrom the exported result:\n") - (dolist (buf non-file-buffers) - (insert "- " (buffer-name buf) "\n")) - (insert "\nEither save the buffers or use the" - " `embark-consult-export-location-occur'\nexporter.") - (message "This exporter does not support non-file buffers: %s" - non-file-buffers) - (add-text-properties - start (point-max) - '(read-only t wgrep-footer t front-sticky t)))))))) - -(defun embark-consult--upgrade-markers () - "Upgrade consult-location cheap markers to real markers. -This function is meant to be added to `embark-collect-mode-hook'." - (when (eq embark--type 'consult-location) - (dolist (entry tabulated-list-entries) - (when (car entry) - (consult--get-location (car entry)))))) - -;; Set default `occur-mode' based exporter for consult-line, -;; consult-line-multi, consult-outline and alike Another option is -;; using grep-mode by using `embark-consult-export-location-grep' -(setf (alist-get 'consult-location embark-exporters-alist) - #'embark-consult-export-location-occur) -(cl-pushnew #'embark-consult--upgrade-markers embark-collect-mode-hook) - -;;; Support for consult-grep - -(defvar grep-mode-line-matches) -(defvar grep-num-matches-found) -(declare-function wgrep-setup "ext:wgrep") - -(defvar-keymap embark-consult-rerun-map - :doc "A keymap with a binding for `embark-rerun-collect-or-export'." - :parent nil - "g" #'embark-rerun-collect-or-export) - -(cl-defun embark-consult--export-grep (&key header lines insert footer) - "Create a grep mode buffer listing LINES. -The HEADER string is inserted at the top of the buffer. The -function INSERT is called to insert the LINES and should return a -count of the matches (there may be more than one match per line). -The function FOOTER is called to insert a footer." - (let ((buf (generate-new-buffer "*Embark Export Grep*"))) - (with-current-buffer buf - (insert (propertize header 'wgrep-header t 'front-sticky t)) - (let ((count (funcall insert lines))) - (funcall footer) - (goto-char (point-min)) - (grep-mode) - (setq-local grep-num-matches-found count - mode-line-process grep-mode-line-matches)) - ;; Make this buffer current for next/previous-error - (setq next-error-last-buffer buf) - ;; Set up keymap before possible wgrep-setup, so that wgrep - ;; restores our binding too when the user finishes editing. - (use-local-map (make-composed-keymap - embark-consult-rerun-map - (current-local-map))) - ;; TODO Wgrep 3.0 and development versions use different names for the - ;; parser variable. - (defvar wgrep-header/footer-parser) - (defvar wgrep-header&footer-parser) - (setq-local wgrep-header/footer-parser #'ignore - wgrep-header&footer-parser #'ignore) - (when (fboundp 'wgrep-setup) (wgrep-setup))) - (pop-to-buffer buf))) - -(defun embark-consult-export-grep (lines) - "Create a grep mode buffer listing LINES. -The elements of LINES should be completion candidates with -category `consult-grep'." - (embark-consult--export-grep - :header "Exported grep results:\n\n" - :lines lines - :insert - (lambda (lines) - (dolist (line lines) (insert line "\n")) - (goto-char (point-min)) - (let ((count 0) prop) - (while (setq prop (text-property-search-forward - 'face 'consult-highlight-match t)) - (cl-incf count) - (put-text-property (prop-match-beginning prop) - (prop-match-end prop) - 'font-lock-face - 'match)) - count)) - :footer #'ignore)) - -(defun embark-consult-goto-grep (location) - "Go to LOCATION, which should be a string with a grep match." - (consult--jump (consult--grep-position location)) - (pulse-momentary-highlight-one-line (point))) - -(setf (alist-get 'consult-grep embark-default-action-overrides) - #'embark-consult-goto-grep) -(setf (alist-get 'consult-grep embark-exporters-alist) - #'embark-consult-export-grep) - -;;; Support for consult-xref - -(declare-function xref--show-xref-buffer "ext:xref") -(declare-function consult-xref "ext:consult-xref") -(defvar xref-auto-jump-to-first-xref) -(defvar consult-xref--fetcher) - -(defun embark-consult-export-xref (items) - "Create an xref buffer listing ITEMS." - (cl-flet ((xref-items (items) - (mapcar (lambda (item) (get-text-property 0 'consult-xref item)) - items))) - (let ((fetcher consult-xref--fetcher) - (input (minibuffer-contents))) - (set-buffer - (xref--show-xref-buffer - (lambda () - (let ((candidates (funcall fetcher))) - (if (null (cdr candidates)) - candidates - (catch 'xref-items - (minibuffer-with-setup-hook - (lambda () - (insert input) - (add-hook - 'minibuffer-exit-hook - (lambda () - (throw 'xref-items - (xref-items - (or - (plist-get - (embark--maybe-transform-candidates) - :candidates) - (user-error "No candidates for export"))))) - nil t)) - (consult-xref fetcher)))))) - `((fetched-xrefs . ,(xref-items items)) - (window . ,(embark--target-window)) - (auto-jump . ,xref-auto-jump-to-first-xref) - (display-action))))))) - -(setf (alist-get 'consult-xref embark-exporters-alist) - #'embark-consult-export-xref) - -;;; Support for consult-find and consult-locate - -(setf (alist-get '(file . consult-find) embark-default-action-overrides - nil nil #'equal) - #'find-file) - -(setf (alist-get '(file . consult-locate) embark-default-action-overrides - nil nil #'equal) - #'find-file) - -;;; Support for consult-isearch-history - -(setf (alist-get 'consult-isearch-history embark-transformer-alist) - #'embark-consult--target-strip) - -;;; Support for consult-man and consult-info - -(defun embark-consult-man (cand) - "Default action override for `consult-man', open CAND man page." - (man (get-text-property 0 'consult-man cand))) - -(setf (alist-get 'consult-man embark-default-action-overrides) - #'embark-consult-man) - -(declare-function consult-info--action "ext:consult-info") - -(defun embark-consult-info (cand) - "Default action override for `consult-info', open CAND info manual." - (consult-info--action cand) - (pulse-momentary-highlight-one-line (point))) - -(setf (alist-get 'consult-info embark-default-action-overrides) - #'embark-consult-info) - -(setf (alist-get 'consult-info embark-transformer-alist) - #'embark-consult--target-strip) - -;;; Bindings for consult commands in embark keymaps - -(keymap-set embark-become-file+buffer-map "C b" #'consult-buffer) -(keymap-set embark-become-file+buffer-map "C 4 b" #'consult-buffer-other-window) - -;;; Support for Consult search commands - -(defvar-keymap embark-consult-sync-search-map - :doc "Keymap for Consult sync search commands" - :parent nil - "o" #'consult-outline - "i" 'consult-imenu - "I" 'consult-imenu-multi - "l" #'consult-line - "L" #'consult-line-multi) - -(defvar-keymap embark-consult-async-search-map - :doc "Keymap for Consult async search commands" - :parent nil - "g" #'consult-grep - "r" #'consult-ripgrep - "G" #'consult-git-grep - "f" #'consult-find - "F" #'consult-locate) - -(defvar embark-consult-search-map - (keymap-canonicalize - (make-composed-keymap embark-consult-sync-search-map - embark-consult-async-search-map)) - "Keymap for all Consult search commands.") - -(fset 'embark-consult-sync-search-map embark-consult-sync-search-map) -(keymap-set embark-become-match-map "C" 'embark-consult-sync-search-map) - -(cl-pushnew 'embark-consult-async-search-map embark-become-keymaps) - -(fset 'embark-consult-search-map embark-consult-search-map) -(keymap-set embark-general-map "C" 'embark-consult-search-map) - -(map-keymap - (lambda (_key cmd) - (cl-pushnew 'embark--unmark-target - (alist-get cmd embark-pre-action-hooks)) - (cl-pushnew 'embark--allow-edit - (alist-get cmd embark-target-injection-hooks))) - embark-consult-search-map) - -(defun embark-consult--unique-match (&rest _) - "If there is a unique matching candidate, accept it. -This is intended to be used in `embark-target-injection-hooks'." - (let ((candidates (cdr (embark-minibuffer-candidates)))) - (if (or (null candidates) (cdr candidates)) - (embark--allow-edit) - (delete-minibuffer-contents) - (insert (car candidates))))) - -(dolist (cmd '(consult-outline consult-imenu consult-imenu-multi)) - (setf (alist-get cmd embark-target-injection-hooks) - (remq 'embark--allow-edit - (alist-get cmd embark-target-injection-hooks))) - (cl-pushnew #'embark-consult--unique-match - (alist-get cmd embark-target-injection-hooks))) - -(cl-defun embark-consult--async-search-dwim - (&key action type target candidates &allow-other-keys) - "DWIM when using a Consult async search command as an ACTION. -If the TYPE of the target(s) has a notion of associated -file (files, buffers, libraries and some bookmarks do), then run -the ACTION with `consult-project-function' set to nil, and search -only the files associated to the TARGET or CANDIDATES. For other -types, run the ACTION with TARGET or CANDIDATES as initial input." - (if-let ((file-fn (cdr (assq type embark--associated-file-fn-alist)))) - (let (consult-project-function) - (funcall action - (delq nil (mapcar file-fn (or candidates (list target)))))) - (funcall action nil (or target (string-join candidates " "))))) - -(map-keymap - (lambda (_key cmd) - (unless (eq cmd #'consult-locate) - (cl-pushnew cmd embark-multitarget-actions) - (cl-pushnew #'embark-consult--async-search-dwim - (alist-get cmd embark-around-action-hooks)))) - embark-consult-async-search-map) - -;;; Tables of contents for buffers: imenu and outline candidate collectors - -(defun embark-consult-outline-candidates () - "Collect all outline headings in the current buffer." - (cons 'consult-location (consult--outline-candidates))) - -(autoload 'consult-imenu--items "consult-imenu") - -(defun embark-consult-imenu-candidates () - "Collect all imenu items in the current buffer." - (cons 'imenu (mapcar #'car (consult-imenu--items)))) - -(declare-function consult-imenu--group "ext:consult-imenu") - -(defun embark-consult--imenu-group-function (type prop) - "Return a suitable group-function for imenu. -TYPE is the completion category. -PROP is the metadata property. -Meant as :after-until advice for `embark-collect--metadatum'." - (when (and (eq type 'imenu) (eq prop 'group-function)) - (consult-imenu--group))) - -(advice-add #'embark-collect--metadatum :after-until - #'embark-consult--imenu-group-function) - -(defun embark-consult-imenu-or-outline-candidates () - "Collect imenu items in prog modes buffer or outline headings otherwise." - (if (derived-mode-p 'prog-mode) - (embark-consult-imenu-candidates) - (embark-consult-outline-candidates))) - -(setf (alist-get 'imenu embark-default-action-overrides) 'consult-imenu) - -(add-to-list 'embark-candidate-collectors - #'embark-consult-imenu-or-outline-candidates - 'append) - -(provide 'embark-consult) -;;; embark-consult.el ends here blob - 40cb3e05cfbf429e200afab12345618bacb03dd4 (mode 644) blob + /dev/null --- elpa/embark-consult-1.1.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2024-04-19T11:05:04+0200 using EDDSA \ No newline at end of file blob - dc8f8ab4589a1e2d5687d7d8f820376510d45b53 (mode 644) blob + /dev/null --- elpa/esxml-0.3.7/esxml-autoloads.el +++ /dev/null @@ -1,33 +0,0 @@ -;;; esxml-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from esxml.el - -(register-definition-prefixes "esxml" '("attr" "esxml-" "pp-esxml-to-xml" "string-trim-whitespace" "sxml-to-" "xml-to-esxml")) - - -;;; Generated autoloads from esxml-query.el - -(register-definition-prefixes "esxml-query" '("esxml-")) - -;;; End of scraped data - -(provide 'esxml-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; esxml-autoloads.el ends here blob - 7c924f6cbb7c87d05ca63b34e6fcb26ffe5b917f (mode 644) blob + /dev/null --- elpa/esxml-0.3.7/esxml-pkg.el +++ /dev/null @@ -1,12 +0,0 @@ -(define-package "esxml" "0.3.7" "Library for working with xml via esxml and sxml" - '((kv "0.0.5") - (cl-lib "0.5")) - :commit "9f96449f6059cb75491dc812ddeb1b6200ec6740" :authors - '(("Evan Izaksonas-Smith ")) - :maintainer - '("Evan Izaksonas-Smith") - :keywords - '("tools" "lisp" "comm")) -;; Local Variables: -;; no-byte-compile: t -;; End: blob - 2ce1e40f26cfacc1c3862e39eb275e738a102a51 (mode 644) blob + /dev/null --- elpa/esxml-0.3.7/esxml-query.el +++ /dev/null @@ -1,783 +0,0 @@ -;;; esxml-query.el --- select esxml nodes jQuery-style - -;; Copyright (C) 2017 Vasilij Schneidermann - -;; Author: Vasilij Schneidermann -;; Maintainer: Vasilij Schneidermann -;; Version: 0.1.1 -;; Keywords: data, lisp -;; Package-Requires: ((cl-lib "0.1")) -;; -;; This program 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 of the -;; License, or (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Traditionally people pick one of the following options when faced -;; with the task of extracting data from XML in Emacs Lisp: -;; -;; - Using regular expressions on the unparsed document -;; - Manual tree traversal with `assoc', `car' and `cdr' -;; -;; Browsers faced a similar problem until jQuery happened, shortly -;; afterwards they started providing the `node.querySelector' and -;; `node.querySelectorAll' API for retrieving one or all nodes -;; matching a given CSS selector. This code implements the same API -;; with the `esxml-query' and `esxml-query-all' functions. The -;; following table summarizes the currently supported modifiers and -;; combinators: -;; -;; | Name | Supported? | Syntax | -;; |------------------------------------+------------+-------------| -;; | Namespaces | No | foo|bar | -;; | Commas | Yes | foo, bar | -;; | Descendant combinator | Yes | foo bar | -;; | Child combinator | Yes | foo>bar | -;; | Adjacent sibling combinator | No | foo+bar | -;; | General sibling combinator | No | foo~bar | -;; | Universal selector | Yes | * | -;; | Type selector | Yes | tag | -;; | ID selector | Yes | #foo | -;; | Class selector | Yes | .foo | -;; | Attribute selector | Yes | [foo] | -;; | Exact match attribute selector | Yes | [foo=bar] | -;; | Prefix match attribute selector | Yes | [foo^=bar] | -;; | Suffix match attribute selector | Yes | [foo$=bar] | -;; | Substring match attribute selector | Yes | [foo*=bar] | -;; | Include match attribute selector | Yes | [foo~=bar] | -;; | Dash match attribute selector | Yes | [foo|=bar] | -;; | Attribute selector modifiers | No | [foo=bar i] | -;; | Pseudo elements | No | ::foo | -;; | Pseudo classes | No | :foo | - -;;; Code: - -(require 'cl-lib) - - -;;; CSS selector parsing - -;; https://www.w3.org/TR/selectors/#w3cselgrammar -;; https://www.w3.org/TR/selectors4/#grammar -;; https://www.w3.org/TR/2003/WD-css3-syntax-20030813/#detailed-grammar -;; https://www.w3.org/TR/2003/WD-css3-syntax-20030813/#tokenization - -;; you might be wondering why I'm using both level 3 and 4 standards, -;; well, the level 3 one has a buggy lexer section whereas level 4 -;; omits crucial parser definitions, so both have to be used... - -;; TODO: support :not -(defvar esxml--css-selector-token-matchers - (let* ((h "[0-9a-f]") - (nl "\n\\|\r\n\\|\r\\|\f") - (nonascii "[\200-\U0010ffff]") - (unicode (format "\\\\%s\\{1,6\\}[ \t\r\n\f]?" h)) - (escape (format "\\(?:%s\\)\\|\\\\[ -~\200-\U0010ffff]" unicode)) - (nmstart (format "[a-z_]\\|%s\\|\\(?:%s\\)" nonascii escape)) - (nmchar (format "[a-z0-9_-]\\|%s\\|\\(?:%s\\)" nonascii escape)) - (num "[0-9]+\\|[0-9]*\\.[0-9]+") - (string1 (format "\"\\(?:[\t !#$%%&(-~]\\|\\\\\\(?:%s\\)\\|'\\|%s\\|\\(?:%s\\)\\)*\"" nl nonascii escape)) - (string2 (format "'\\(?:[\t !#$%%&(-~]\\|\\\\\\(?:%s\\)\\|\"\\|%s\\|\\(?:%s\\)\\)*'" nl nonascii escape)) - (ident (format "[-]?\\(?:%s\\)\\(?:%s\\)*" nmstart nmchar)) - (unit (format "[-]?\\(?:%s\\)\\(?:%s\\)+" nmstart nmchar)) - (name (format "\\(?:%s\\)+" nmchar))) - - `((whitespace . "[ \t\r\n\f]+") - (string . ,(format "\\(?:%s\\|%s\\)" string1 string2)) - (ident . ,ident) - (hash . ,(format "#%s" name)) - (function . ,(format "%s(" ident)) - (number . ,num) - (dimension . ,(format "\\(?:%s\\)%s" num unit)) - (prefix-match . "\\^=") - (suffix-match . "\\$=") - (substring-match . "\\*=") - (include-match . "~=") - (dash-match . "|=") - (comma . ",") - (gt . ">") - (plus . "\\+") - (minus . "-") - (tilde . "~") - (asterisk . "\\*") - (period . "\\.") - (equals . "=") - (colon . ":") - (lbracket . "\\[") - (rbracket . "\\]") - (rparen . ")")))) - -(defun esxml--tokenize-css-selector (string) - (let (result) - (with-temp-buffer - (insert string) - (goto-char (point-min)) - (while (not (eobp)) - (let ((max-length 0) - longest) - (dolist (matcher esxml--css-selector-token-matchers) - (let ((id (car matcher)) - (re (cdr matcher))) - (when (looking-at re) - (let* ((token (match-string 0)) - (length (length token))) - (when (> length max-length) - (setq max-length length) - (setq longest (cons id token))))))) - (when (not longest) - (error "Invalid token detected: %s" - (buffer-substring (point) (point-max)))) - (push longest result) - (goto-char (+ (point) max-length))))) - (nreverse result))) - -;; the alternative is creating a mutable object with peek/next methods -;; and passing it around, so I chose the one requiring less typing, a -;; dynamically bound variable :< - -(defvar esxml--token-stream) - -;; TODO: support :not -;; css-selector: -;; css-selector-list; -;; css-selector-list: -;; complex-css-selector [ comma whitespace* complex-css-selector ]*; -;; complex-css-selector: -;; compound-css-selector [ css-combinator compound-css-selector ]* whitespace*; -;; css-combinator: -;; whitespace+ | whitespace* [ '>' | '+' | '~' ] whitespace*; -;; compound-css-selector: -;; css-type-selector css-modifier* | css-modifier+; -;; css-type-selector: -;; IDENT | *; -;; css-modifier: -;; css-id | css-class | css-attrib | css-pseudo; -;; css-id: -;; HASH; -;; css-class: -;; '.' IDENT; -;; css-attrib: -;; '[' whitespace* css-attrib-name ']' -;; | '[' whitespace* css-attrib-name css-attrib-match css-attrib-value whitespace* ']'; -;; css-attrib-name: -;; IDENT whitespace*; -;; css-attrib-match: -;; [ '=' | PREFIX-MATCH | SUFFIX-MATCH | SUBSTRING-MATCH | INCLUDE-MATCH | DASH-MATCH ] whitespace*; -;; css-attrib-value: -;; IDENT | STRING; -;; css-pseudo: -;; ':' ':'? [ IDENT | css-functional-pseudo ]; -;; css-functional-pseudo: -;; FUNCTION whitespace* [ css-expression whitespace* ]+ ')'; -;; css-expression: -;; '+' | '-' | DIMENSION | NUMBER | STRING | IDENT - -(defun esxml-query-css-escape (string) - "Returns escaped version of STRING for use in selectors. -The logic used here corresponds to the CSS.escape API as -specified in https://drafts.csswg.org/cssom/#the-css.escape()-method." - (let (chars) - (dotimes (i (length string)) - (let* ((char (aref string i)) - (unprintablep (or (and (>= char ?\u0001) (<= char ?\u001f)) - (= char ?\u007f))) - (nonasciip (>= char ?\u0080)) - (digitp (and (>= char ?\u0030) (<= char ?\u0039))) - (upperp (and (>= char ?\u0041) (<= char ?\u005a))) - (lowerp (and (>= char ?\u0061) (<= char ?\u007a)))) - (cond - ((= char ?\u0000) - (push ?\ufffd chars)) - (unprintablep - (dolist (char (string-to-list (format "\\%x " char))) - (push char chars))) - ((and (= i 0) digitp) - (dolist (char (string-to-list (format "\\%x " char))) - (push char chars))) - ((and (= i 1) digitp (= (aref string 0) ?-)) - (dolist (char (string-to-list (format "\\%x " char))) - (push char chars))) - ((and (= i 0) (= char ?-) (= (length string) 1)) - (push ?\\ chars) - (push char chars)) - ((or nonasciip (= char ?-) (= char ?_) digitp upperp lowerp) - (push char chars)) - (t - (push ?\\ chars) - (push char chars))))) - (concat (nreverse chars)))) - -(defun esxml--parse-css-identifier (string) - ;; https://www.w3.org/TR/css-syntax-3/#consume-string-token - (let* ((code-points (string-to-list string)) - chars - token) - (while code-points - (let ((char (pop code-points))) - (if (= char ?\\) - (let ((char (pop code-points))) - (cond - ((not char)) - ((= char ?\n)) - ((or (and (>= char ?0) (<= char ?9)) - (and (>= char ?a) (<= char ?f)) - (and (>= char ?A) (<= char ?F))) - (let ((i 0) - (hex-chars (list char))) - (while (and (< i 5) code-points) - (let ((char (car code-points))) - (if (or (and (>= char ?0) (<= char ?9)) - (and (>= char ?a) (<= char ?f)) - (and (>= char ?A) (<= char ?F))) - (push (pop code-points) hex-chars) - (setq i 5))) - (setq i (1+ i))) - (let ((char (car code-points))) - (when (and char (= char ?\s)) - (pop code-points))) - (let* ((hex-token (concat (nreverse hex-chars))) - (code-point (string-to-number hex-token 16))) - (if (or (zerop code-point) - (and (>= code-point ?\ud800) (<= code-point ?\udfff)) - (> code-point ?\U0010ffff)) - (push ?\ufffd chars) - (push code-point chars))))) - (t ; unspecified: non-hex digit - (push char chars)))) - (push char chars)))) - (concat (nreverse chars)))) - -(defun esxml--parse-css-string-literal (string) - (esxml--parse-css-identifier (substring string 1 -1))) - -(defmacro esxml--with-parse-shorthands (&rest body) - `(cl-macrolet ((peek () '(car esxml--token-stream)) - (next () '(pop esxml--token-stream)) - (accept (type) `(and (peek) (eq (car (peek)) ,type) - (cdr (next)))) - (eat-whitespace () '(while (accept 'whitespace)))) - ,@body)) -(def-edebug-spec esxml--with-parse-shorthands (body)) - -(defun esxml-parse-css-selector (string) - "Parse CSS selector STRING into a list of alists. -Each alist represents a complex CSS selector. The result can be -passed to `esxml-query' and `esxml-query-all' as the selector -argument." - (let* ((esxml--token-stream (esxml--tokenize-css-selector string)) - (result (esxml--parse-css-selector-list))) - (when esxml--token-stream - (error "Trailing garbage: %s" - (mapconcat 'cdr esxml--token-stream ""))) - result)) - -(defun esxml--parse-css-selector-list () - (esxml--with-parse-shorthands - (let ((first (esxml--parse-complex-css-selector)) - result) - (when (not first) - (error "Expected at least one selector")) - (push first result) - - (while (accept 'comma) - (eat-whitespace) - (let ((selector (esxml--parse-complex-css-selector))) - (when (not selector) - (error "Expected selector after comma")) - (push selector result))) - (nreverse result)))) - -(defun esxml--parse-complex-css-selector () - (esxml--with-parse-shorthands - (let ((first (esxml--parse-compound-css-selector)) - result done) - (when first - (push first result) - - (while (not done) - (let ((combinator (esxml--parse-css-combinator))) - (if combinator - (let ((compound (esxml--parse-compound-css-selector))) - (cond - (compound - (setq result (append (list compound combinator) result))) - ;; allow whitespace before comma - ((not (eq (car (peek)) 'comma)) - (error "Trailing combinator")))) - (setq done t)))) - (nreverse result))))) - -(defun esxml--parse-css-combinator () - (esxml--with-parse-shorthands - ;; NOTE: whitespace-surrounded combinators are distinguished from - ;; whitespace-only ones by checking whether there has been - ;; whitespace followed by a non-blank combinator - (let ((leading-whitespace-p (eq (car (peek)) 'whitespace)) - result) - (eat-whitespace) - (let ((type (car (peek)))) - (cond - ((member type '(gt plus tilde)) - (next) - (cond - ((eq type 'gt) - (setq result '((combinator . child)))) - ((eq type 'plus) - (setq result '((combinator . direct-sibling)))) - ((eq type 'tilde) - (setq result '((combinator . indirect-sibling))))) - (eat-whitespace)) - (leading-whitespace-p - (setq result '((combinator . descendant)))) - (t nil))) - result))) - -(defun esxml--parse-compound-css-selector () - (esxml--with-parse-shorthands - (let ((type-selector (esxml--parse-css-type-selector)) - done - result) - ;; NOTE: css-type-selector css-modifier* | css-modifier+; is - ;; equivalent to: [ css-type-selector | css-modifier ] css-modifier*; - (if type-selector - (push type-selector result) - (let ((modifier (esxml--parse-css-modifier))) - (if modifier - (push modifier result) - ;; NOTE: this allows the trailing combinator error to be thrown - (setq done t)))) - - (while (not done) - (let ((modifier (esxml--parse-css-modifier))) - (if modifier - (push modifier result) - (setq done t)))) - (when (> (cl-count 'id result :key 'car) 1) - (error "Only one id selector allowed per compound")) - (nreverse result)))) - -(defun esxml--parse-css-type-selector () - (esxml--with-parse-shorthands - (let ((token (peek))) - (cond - ((eq (car token) 'ident) - (next) - (cons 'tag (intern (esxml--parse-css-identifier (cdr token))))) - ((eq (car token) 'asterisk) - (next) - '(wildcard)) - (t nil))))) - -(defun esxml--parse-css-modifier () - (or (esxml--parse-css-id) - (esxml--parse-css-class) - (esxml--parse-css-attrib) - (esxml--parse-css-pseudo))) - -(defun esxml--parse-css-id () - (esxml--with-parse-shorthands - (let ((value (accept 'hash))) - (when value - (cons 'id (substring value 1)))))) - -(defun esxml--parse-css-class () - (esxml--with-parse-shorthands - (when (accept 'period) - (let ((value (accept 'ident))) - (if value - (cons 'class value) - (error "Expected identifier after period")))))) - -(defun esxml--parse-css-attrib () - (esxml--with-parse-shorthands - (let (result) - (when (accept 'lbracket) - (eat-whitespace) - (let ((name (esxml--parse-css-attrib-name))) - (when (not name) - (error "Expected attribute name")) - (push (cons 'name (esxml--parse-css-identifier name)) result) - (when (not (accept 'rbracket)) - (let ((match (esxml--parse-css-attrib-match))) - (when (not match) - (error "Expected attribute matcher")) - (let ((value (esxml--parse-css-attrib-value))) - (when (not value) - (error "Expected attribute value")) - (eat-whitespace) - (when (not (accept 'rbracket)) - (error "Unterminated attribute")) - (push (cons match value) result))))) - (cons 'attribute (nreverse result)))))) - -(defun esxml--parse-css-attrib-name () - (esxml--with-parse-shorthands - (let ((name (accept 'ident))) - (when name - (eat-whitespace) - name)))) - -(defun esxml--parse-css-attrib-match () - (esxml--with-parse-shorthands - (let (result) - (cond - ((accept 'equals) - (setq result 'exact-match)) - ((accept 'prefix-match) - (setq result 'prefix-match)) - ((accept 'suffix-match) - (setq result 'suffix-match)) - ((accept 'substring-match) - (setq result 'substring-match)) - ((accept 'include-match) - (setq result 'include-match)) - ((accept 'dash-match) - (setq result 'dash-match))) - (eat-whitespace) - result))) - -(defun esxml--parse-css-attrib-value () - (esxml--with-parse-shorthands - (let ((token (peek))) - (cond - ((eq (car token) 'ident) - (next) - (esxml--parse-css-identifier (cdr token))) - ((eq (car token) 'string) - (next) - (esxml--parse-css-string-literal (cdr token))) - (t nil))))) - -(defun esxml--parse-css-pseudo () - (esxml--with-parse-shorthands - (let (result type) - (when (accept 'colon) - (if (accept 'colon) - (setq type 'pseudo-element) - (setq type 'pseudo-class)) - (let ((functional (esxml--parse-css-functional-pseudo))) - (if functional - (if (eq type 'pseudo-class) - (let ((value (car functional)) - (args (cdr functional))) - (push (cons 'name (esxml--parse-css-identifier value)) result) - (push (cons 'args args) result)) - (error "Pseudo-elements may not have arguments")) - (let ((value (accept 'ident))) - (if value - (push (cons 'name (esxml--parse-css-identifier value)) result) - (error "Expected function or identifier"))))) - (cons type (nreverse result)))))) - -(defun esxml--parse-css-functional-pseudo () - (esxml--with-parse-shorthands - (let ((function (accept 'function)) - result) - (when function - (push (substring function 0 -1) result) - (eat-whitespace) - (let ((expression (esxml--parse-css-expression)) - done) - (eat-whitespace) - (when (not expression) - (error "Expected at least one expression for function")) - (push expression result) - (while (not done) - (setq expression (esxml--parse-css-expression)) - (if expression - (progn - (push expression result) - (eat-whitespace)) - (setq done t)))) - (when (not (accept 'rparen)) - (error "Unterminated function argument list")) - (nreverse result))))) - -(defun esxml--parse-css-expression () - (esxml--with-parse-shorthands - (let ((token (peek))) - (cond - ((accept 'plus) - '(operator . +)) - ((accept 'minus) - '(operator . -)) - ((eq (car token) 'dimension) - (next) - (cons 'dimension (esxml--parse-css-identifier (cdr token)))) - ((eq (car token) 'number) - (next) - (cons 'number (string-to-number (cdr token)))) - ((eq (car token) 'string) - (next) - (cons 'string (esxml--parse-css-string-literal (cdr token)))) - ((eq (car token) 'ident) - (next) - (cons 'ident (esxml--parse-css-identifier (cdr token)))) - (t nil))))) - - -;;; tree traversal - -;; TODO: these helpers should be part of esxml.el -(defun esxml-branch-p (node) - "Non-nil if NODE refers to an esxml branch." - (and (listp node) - (>= (length node) 2) - (symbolp (car node)) - (listp (cadr node)))) - -(defun esxml-node-tag (node) - "Returns the tag of NODE if available." - (and (esxml-branch-p node) - (car node))) - -(defun esxml-node-attributes (node) - "Returns the attributes of NODE if available." - (and (esxml-branch-p node) - (cadr node))) - -(defun esxml-node-attribute (attribute node) - "Returns the attribute ATTRIBUTE of NODE if available." - (and (esxml-branch-p node) - (cdr (assq attribute (cadr node))))) - -(defun esxml-node-children (node) - "Returns the children of NODE if available." - (and (esxml-branch-p node) - (nthcdr 2 node))) - -(defun esxml-find-node (pred root) - "Locates a node satisfying PRED starting from ROOT. -Returns the node or nil if none found." - (if (funcall pred root) - root - (cl-some (lambda (node) (esxml-find-node pred node)) - (esxml-node-children root)))) - -(defun esxml-visit-nodes (function root) - "Visit nodes by calling FUNCTION on each starting from ROOT." - (funcall function root) - (mapc (lambda (node) (esxml-visit-nodes function node)) - (esxml-node-children root))) - -(defun esxml-find-nodes (pred root) - "Locates all nodes satisfying PRED starting from ROOT. -Returns a list of the nodes or nil if none found." - (let ((acc '())) - (esxml-visit-nodes - (lambda (node) - (when (funcall pred node) - (push node acc))) - root) - (nreverse acc))) - -(defun esxml-find-descendant (pred root) - "Locates a node satisfying PRED starting from ROOT's children. -Returns the node or nil if none found." - (cl-some (lambda (node) (esxml-find-node pred node)) - (esxml-node-children root))) - -(defun esxml-find-descendants (pred root) - "Locates all nodes satisfying PRED starting from ROOT's children. -Returns a list of the nodes or nil if none found." - (cl-mapcan (lambda (node) (esxml-find-nodes pred node)) - (esxml-node-children root))) - -(defun esxml-find-child (pred root) - "Locates a node satisfying PRED among ROOT's children. -Returns the node or nil if none found." - (cl-some (lambda (node) (when (funcall pred node) node)) - (esxml-node-children root))) - -(defun esxml-find-children (pred root) - "Locates all nodes satisfying PRED among ROOT's children. -Returns a list of the nodes or nil if none found." - (mapcar (lambda (node) (when (funcall pred node) node)) - (esxml-node-children root))) - -(defun esxml--node-with-children (node children) - (let ((tag (esxml-node-tag node)) - (attributes (esxml-node-attributes node))) - (append (list tag attributes) children))) - -(defun esxml--node-with-attributes (node attributes) - (let ((tag (esxml-node-tag node)) - (children (esxml-node-children node))) - (append (list tag attributes) children))) - -(defun esxml-tree-map (function root) - "Returns a copy of ROOT with FUNCTION applied to each node." - (if (esxml-branch-p root) - (esxml--node-with-children - (funcall function root) - (mapcar (lambda (node) (esxml-tree-map function node)) - (esxml-node-children root))) - (funcall function root))) - -(defvar esxml--symbol (make-symbol "id")) - -(defun esxml--decorate-tree (root) - (let ((i 0)) - (esxml-tree-map - (lambda (node) - (let ((attribute (cons esxml--symbol i)) - (attributes (esxml-node-attributes node))) - (setq attributes (append (list attribute) attributes)) - (setq i (1+ i)) - (if (esxml-branch-p node) - (esxml--node-with-attributes node attributes) - node))) - root))) - -(defun esxml--undecorate-node (node) - (if (esxml-branch-p node) - (let ((attributes (esxml-node-attributes node))) - (esxml--node-with-attributes node (assq-delete-all esxml--symbol - attributes))) - node)) - -(defun esxml--retrieve-decoration (node) - (esxml-node-attribute esxml--symbol node)) - - -;;; querying - -;; NOTE: supporting structural pseudo functions, direct siblings and -;; indirect siblings requires breadth instead of depth traversal, -;; something that could be emulated without zippers if you had the -;; parent of the node (and the position of the child)... - -(defun esxml--node-matches-attribute-p (node modifier) - (let ((attributes (esxml-node-attributes node)) - haystack) - (cl-every - (lambda (item) - (let ((type (car item)) - (value (cdr item))) - (cond - ((eq type 'name) - (let ((match (assq (intern value) attributes))) - (setq haystack (cdr match)) - match)) - ((eq type 'exact-match) - (equal haystack value)) - ((eq type 'prefix-match) - (string-prefix-p value haystack)) - ((eq type 'suffix-match) - (string-suffix-p value haystack)) - ((eq type 'substring-match) - (string-match-p (regexp-quote value) haystack)) - ((eq type 'include-match) - (member value (split-string haystack " "))) - ((eq type 'dash-match) - (or (equal value haystack) - (string-match-p (format "^%s-" (regexp-quote value)) haystack))) - (t (error "Unknown attribute modifier"))))) - modifier))) - -(defun esxml--node-matches-modifier-p (node type value) - (cond - ((eq type 'wildcard) - t) - ((eq type 'tag) - (equal (esxml-node-tag node) value)) - ((eq type 'id) - (equal (esxml-node-attribute 'id node) value)) - ((eq type 'class) - (let ((class (esxml-node-attribute 'class node))) - (and class (member value (split-string class " "))))) - ((eq type 'attribute) - (esxml--node-matches-attribute-p node value)) - ;; TODO: support structural pseudo functions - ;; TODO: error out on invalid pseudo-class arguments - (t (error "Unimplemented attribute type: %s" type)))) - -(defun esxml--find-node-for (attributes) - (lambda (node) - (cl-every - (lambda (attribute) - (let ((type (car attribute)) - (value (cdr attribute))) - (esxml--node-matches-modifier-p node type value))) - attributes))) - -(defun esxml--find-nodes (root combinator attributes) - (let* ((type (cdr (assq 'combinator combinator))) - (walker (cond - ((not type) - 'esxml-find-nodes) - ((eq type 'descendant) - 'esxml-find-descendants) - ((eq type 'child) - 'esxml-find-children) - ;; TODO: support direct sibling - ;; TODO: support indirect sibling - (t (error "Unimplemented combinator %s" combinator))))) - (funcall walker (esxml--find-node-for attributes) root))) - -(defun esxml--query (selector root) - (let* ((attributes (pop selector)) - combinator - (result (esxml--find-nodes root nil attributes))) - (while (and result selector) - (setq combinator (pop selector)) - (setq attributes (pop selector)) - (setq result (cl-mapcan - (lambda (node) - (esxml--find-nodes node combinator attributes)) - result)) - (setq result (delq nil result))) - result)) - -(defun esxml--delete-dups (items test) - (let ((seen (make-hash-table :test test)) - result) - (while items - (let ((item (pop items))) - (when (not (gethash item seen)) - (push item result) - (puthash item t seen)))) - (nreverse result))) - -(defun esxml-query-all (selector root) - "Locates all nodes satisfying SELECTOR starting from ROOT. -SELECTOR must be a string containing a CSS selector or a parsed -CSS selector returned by `esxml-parse-css-selector'. Returns a -list of the nodes or nil if none found." - (when (stringp selector) - (setq selector (esxml-parse-css-selector selector))) - (if (= (length selector) 1) - ;; no commas, we can only get the same nodes repeatedly - (esxml--delete-dups (esxml--query (car selector) root) 'eq) - ;; commas, nodes might be the same *and* in the wrong order - (setq root (esxml--decorate-tree root)) - (let (result) - (while selector - (setq result (nconc result (esxml--query (pop selector) root)))) - (setq result (cl-sort result '< :key 'esxml--retrieve-decoration)) - (setq result (cl-delete-duplicates result :test '= - :key 'esxml--retrieve-decoration)) - (mapcar (lambda (node) (esxml--undecorate-node node)) result)))) - -(defun esxml-query (selector root) - "Locates a node satisfying SELECTOR starting from ROOT. -SELECTOR must be a string containing a CSS selector or a parsed -CSS selector returned by `esxml-parse-css-selector'. Returns the -node or nil if none found." - ;; NOTE: you can do a bit less work (the savings decrease the more - ;; branches the query discards), but it's simpler and safer to just - ;; have the same algorithm for both entry points - (car (esxml-query-all selector root))) - -(provide 'esxml-query) -;;; esxml-query.el ends here blob - b123249fe1bde22636dbf028a62f11037c5bb540 (mode 644) blob + /dev/null --- elpa/esxml-0.3.7/esxml.el +++ /dev/null @@ -1,266 +0,0 @@ -;;; esxml.el --- Library for working with xml via esxml and sxml -;; Copyright (C) 2012 - -;; Author: Evan Izaksonas-Smith -;; Maintainer: Evan Izaksonas-Smith -;; Created: 15th August 2012 -;; Version: 0.3.7 -;; Keywords: tools, lisp, comm -;; Description: A library for easily generating XML/XHTML in elisp -;; -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This is XML/XHTML done with S-Expressions in EmacsLisp. Simply, -;; this is the easiest way to write HTML or XML in Lisp. -;; -;; This library uses the native form of XML representation as used by -;; many libraries already included within emacs. This representation -;; will be referred to as "esxml" throughout this library. See -;; `esxml-to-xml' for a concise description of the format. -;; -;; This library is not intended to be used directly by a user, though -;; it certainly could be. It could be used to generate static html, -;; or use a library like `elnode' to serve dynamic pages. Or even to -;; extract a form from a site to produce an API. -;; -;; TODO: Better documentation, more convenience. -;; -;; NOTICE: Code base will be transitioning to using pcase instead of -;; destructuring bind wherever possible. If this leads to hard to -;; debug code, please let me know, and I will do whatever I can to -;; resolve these issues. -;; -;;; Code: -(require 'cl-lib) -(require 'xml) -(require 'pcase) - -(defun string-trim-whitespace (string) - "A simple function, strips the whitespace from beginning and -end of the string. Leaves all other whitespace untouched." - (replace-regexp-in-string - (rx string-start (* whitespace) - (group (+? anything)) - (* whitespace) string-end) - "\\1" - string)) - -(defun esxml-trim-ws (esxml) - "This may cause problems, is intended for parsing xml into sxml -but may eroneously delete desirable white space." - (if (stringp esxml) (string-trim-whitespace esxml) - (pcase-let ((`(,tag ,attrs . ,body) esxml)) - `(,tag ,attrs - ,@(mapcar 'esxml-trim-ws body))))) - -(defun attrp (attr) - "Returns t if attr is a an esxml attribute. -An esxml attribute is a cons of the form (symbol . string)" - (and (consp attr) - (symbolp (car attr)) - (stringp (cdr attr)))) - -(defun esxml--convert-pair (attr) - "Converts from cons cell to attribute pair. Not intended for -general use." - (pcase-let ((`(,car . ,cdr) attr)) - (cl-check-type cdr string) - (concat (symbol-name car) - "=" - (prin1-to-string cdr)))) - -(defun attrsp (attrs) - "Returns t if attrs is a list of esxml attributes. - -See: `attrp'" - (and (listp attrs) - (cl-every (lambda (attr) - (and (consp attr) - (symbolp (car attr)) - (stringp (cdr attr)))) - attrs))) - -(defun esxml-validate-form (esxml) - "A fast esxml validator. Will error on invalid subparts making -it suitable for hindsight testing." - (cond ((stringp esxml) nil) - ((< (length esxml) 2) - (error "%s is too short to be a valid esxml expression" esxml)) - (t (pcase-let ((`(,tag ,attrs . ,body) esxml)) - (cl-check-type tag symbol) - (cl-check-type attrs attrs) - (mapcar 'esxml-validate-form body))))) - -;; While the following could certainly have been written using format, -;; concat makes them easier to read. Update later if neccesary for -;; efficiency. - -;; Though at first glance the recursive nature of this function might -;; give one pause, since xml is a recursive data type, a recursive -;; parser is an optimal strategy. each node will be visited exactly -;; once during the transformation. -;; -;; Further, since a string is a terminal node and since xml can be -;; represented as a string, non dynamic portions of the page may be -;; precached quite easily. -(defun esxml--to-xml-recursive (esxml) - (pcase esxml - ((pred stringp) - esxml) - (`(comment nil ,body) - (concat "")) - (`(,tag ,attrs . ,body) - ;; code goes here to catch invalid data. - (concat "<" (symbol-name tag) - (when attrs - (concat " " (mapconcat 'esxml--convert-pair attrs " "))) - (if body - (concat ">" (mapconcat 'esxml--to-xml-recursive body "") - "") - "/>"))))) - -(defun esxml-to-xml (esxml) - "This translates an esxml expression, i.e. that which is -returned by xml-parse-region. The structure is defined as a -string or a list where the first element is the tag the second is -an alist of attribute value pairs and the remainder of the list -is 0 or more esxml elements. - - (TAG ATTRS &rest BODY) || STRING - -TAG: is the tag and must be a symbol. - -ATTRS: is an alist of attribute pairs each pair must be of the - form (KEY . VALUE). - -KEY: is the name of the attribute and must be a symbol. - -VALUE: is the value of the attribute and must be a string. - -BODY: is zero or more esxml expressions. Having no body forms - implies that the tag should be self closed. If there is - one or more body forms the tag will always be explicitly - closed, even if they are the empty string. - -STRING: if the esxml expression is a string it is returned - unchanged, this allows for caching of any constant parts, - such as headers and footers. -" - (condition-case nil - (esxml--to-xml-recursive esxml) - (error (esxml-validate-form esxml)))) - -(defun pp-esxml-to-xml (esxml) - "This translates an esxml expresion as `esxml-to-xml' but -indents it for ease of human readability, it is neccesarrily -slower and will produce longer output." - (pcase esxml - ((pred stringp) - esxml) - (`(comment nil ,body) - (concat "")) - (`(,tag ,attrs . ,body) - (cl-check-type tag symbol) - (cl-check-type attrs attrs) - (concat "<" (symbol-name tag) - (when attrs - (concat " " (mapconcat 'esxml--convert-pair attrs " "))) - (if body - (concat ">" (if (cl-every 'stringp body) - (mapconcat 'identity body " ") - (concat "\n" - (replace-regexp-in-string - "^" " " - (mapconcat 'pp-esxml-to-xml body "\n")) - "\n")) - "") - "/>"))) - (_ - (error "%s is not a valid esxml expression" esxml)))) - -(defun sxml-to-esxml (sxml) - "Translates sxml to esxml so the common standard can be used. -See: http://okmij.org/ftp/Scheme/SXML.html." - (pcase sxml - (`(,tag (@ . ,attrs) . ,body) - `(,tag ,(mapcar (lambda (attr) - (cons (car attr) - (or (cadr attr) - (prin1-to-string (car attr))))) - attrs) - ,@(mapcar 'sxml-to-esxml body))) - (`(,tag . ,body) - `(,tag nil - ,@(mapcar 'sxml-to-esxml body))) - ((and sxml (pred stringp)) sxml))) - -(defun sxml-to-xml (sxml) - "Translates sxml to xml, via esxml, hey it's only a constant -factor. :)" - (esxml-to-xml (sxml-to-esxml sxml))) - - - -;; TODO: make agnostic with respect to libxml vs xml.el -(defun xml-to-esxml (string &optional trim) - (with-temp-buffer - (insert string) - (let ((parse-tree (libxml-parse-xml-region (point-min) - (point-max)))) - (if trim - (esxml-trim-ws parse-tree) - parse-tree)))) - -;; TODO, move to esxpath when mature -(defun esxml-get-by-key (esxml key value) - "Returns a list of all elements whose wttribute KEY match -VALUE. KEY should be a symbol, and VALUE should be a string. -Will not recurse below a match." - (unless (stringp esxml) - (pcase-let ((`(,tag ,attrs . ,body) esxml)) - (if (equal value - (assoc-default key attrs)) - (list esxml) - (apply 'append (mapcar (lambda (sexp) - (esxml-get-by-key sexp key value)) - body)))))) - -(defun esxml-get-tags (esxml tags) - "Returns a list of all elements whose tag is a member of TAGS. -TAGS should be a list of tags to be matched against. Will not -recurse below a match." - (unless (stringp esxml) - (pcase-let ((`(,tag ,attrs . ,body) esxml)) - (if (member tag tags) - (list esxml) - (apply 'append (mapcar (lambda (sexp) - (esxml-get-tags sexp tags)) - body)))))) - -(defun esxml-get-forms (esxml) - "Returns a list of all forms." - (esxml-get-tags esxml '(form))) - -;; taken from kv -(defmacro esxml-destructuring-mapcar (args sexp seq) - (declare (indent 2)) - (let ((entry (make-symbol "entry"))) - `(mapcar (lambda (,entry) - (cl-destructuring-bind ,args ,entry ,sexp)) - ,seq))) - -(provide 'esxml) -;;; esxml.el ends here blob - e34065995ffe43b6070735d1d8b9a4a3f50a10da (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: emacs-lisp -before_install: - - curl -fsSkL https://gist.github.com/rejeep/ebcd57c3af83b049833b/raw > x.sh && source ./x.sh - - evm install $EVM_EMACS --use --skip - - cask -env: - - EVM_EMACS=emacs-24.4-travis - - EVM_EMACS=emacs-24.5-travis - - EVM_EMACS=emacs-25.1-travis - - EVM_EMACS=emacs-git-snapshot-travis -script: - ./run-travis-ci.sh - -matrix: - allow_failures: - - env: EVM_EMACS=emacs-git-snapshot-travis blob - f288702d2fa16d3cdf0035b15a9fcbc552cd88e7 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program 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 of the License, or - (at your option) any later version. - - This program 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 this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. blob - dc15d71be4d6e6e7401167fa1676769848557adb (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/Cask +++ /dev/null @@ -1,8 +0,0 @@ -(source melpa) - -(package "expand-region" "0.8.0" "Increase selected region by semantic units.") - -(development - (depends-on "ecukes") - (depends-on "espuds") - (depends-on "undercover")) blob - 876dbca5e2c125c029c8e26f5f1b6e75853306a0 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/README.md +++ /dev/null @@ -1,247 +0,0 @@ -[![Build Status](https://secure.travis-ci.org/magnars/expand-region.el.png)](http://travis-ci.org/magnars/expand-region.el) -[![Coverage Status](https://coveralls.io/repos/magnars/expand-region.el/badge.svg?branch=master&service=github)](https://coveralls.io/github/magnars/expand-region.el) -[![GNU ELPA](https://elpa.gnu.org/packages/expand-region.svg)](https://elpa.gnu.org/packages/expand-region.html) -[![MELPA](https://melpa.org/packages/expand-region-badge.svg)](https://melpa.org/#/expand-region) -[![MELPA Stable](https://stable.melpa.org/packages/expand-region-badge.svg)](https://stable.melpa.org/#/expand-region) - -# expand-region.el - -Expand region increases the selected region by semantic units. Just keep -pressing the key until it selects what you want. - -An example: - - (setq alphabet-start "abc def") - -With the cursor at the `c`, it starts by marking the entire word `abc`, then -expand to the contents of the quotes `abc def`, then to the entire quote -`"abc def"`, then to the contents of the sexp `setq alphabet-start "abc def"` -and finally to the entire sexp. - -You can set it up like this: - - (require 'expand-region) - (global-set-key (kbd "C-=") 'er/expand-region) - -If you expand too far, you can contract the region by pressing `-` (minus key), -or by prefixing the shortcut you defined with a negative argument: `C-- C-=`. - -## Maintenance warning - -I use this package every day, and have been doing so for years. It just works. -At least, it works for all my use cases. And if it breaks somehow, I fix it. - -However, it has become painfully clear to me that I don't have time to fix -problems I don't have. It's been years since I could keep pace with the issues -and pull requests. Whenever I try, I keep getting feedback that my fix isn't -good enough by some standard I don't particularly care about. - -So, I have closed the issue tracker and the pull requests. I hope you can -happily use this package, just like I do. If it doesn't work for you, then I'm -sorry. Thankfully Emacs is infinitely malleable, you can probably fix it -yourself. - -TLDR: *I am still maintaining this package*, but I am no longer crowdsourcing a list of issues. - -## Video - -You can [watch an intro to expand-region at Emacs Rocks](http://emacsrocks.com/e09.html). - -## Installation - -I highly recommend installing expand-region through elpa. - -It's available on [MELPA](https://melpa.org/): - - M-x package-install expand-region - -Via [use-package](https://github.com/jwiegley/use-package): - - (use-package expand-region - :bind ("C-=" . er/expand-region)) - -## Language support - -Expand region works fairly well with most languages, due to the general -nature of the basic expansions: - - er/mark-word - er/mark-symbol - er/mark-symbol-with-prefix - er/mark-next-accessor - er/mark-method-call - er/mark-inside-quotes - er/mark-outside-quotes - er/mark-inside-pairs - er/mark-outside-pairs - er/mark-comment - er/mark-url - er/mark-email - er/mark-defun - -However, most languages also will benefit from some specially crafted -expansions. For instance, expand-region comes with these extra expansions for -html-mode: - - er/mark-html-attribute - er/mark-inner-tag - er/mark-outer-tag - -You can add your own expansions to the languages of your choice simply by -creating a function that looks around point to see if it's inside or looking -at the construct you want to mark, and if so - mark it. - -There's plenty of examples to look at in these files. - -After you make your function, add it to a buffer-local version of -the `er/try-expand-list`. - -**Example:** - -Let's say you want expand-region to also mark paragraphs and pages in -text-mode. Incidentally Emacs already comes with `mark-paragraph` and -`mark-page`. To add it to the try-list, do this: - - (defun er/add-text-mode-expansions () - (make-variable-buffer-local 'er/try-expand-list) - (setq er/try-expand-list (append - er/try-expand-list - '(mark-paragraph - mark-page)))) - - (add-hook 'text-mode-hook 'er/add-text-mode-expansions) - -Add that to its own file, and add it to the `expand-region.el`-file, -where it says "Mode-specific expansions" - -**Warning:** Badly written expansions might slow down expand-region -dramatically. Remember to exit quickly before you start traversing -the entire document looking for constructs to mark. - -## Contribute - -If you make some nice expansions for your favorite mode, it would be -great if you opened a pull-request. The repo is at: - - https://github.com/magnars/expand-region.el - -All changes must be accompanied by feature tests. -They are written in [Ecukes](http://ecukes.info), a Cucumber for Emacs. - -To fetch the test dependencies, install -[cask](https://github.com/rejeep/cask.el) if you haven't already, -then: - - $ cd /path/to/expand-region - $ cask - -Run the tests with: - - $ ./run-tests.sh - -If feature tests are missing for the mode you are changing, please make -sure to add a set of basic tests around the functionality you're changing. - -## Contributors - -* [Josh Johnston](https://github.com/joshwnj) contributed `er/contract-region` -* [Le Wang](https://github.com/lewang) contributed consistent handling of the mark ring, expanding into pairs/quotes just left of the cursor, and general code clean-up. -* [Raimon Grau](https://github.com/kidd) added support for when transient-mark-mode is off. -* [Roland Walker](https://github.com/rolandwalker) added option to copy the contents of the most recent action to a register, and some fixes. -* [Damien Cassou](https://github.com/DamienCassou) added option to continue expanding/contracting with fast keys after initial expand. -* [Sylvain Rousseau](https://github.com/thisirs) fixed loads of little annoyances. -* [Ryan Mulligan](https://github.com/ryantm) cleaned up a lot of byte compilation warnings. -* [Lefteris Karapetsas](https://github.com/LefterisJP) added subword-mode expansions. - -### Language specific contributions - -* [Matt Briggs](https://github.com/mbriggs), [Jorge Dias](https://github.com/diasjorge) and [Le Wang](https://github.com/lewang) contributed Ruby expansions. -* [Ivan Andrus](https://github.com/gvol), [fgeller](https://github.com/fgeller), [edmccard](https://github.com/edmccard) and [Rotem Yaari](https://github.com/vmalloc) contributed Python expansions. -* [François Févotte](https://github.com/ffevotte) contributed C and C++ expansions. -* [Ivan Andrus](https://github.com/gvol) contributed text-mode, LaTeX-mode and nxml-mode expansions. -* [Gleb Peregud](https://github.com/gleber) contributed Erlang expansions. -* [Mark Hepburn](https://github.com/markhepburn) contributed Octave expansions. -* [Rotem Yaari](https://github.com/vmalloc) also contributed an adapter for the region expansion in web-mode. -* [Kang-min Liu](https://github.com/gugod) contributed Perl expansions. -* [Alexis Gallagher](https://github.com/algal) contributs Standard ML expansions. -* [Matt Price](https://github.com/titaniumbones) improved on org-mode expansions. -* [Maksim Grinman](https://github.com/maksle) added inner-quotes expansion for nxml-mode. -* [Andrea Orru](https://github.com/AndreaOrru) added `expand-region-smart-cursor`. - -Thanks! - -## Changelog - -### From 0.11 to 0.12 (WIP) - -* Option `expand-region-subword-enabled` to enable subword expansions -* Improve web-mode expansions (Renato F) -* Fixes for cc-mode expansions (Wilfred Hughes) -* Fixes for org-mode expansions (Wilfred Hughes) -* Fix unnecessary unfolding in org-mode -* Fix bug with transient-mark-mode (Russell Black) -* Fix problems with auto-loading (Philippe Vaucher, Wilfred Hughes) - -### From 0.10 to 0.11 - -* Option `expand-region-smart-cursor` to keep cursor at beginning of region if it is there (Andrea Orru) -* Add subword-mode expansions (Lefteris Karapetsas) -* Improve enh-ruby-mode expansions (Ryan Davis) -* Improve nxml-mode expansions (Maksim Grinman) -* Improve org-mode expansions (Matt Price) -* Improve js-mode expansions -* Better performance -* Lots of bugfixes - -### From 0.9 to 0.10 - -* Smarter expansion of ruby heredoc contents (Steve Purcell) -* Add enh-ruby-mode expansions (Bradley Wright) -* Add basic expansion er/mark-defun -* Big cleanup of byte compilation warnings (Ryan Mulligan) -* Better performance -* Lots of bugfixes - -### From 0.8 to 0.9 - -* Improve org-, clojure-, python-, latex-, cc- and ruby-modes -* Add basic expansions: email and url -* Add sml-mode expansions (Alexis Gallagher) -* Add cperl-mode expansions (Kang-min Liu) -* Add octave-mode expansions (Mark Hepburn) -* Add web-mode expansions (Rotem Yaari) -* Use Carton for dev-dependencies -* Fix bad behavior in minibuffer (Sylvain Rousseau) -* More robust comment expansions -* Improve loading of expansions for all major modes - -### From 0.7 to 0.8 - -* Improve js-, ruby-, python- and latex-modes -* Support built-in javascript-mode -* Handle narrowed buffers correctly -* Include mode-specific expansions when autoloading -* Provide option to copy the contents of the most recent action to a register -* Add cc-mode specific expansions -* Add customization to turn off skipping whitespace when expanding -* Continue expanding/contracting with one key press (optional) - -## License - -Copyright (C) 2011-2019 Magnar Sveen - -Author: Magnar Sveen -Keywords: marking region - -This program 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 of the License, or -(at your option) any later version. - -This program 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 this program. If not, see . blob - 126b7f19e0526633a46686095b5a2824082fea2e (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/cc-mode-expansions.el +++ /dev/null @@ -1,186 +0,0 @@ -;;; cc-mode-expansions.el --- C-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: François Févotte -;; Based on js-mode-expansions by: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: -;; -;; Extra expansions for C-like modes that I've found useful so far: -;; -;; er/c-mark-statement -;; Captures simple and more complex statements. -;; -;; er/c-mark-fully-qualified-name -;; Captures identifiers composed of several '::'-separated parts. -;; -;; er/c-mark-function-call[-1|-2] -;; Captures an identifier followed by a '()'-enclosed block. -;; -;; er/c-mark-statement-block[-1|-2] -;; Captures a statement followed by a '{}'-enclosed block. -;; This matches function definitions and if/for/... constructs. -;; -;; er/c-mark-vector-access[-1|-2] -;; Captures an identifier followed by a '[]'-enclosed block. -;; -;; Feel free to contribute any other expansions for C at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(require 'er-basic-expansions) -(require 'cc-cmds) - -(defun er/c-mark-statement () - "Mark the current C statement. - -This function tries to ensure that pair-delimited substring are -either fully inside or fully outside the statement." - (interactive) - (unless (use-region-p) - (set-mark (point))) - - (if (< (point) (mark)) - (exchange-point-and-mark)) - - ;; Contract the region a bit to make the - ;; er/c-mark-statement function idempotent - (when (>= (- (point) (mark)) 2) - (exchange-point-and-mark) - (forward-char) - (exchange-point-and-mark) - (backward-char)) - - (let (beg end) - ;; Determine boundaries of the outside-pairs region - (save-mark-and-excursion - (c-end-of-statement) - (er/mark-outside-pairs) - (setq beg (point) - end (mark))) - - ;; Determine boundaries of the statement as given - ;; by c-beginning-of-statement/c-end-of-statement - (c-end-of-statement) - (exchange-point-and-mark) - (c-end-of-statement)(c-beginning-of-statement 1) - - ;; If the two regions overlap, expand the region - (cond ((and (<= (point) beg) - (< (mark) end)) - (set-mark end)) - ((and (> (point) beg) - (>= (mark) end)) - (goto-char beg) - (c-end-of-statement) - (c-beginning-of-statement 1))))) - -(defun er/c-mark-fully-qualified-name () - "Mark the current C++ fully qualified identifier. - -This function captures identifiers composed of multiple -'::'-separated parts." - (interactive) - (er/mark-symbol) - (when (use-region-p) - (when (> (point) (mark)) - (exchange-point-and-mark)) - (while (er/looking-back-exact "::") - (backward-char 2) - (skip-syntax-backward "_w")) - (exchange-point-and-mark) - (while (looking-at "::") - (forward-char 2) - (skip-syntax-forward "_w")) - (exchange-point-and-mark))) - -(defmacro er/c-define-construct (name mark-first-part open-brace doc) - (let ((docstring (make-symbol "docstring-tmp"))) - (setq docstring - (concat - doc "\n\n" - "This function tries to mark a region consisting of two parts:\n" - (format " - the first part is marked using `%s'\n" (symbol-name mark-first-part)) - (format " - the second part is a block beginning with %S\n\n" open-brace))) - `(progn - (defun ,(intern (concat (symbol-name name) "-1")) () - ,(concat docstring - "This function assumes that point is in the first part and the\n" - "region is active.\n\n" - (format "See also `%s'." (concat (symbol-name name) "-2"))) - (interactive) - (when (use-region-p) - (,mark-first-part) - (exchange-point-and-mark) - (let ((oldpos (point))) - (skip-syntax-forward " ") - (if (looking-at ,open-brace) - (progn (forward-sexp) - (exchange-point-and-mark)) - (goto-char oldpos))))) - (defun ,(intern (concat (symbol-name name) "-2")) () - ,(concat docstring - "This function assumes that the block constituting the second part\n" - "is already marked and active.\n\n" - (format "See also `%s'." (concat (symbol-name name) "-1"))) - (interactive) - (when (use-region-p) - (when (> (point) (mark)) - (exchange-point-and-mark)) - (when (looking-at ,open-brace) - (let ((beg (point)) - (end (progn (forward-sexp 1) - (point)))) - (goto-char beg) - (skip-syntax-backward " ") - (backward-char) - (deactivate-mark) - (,mark-first-part) - (set-mark end)))))))) - -(er/c-define-construct er/c-mark-function-call er/c-mark-fully-qualified-name "(" - "Mark the current function call.") -(er/c-define-construct er/c-mark-statement-block er/c-mark-statement "{" - "Mark the current block construct (like if, for, etc.)") -(er/c-define-construct er/c-mark-vector-access er/c-mark-fully-qualified-name "\\[" - "Mark the current vector access.") - -(defun er/add-cc-mode-expansions () - "Adds expansions for buffers in c-mode." - (set (make-local-variable 'er/try-expand-list) - (append er/try-expand-list - '(er/c-mark-statement - er/c-mark-fully-qualified-name - er/c-mark-function-call-1 er/c-mark-function-call-2 - er/c-mark-statement-block-1 er/c-mark-statement-block-2 - er/c-mark-vector-access-1 er/c-mark-vector-access-2)))) - -(er/enable-mode-expansions 'c-mode #'er/add-cc-mode-expansions) -(er/enable-mode-expansions 'c++-mode #'er/add-cc-mode-expansions) -(er/enable-mode-expansions 'objc-mode #'er/add-cc-mode-expansions) -(er/enable-mode-expansions 'java-mode #'er/add-cc-mode-expansions) -(er/enable-mode-expansions 'idl-mode #'er/add-cc-mode-expansions) -(er/enable-mode-expansions 'pike-mode #'er/add-cc-mode-expansions) -(er/enable-mode-expansions 'awk-mode #'er/add-cc-mode-expansions) - -(provide 'cc-mode-expansions) - -;; cc-mode-expansions.el ends here blob - 17c75d20adcbb91f444e41adfb2f5c600e9823b2 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/clojure-mode-expansions.el +++ /dev/null @@ -1,106 +0,0 @@ -;;; clojure-mode-expansions.el --- Clojure-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Extra expansions for clojure-mode: -;; -;; * `er/mark-clj-word` - includes dashes, but not slashes. -;; * `er/mark-clj-regexp-literal` -;; * `er/mark-clj-function-literal` -;; -;; Feel free to contribute any other expansions for Clojure at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(require 'er-basic-expansions) - -(defun er/mark-clj-word () - "Mark the entire word around or in front of point, including dashes." - (interactive) - (let ((word-regexp "\\(\\sw\\|-\\)")) - (when (or (looking-at word-regexp) - (er/looking-back-on-line word-regexp)) - (while (looking-at word-regexp) - (forward-char)) - (set-mark (point)) - (while (er/looking-back-on-line word-regexp) - (backward-char))))) - -(defun er/mark-clj-set-literal () - "Mark clj-set-literal presumes that point is outside the brackets. -If point is inside the brackets, those will be marked first anyway." - (interactive) - (when (or (looking-at "#{") - (er/looking-back-exact "#")) - (forward-char 1) - (search-backward "#") - (set-mark (point)) - (search-forward "{") - (forward-char -1) - (forward-list 1) - (exchange-point-and-mark))) - -(defun er/mark-clj-regexp-literal () - "Mark clj-regexp-literal presumes that point is outside the string. -If point is inside the string, the quotes will be marked first anyway." - (interactive) - (when (or (looking-at "#\"") - (er/looking-back-exact "#")) - (forward-char 1) - (search-backward "#") - (set-mark (point)) - (search-forward "\"") - (forward-char 1) - (er--move-point-forward-out-of-string) - (exchange-point-and-mark))) - -(defun er/mark-clj-function-literal () - "Mark clj-function-literal presumes that point is outside the parens. -If point is inside the parens, they will be marked first anyway." - (interactive) - (when (or (looking-at "#(") - (er/looking-back-exact "#")) - (forward-char) - (search-backward "#") - (set-mark (point)) - (search-forward "(") - (backward-char) - (forward-list) - (exchange-point-and-mark))) - -(defun er/add-clojure-mode-expansions () - "Adds clojure-specific expansions for buffers in clojure-mode" - (set (make-local-variable 'er/try-expand-list) (append - er/try-expand-list - '(er/mark-clj-word - er/mark-clj-regexp-literal - er/mark-clj-set-literal - er/mark-clj-function-literal)))) - -(er/enable-mode-expansions 'clojure-mode #'er/add-clojure-mode-expansions) -(er/enable-mode-expansions 'nrepl-mode #'er/add-clojure-mode-expansions) - -(provide 'clojure-mode-expansions) - -;; clojure-mode-expansions.el ends here blob - b9e4e2e47e2e44be4f45a5e53c1d36456997c97b (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/cperl-mode-expansions.el +++ /dev/null @@ -1,67 +0,0 @@ -;;; cperl-mode-expansions.el --- perl-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Kang-min Liu -;; Keywords: marking region cperl - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Code: - -(require 'expand-region-core) - -(defun er/mark-cperl-variable-name () - "Marks one perl variable" - (interactive) - (forward-word) - (backward-word) - (search-backward-regexp "[@$%]" (line-beginning-position)) - (set-mark (point)) - (forward-char) - (search-forward-regexp "[^a-z_]" (line-end-position)) - (backward-char) - (exchange-point-and-mark)) - -(defun er/mark-cperl-package-name () - "Marks one perl package name" - (interactive) - (forward-sexp) - (backward-sexp) - (set-mark (point)) - (forward-sexp) - (search-backward "::" (line-beginning-position)) - (exchange-point-and-mark)) - -(defun er/mark-cperl-subroutine () - "Marks current subroutine body." - (interactive) - (end-of-defun) - (set-mark (point)) - (beginning-of-defun)) - -(defun er/add-cperl-mode-expansions () - "Add cprel mode expansinos" - (set (make-local-variable 'er/try-expand-list) (append - er/try-expand-list - '(er/mark-cperl-variable-name - er/mark-cperl-package-name - er/mark-cperl-subroutine - )))) - -(er/enable-mode-expansions 'cperl-mode #'er/add-cperl-mode-expansions) - -(provide 'cperl-mode-expansions) - -;; cperl-mode-expansions.el ends here blob - 3ab14cb32c0bcd14826e259c24311af2213c8b63 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/css-mode-expansions.el +++ /dev/null @@ -1,52 +0,0 @@ -;;; css-mode-expansions.el --- CSS-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; For now I have only found the need for mark-css-declaration. -;; -;; Feel free to contribute any other expansions for CSS at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) - -(defun er/mark-css-declaration () - "Marks one CSS declaration, eg. font-weight: bold;" - (interactive) - (search-backward-regexp "[;{] ?" (line-beginning-position)) - (forward-char) - (set-mark (point)) - (search-forward ";" (line-end-position)) - (exchange-point-and-mark)) - -(defun er/add-css-mode-expansions () - "Adds CSS-specific expansions for buffers in css-mode" - (set (make-local-variable 'er/try-expand-list) (append - er/try-expand-list - '(er/mark-css-declaration)))) - -(er/enable-mode-expansions 'css-mode #'er/add-css-mode-expansions) - -(provide 'css-mode-expansions) - -;; css-mode-expansions.el ends here blob - 4e26802d7372013b602c9c96329e6a07a53964e9 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/enh-ruby-mode-expansions.el +++ /dev/null @@ -1,44 +0,0 @@ -;;; enh-ruby-mode-expansions.el --- Expansions for enh-ruby-mode -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; enh-ruby-mode doesn't use ruby-mode's mark-defun - it has its own. -;; -;; Feel free to contribute any other expansions for enh-ruby-mode at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) - -(defun er/add-enh-ruby-mode-expansions () - "Adds Ruby-specific expansions for buffers in enh-ruby-mode" - (require 'ruby-mode-expansions) - - (set (make-local-variable 'er/try-expand-list) (append - (remove 'er/mark-defun er/try-expand-list) - '(er/mark-ruby-instance-variable - er/mark-ruby-block-up)))) - -(er/enable-mode-expansions 'enh-ruby-mode #'er/add-enh-ruby-mode-expansions) - -(provide 'enh-ruby-mode-expansions) blob - 2e771a5a5568b4a5e69f7fe39e01b660c876614d (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/er-basic-expansions.el +++ /dev/null @@ -1,246 +0,0 @@ -;;; er-basic-expansions.el --- Words, symbols, strings, et al -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Expansions that are useful in any major mode. - -;;; Code: - -(require 'expand-region-core) - -(defun er/mark-word () - "Mark the entire word around or in front of point." - (interactive) - (let ((word-regexp "\\sw")) - (when (or (looking-at word-regexp) - (er/looking-back-on-line word-regexp)) - (skip-syntax-forward "w") - (set-mark (point)) - (skip-syntax-backward "w")))) - -(defun er/mark-symbol () - "Mark the entire symbol around or in front of point." - (interactive) - (let ((symbol-regexp "\\s_\\|\\sw")) - (when (or (looking-at symbol-regexp) - (er/looking-back-on-line symbol-regexp)) - (skip-syntax-forward "_w") - (set-mark (point)) - (skip-syntax-backward "_w")))) - -(defun er/mark-symbol-with-prefix () - "Mark the entire symbol around or in front of point, including prefix." - (interactive) - (let ((symbol-regexp "\\s_\\|\\sw") - (prefix-regexp "\\s'")) - (when (or (looking-at prefix-regexp) - (looking-at symbol-regexp) - (er/looking-back-on-line symbol-regexp)) - (skip-syntax-forward "'") - (skip-syntax-forward "_w") - (set-mark (point)) - (skip-syntax-backward "_w") - (skip-syntax-backward "'")))) - -;; Mark method call - -(defun er/mark-next-accessor () - "Presumes that current symbol is already marked, skips over one -period and marks next symbol." - (interactive) - (when (use-region-p) - (when (< (point) (mark)) - (exchange-point-and-mark)) - ;; (let ((symbol-regexp "\\s_\\|\\sw")) - (when (looking-at "\\.") - (forward-char 1) - (skip-syntax-forward "_w") - (exchange-point-and-mark)))) ;; ) - -(defun er/mark-method-call () - "Mark the current symbol (including dots) and then paren to closing paren." - (interactive) - (let ((symbol-regexp "\\(\\s_\\|\\sw\\|\\.\\)+")) - (when (or (looking-at symbol-regexp) - (er/looking-back-on-line symbol-regexp)) - (skip-syntax-backward "_w.") - (set-mark (point)) - (when (looking-at symbol-regexp) - (goto-char (match-end 0))) - (if (looking-at "(") - (forward-list)) - (exchange-point-and-mark)))) - -;; Comments - -(defun er--point-is-in-comment-p () - "t if point is in comment, otherwise nil" - (or (nth 4 (syntax-ppss)) - (memq (get-text-property (point) 'face) '(font-lock-comment-face font-lock-comment-delimiter-face)))) - -(defun er/mark-comment () - "Mark the entire comment around point." - (interactive) - (when (er--point-is-in-comment-p) - (let ((p (point))) - (while (and (er--point-is-in-comment-p) (not (eobp))) - (forward-char 1)) - (skip-chars-backward "\n\r") - (set-mark (point)) - (goto-char p) - (while (er--point-is-in-comment-p) - (forward-char -1)) - (forward-char 1)))) - -;; Quotes - -(defun er--current-quotes-char () - "The char that is the current quote delimiter" - (nth 3 (syntax-ppss))) - -(defalias 'er--point-inside-string-p #'er--current-quotes-char) - -(defun er--move-point-forward-out-of-string () - "Move point forward until it exits the current quoted string." - (er--move-point-backward-out-of-string) - (forward-sexp)) - -(defun er--move-point-backward-out-of-string () - "Move point backward until it exits the current quoted string." - (goto-char (nth 8 (syntax-ppss)))) - -(defun er/mark-inside-quotes () - "Mark the inside of the current string, not including the quotation marks." - (interactive) - (when (er--point-inside-string-p) - (er--move-point-backward-out-of-string) - (forward-char) - (set-mark (point)) - (er--move-point-forward-out-of-string) - (backward-char) - (exchange-point-and-mark))) - -(defun er/mark-outside-quotes () - "Mark the current string, including the quotation marks." - (interactive) - (if (er--point-inside-string-p) - (er--move-point-backward-out-of-string) - (when (and (not (use-region-p)) - (er/looking-back-on-line "\\s\"")) - (backward-char) - (er--move-point-backward-out-of-string))) - (when (looking-at "\\s\"") - (set-mark (point)) - (forward-char) - (er--move-point-forward-out-of-string) - (exchange-point-and-mark))) - -;; Pairs - ie [] () {} etc - -(defun er--point-inside-pairs-p () - "Is point inside any pairs?" - (> (car (syntax-ppss)) 0)) - -(defun er/mark-inside-pairs () - "Mark inside pairs (as defined by the mode), not including the pairs." - (interactive) - (when (er--point-inside-pairs-p) - (goto-char (nth 1 (syntax-ppss))) - (set-mark (save-excursion - (forward-char 1) - (skip-chars-forward er--space-str) - (point))) - (forward-list) - (backward-char) - (skip-chars-backward er--space-str) - (exchange-point-and-mark))) - -(defun er--looking-at-pair () - "Is point looking at an opening pair char?" - (looking-at "\\s(")) - -(defun er--looking-at-marked-pair () - "Is point looking at a pair that is entirely marked?" - (and (er--looking-at-pair) - (use-region-p) - (>= (mark) - (save-excursion - (forward-list) - (point))))) - -(defun er/mark-outside-pairs () - "Mark pairs (as defined by the mode), including the pair chars." - (interactive) - (if (and (er/looking-back-on-line "\\s)+\\=") - (not (er--looking-at-pair))) - (ignore-errors (backward-list 1)) - (skip-chars-forward er--space-str)) - (when (and (er--point-inside-pairs-p) - (or (not (er--looking-at-pair)) - (er--looking-at-marked-pair))) - (goto-char (nth 1 (syntax-ppss)))) - (when (er--looking-at-pair) - (set-mark (point)) - (forward-list) - (exchange-point-and-mark))) - -(require 'thingatpt) - -(defun er/mark-url () - (interactive) - (end-of-thing 'url) - (set-mark (point)) - (beginning-of-thing 'url)) - -(defun er/mark-email () - (interactive) - (end-of-thing 'email) - (set-mark (point)) - (beginning-of-thing 'email)) - -(defun er/mark-defun () - "Mark defun around or in front of point." - (interactive) - (end-of-defun) - (skip-chars-backward er--space-str) - (set-mark (point)) - (beginning-of-defun) - (skip-chars-forward er--space-str)) - -;; Methods to try expanding to -(setq er/try-expand-list - (append '(er/mark-word - er/mark-symbol - er/mark-symbol-with-prefix - er/mark-next-accessor - er/mark-method-call - er/mark-inside-quotes - er/mark-outside-quotes - er/mark-inside-pairs - er/mark-outside-pairs - er/mark-comment - er/mark-url - er/mark-email - er/mark-defun) - er/try-expand-list)) - -(provide 'er-basic-expansions) -;;; er-basic-expansions.el ends here blob - 9970c5d3448186503f3e184b84c6f1ee400c8947 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/erlang-mode-expansions.el +++ /dev/null @@ -1,47 +0,0 @@ -;;; erlang-mode-expansions.el --- Erlang-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Gleb Peregud -;; Based on python-mode-expansions by: Ivan Andrus -;; Keywords: marking region erlang - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Feel free to contribute any other expansions for Erlang at -;; -;; https://github.com/magnars/expand-region.el - -;;; Bugs: - -;; Doesn't handle many Erlang syntax constructs, just the basics - -;;; Code: - -(require 'expand-region-core) - -(defun er/add-erlang-mode-expansions () - "Adds Erlang-specific expansions for buffers in erlang-mode" - (set (make-local-variable 'er/try-expand-list) (append - er/try-expand-list - '(erlang-mark-function - erlang-mark-clause)))) - -(er/enable-mode-expansions 'erlang-mode #'er/add-erlang-mode-expansions) - -(provide 'erlang-mode-expansions) - -;; erlang-mode-expansions.el ends here blob - 5ba35a844a7e6e04feb1fd46cc9ff88e0d84d325 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/expand-region-autoloads.el +++ /dev/null @@ -1,219 +0,0 @@ -;;; expand-region-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from cc-mode-expansions.el - -(register-definition-prefixes "cc-mode-expansions" '("er/")) - - -;;; Generated autoloads from clojure-mode-expansions.el - -(register-definition-prefixes "clojure-mode-expansions" '("er/")) - - -;;; Generated autoloads from cperl-mode-expansions.el - -(register-definition-prefixes "cperl-mode-expansions" '("er/")) - - -;;; Generated autoloads from css-mode-expansions.el - -(register-definition-prefixes "css-mode-expansions" '("er/")) - - -;;; Generated autoloads from enh-ruby-mode-expansions.el - -(register-definition-prefixes "enh-ruby-mode-expansions" '("er/add-enh-ruby-mode-expansions")) - - -;;; Generated autoloads from er-basic-expansions.el - -(register-definition-prefixes "er-basic-expansions" '("er--" "er/mark-")) - - -;;; Generated autoloads from erlang-mode-expansions.el - -(register-definition-prefixes "erlang-mode-expansions" '("er/add-erlang-mode-expansions")) - - -;;; Generated autoloads from expand-region.el - -(autoload 'er/expand-region "expand-region" "\ -Increase selected region by semantic units. - -With prefix argument expands the region that many times. -If prefix argument is negative calls `er/contract-region'. -If prefix argument is 0 it resets point and mark to their state -before calling `er/expand-region' for the first time. - -(fn ARG)" t) - - -;;; Generated autoloads from expand-region-core.el - -(autoload 'er/contract-region "expand-region-core" "\ -Contract the selected region to its previous size. -With prefix argument contracts that many times. -If prefix argument is negative calls `er/expand-region'. -If prefix argument is 0 it resets point and mark to their state -before calling `er/expand-region' for the first time. - -(fn ARG)" t) -(register-definition-prefixes "expand-region-core" '("er--" "er/")) - - -;;; Generated autoloads from expand-region-custom.el - -(let ((loads (get 'expand-region 'custom-loads))) (if (member '"expand-region-custom" loads) nil (put 'expand-region 'custom-loads (cons '"expand-region-custom" loads)) (put 'tools 'custom-loads (cons 'expand-region (get 'tools 'custom-loads))))) -(defvar expand-region-preferred-python-mode 'python "\ -The name of your preferred python mode") -(custom-autoload 'expand-region-preferred-python-mode "expand-region-custom" t) -(defvar expand-region-guess-python-mode t "\ -If expand-region should attempt to guess your preferred python mode") -(custom-autoload 'expand-region-guess-python-mode "expand-region-custom" t) -(defvar expand-region-autocopy-register "" "\ -If set to a string of a single character (try \"e\"), then the -contents of the most recent expand or contract command will -always be copied to the register named after that character.") -(custom-autoload 'expand-region-autocopy-register "expand-region-custom" t) -(defvar expand-region-skip-whitespace t "\ -If expand-region should skip past whitespace on initial expansion") -(custom-autoload 'expand-region-skip-whitespace "expand-region-custom" t) -(defvar expand-region-fast-keys-enabled t "\ -If expand-region should bind fast keys after initial expand/contract") -(custom-autoload 'expand-region-fast-keys-enabled "expand-region-custom" t) -(defvar expand-region-contract-fast-key "-" "\ -Key to use after an initial expand/contract to contract once more.") -(custom-autoload 'expand-region-contract-fast-key "expand-region-custom" t) -(defvar expand-region-reset-fast-key "0" "\ -Key to use after an initial expand/contract to undo.") -(custom-autoload 'expand-region-reset-fast-key "expand-region-custom" t) -(defvar expand-region-exclude-text-mode-expansions '(html-mode nxml-mode) "\ -List of modes which derive from `text-mode' for which text mode expansions are not appropriate.") -(custom-autoload 'expand-region-exclude-text-mode-expansions "expand-region-custom" t) -(defvar expand-region-smart-cursor nil "\ -Defines whether the cursor should be placed intelligently after expansion. - -If set to t, and the cursor is already at the beginning of the new region, -keep it there; otherwise, put it at the end of the region. - -If set to nil, always place the cursor at the beginning of the region.") -(custom-autoload 'expand-region-smart-cursor "expand-region-custom" t) -(define-obsolete-variable-alias 'er/enable-subword-mode? 'expand-region-subword-enabled "\ -2019-03-23") -(defvar expand-region-subword-enabled nil "\ -Whether expand-region should use subword expansions.") -(custom-autoload 'expand-region-subword-enabled "expand-region-custom" t) -(register-definition-prefixes "expand-region-custom" '("expand-region-")) - - -;;; Generated autoloads from feature-mode-expansions.el - -(register-definition-prefixes "feature-mode-expansions" '("er--block-between-keywords" "er/")) - - -;;; Generated autoloads from html-mode-expansions.el - -(register-definition-prefixes "html-mode-expansions" '("er--" "er/")) - - -;;; Generated autoloads from js-mode-expansions.el - -(register-definition-prefixes "js-mode-expansions" '("er/")) - - -;;; Generated autoloads from js2-mode-expansions.el - -(register-definition-prefixes "js2-mode-expansions" '("er/add-js2-mode-expansions" "js2-mark-parent-statement")) - - -;;; Generated autoloads from jsp-expansions.el - -(register-definition-prefixes "jsp-expansions" '("er/")) - - -;;; Generated autoloads from latex-mode-expansions.el - -(register-definition-prefixes "latex-mode-expansions" '("er/")) - - -;;; Generated autoloads from nxml-mode-expansions.el - -(register-definition-prefixes "nxml-mode-expansions" '("er/")) - - -;;; Generated autoloads from octave-expansions.el - -(register-definition-prefixes "octave-expansions" '("er/")) - - -;;; Generated autoloads from python-el-expansions.el - -(register-definition-prefixes "python-el-expansions" '("er--python-string-delimiter" "er/")) - - -;;; Generated autoloads from python-el-fgallina-expansions.el - -(register-definition-prefixes "python-el-fgallina-expansions" '("er--python-" "er/")) - - -;;; Generated autoloads from python-mode-expansions.el - -(register-definition-prefixes "python-mode-expansions" '("er--" "er/" "py-goto-beyond-clause")) - - -;;; Generated autoloads from ruby-mode-expansions.el - -(register-definition-prefixes "ruby-mode-expansions" '("er/")) - - -;;; Generated autoloads from sml-mode-expansions.el - -(register-definition-prefixes "sml-mode-expansions" '("er/")) - - -;;; Generated autoloads from subword-mode-expansions.el - -(register-definition-prefixes "subword-mode-expansions" '("er/")) - - -;;; Generated autoloads from text-mode-expansions.el - -(register-definition-prefixes "text-mode-expansions" '("er/")) - - -;;; Generated autoloads from the-org-mode-expansions.el - -(register-definition-prefixes "the-org-mode-expansions" '("er/")) - - -;;; Generated autoloads from web-mode-expansions.el - -(register-definition-prefixes "web-mode-expansions" '("er/add-web-mode-expansions")) - - -;;; Generated autoloads from yaml-mode-expansions.el - -(register-definition-prefixes "yaml-mode-expansions" '("er--" "er/" "yaml-indent")) - -;;; End of scraped data - -(provide 'expand-region-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; expand-region-autoloads.el ends here blob - c239fd1f99b13036d76e1edcfb100ee72e442846 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/expand-region-core.el +++ /dev/null @@ -1,322 +0,0 @@ -;;; expand-region-core.el --- Increase selected region by semantic units. -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; The core functionality of expand-region. - -;; See README.md - -;;; Code: - -(require 'expand-region-custom) -(declare-function er/expand-region "expand-region") - -(defvar er/history '() - "A history of start and end points so we can contract after expanding.") - -;; history is always local to a single buffer -(make-variable-buffer-local 'er/history) - -(defvar er--space-str " \t\n") -(defvar er--blank-list (append er--space-str nil)) - -(defvar er--show-expansion-message nil) - -(defvar er/try-expand-list nil - "A list of functions that are tried when expanding.") - -(defvar er/save-mode-excursion nil - "A function to save excursion state when expanding.") - -(defsubst er--first-invocation () - "t if this is the first invocation of `er/expand-region' or `er/contract-region'." - (not (memq last-command '(er/expand-region er/contract-region)))) - -(defun er--prepare-expanding () - (when (and (er--first-invocation) - (not (use-region-p))) - (push-mark nil t) ;; one for keeping starting position - (push-mark nil t)) ;; one for replace by set-mark in expansions - - (when (not transient-mark-mode) - (setq-local transient-mark-mode (cons 'only transient-mark-mode)))) - -(defun er--copy-region-to-register () - (when (and (stringp expand-region-autocopy-register) - (> (length expand-region-autocopy-register) 0)) - (set-register (aref expand-region-autocopy-register 0) - (filter-buffer-substring (region-beginning) (region-end))))) - -;; save-mark-and-excursion in Emacs 25 works like save-excursion did before -(eval-when-compile - (when (< emacs-major-version 25) - (defmacro save-mark-and-excursion (&rest body) - `(save-excursion ,@body)))) - -(defmacro er--save-excursion (&rest body) - `(let ((action (lambda () - (save-mark-and-excursion ,@body)))) - (if er/save-mode-excursion - (funcall er/save-mode-excursion action) - (funcall action)))) - -(defun er--expand-region-1 () - "Increase selected region by semantic units. -Basically it runs all the mark-functions in `er/try-expand-list' -and chooses the one that increases the size of the region while -moving point or mark as little as possible." - (let* ((p1 (point)) - (p2 (if (use-region-p) (mark) (point))) - (start (min p1 p2)) - (end (max p1 p2)) - (try-list er/try-expand-list) - (best-start (point-min)) - (best-end (point-max)) - ;; (set-mark-default-inactive nil) - ) - - ;; add hook to clear history on buffer changes - (unless er/history - (add-hook 'after-change-functions #'er/clear-history t t)) - - ;; remember the start and end points so we can contract later - ;; unless we're already at maximum size - (unless (and (= start best-start) - (= end best-end)) - (push (cons p1 p2) er/history)) - - (when (and expand-region-skip-whitespace - (er--point-is-surrounded-by-white-space) - (= start end)) - (skip-chars-forward er--space-str) - (setq start (point))) - - (while try-list - (er--save-excursion - (ignore-errors - (funcall (car try-list)) - (when (and (region-active-p) - (er--this-expansion-is-better start end best-start best-end)) - (setq best-start (point)) - (setq best-end (mark)) - (when (and er--show-expansion-message (not (minibufferp))) - (message "%S" (car try-list)))))) - (setq try-list (cdr try-list))) - - (setq deactivate-mark nil) - ;; if smart cursor enabled, decide to put it at start or end of region: - (if (and expand-region-smart-cursor - (not (= start best-start))) - (progn (goto-char best-end) - (set-mark best-start)) - (goto-char best-start) - (set-mark best-end)) - - (er--copy-region-to-register) - - (when (and (= best-start (point-min)) - (= best-end (point-max))) ;; We didn't find anything new, so exit early - 'early-exit))) - -(defun er--this-expansion-is-better (start end best-start best-end) - "t if the current region is an improvement on previous expansions. - -This is provided as a separate function for those that would like -to override the heuristic." - (and - (<= (point) start) - (>= (mark) end) - (> (- (mark) (point)) (- end start)) - (or (> (point) best-start) - (and (= (point) best-start) - (< (mark) best-end))))) - -;;;###autoload -(defun er/contract-region (arg) - "Contract the selected region to its previous size. -With prefix argument contracts that many times. -If prefix argument is negative calls `er/expand-region'. -If prefix argument is 0 it resets point and mark to their state -before calling `er/expand-region' for the first time." - (interactive "p") - (if (< arg 0) - (er/expand-region (- arg)) - (when er/history - ;; Be sure to reset them all if called with 0 - (when (= arg 0) - (setq arg (length er/history))) - - (when (not transient-mark-mode) - (setq-local transient-mark-mode (cons 'only transient-mark-mode))) - - ;; Advance through the list the desired distance - (while (and (cdr er/history) - (> arg 1)) - (setq arg (- arg 1)) - (setq er/history (cdr er/history))) - ;; Reset point and mark - (let* ((last (pop er/history)) - (start (car last)) - (end (cdr last))) - (goto-char start) - (set-mark end) - - (er--copy-region-to-register) - - (when (eq start end) - (deactivate-mark) - (er/clear-history)))))) - -(defun er/prepare-for-more-expansions-internal (repeat-key-str) - "Return bindings and a message to inform user about them" - (let ((msg (format "Type %s to expand again" repeat-key-str)) - (bindings (list (cons repeat-key-str '(er/expand-region 1))))) - ;; If contract and expand are on the same binding, ignore contract - (unless (string-equal repeat-key-str expand-region-contract-fast-key) - (setq msg (concat msg (format ", %s to contract" expand-region-contract-fast-key))) - (push (cons expand-region-contract-fast-key '(er/contract-region 1)) bindings)) - ;; If reset and either expand or contract are on the same binding, ignore reset - (unless (or (string-equal repeat-key-str expand-region-reset-fast-key) - (string-equal expand-region-contract-fast-key expand-region-reset-fast-key)) - (setq msg (concat msg (format ", %s to reset" expand-region-reset-fast-key))) - (push (cons expand-region-reset-fast-key '(er/expand-region 0)) bindings)) - (cons msg bindings))) - -(defun er/prepare-for-more-expansions () - "Let one expand more by just pressing the last key." - (let* ((repeat-key (event-basic-type last-input-event)) - (repeat-key-str (single-key-description repeat-key)) - (msg-and-bindings (er/prepare-for-more-expansions-internal repeat-key-str)) - (msg (car msg-and-bindings)) - (bindings (cdr msg-and-bindings))) - (when repeat-key - (er/set-temporary-overlay-map - (let ((map (make-sparse-keymap))) - (dolist (binding bindings map) - (define-key map (read-kbd-macro (car binding)) - `(lambda () - (interactive) - (setq this-command `,(cadr ',binding)) - (or (not expand-region-show-usage-message) (minibufferp) (message "%s" ,msg)) - (eval `,(cdr ',binding)))))) - t) - (or (not expand-region-show-usage-message) (minibufferp) (message "%s" msg))))) - -(defalias 'er/set-temporary-overlay-map - (if (fboundp 'set-temporary-overlay-map) ;Emacs≥24.3 - #'set-temporary-overlay-map - ;; Backport this function from newer emacs versions - (lambda (map &optional keep-pred) - "Set a new keymap that will only exist for a short period of time. -The new keymap to use must be given in the MAP variable. When to -remove the keymap depends on user input and KEEP-PRED: - -- if KEEP-PRED is nil (the default), the keymap disappears as - soon as any key is pressed, whether or not the key is in MAP; - -- if KEEP-PRED is t, the keymap disappears as soon as a key *not* - in MAP is pressed; - -- otherwise, KEEP-PRED must be a 0-arguments predicate that will - decide if the keymap should be removed (if predicate returns - nil) or kept (otherwise). The predicate will be called after - each key sequence." - - (let* ((clearfunsym (make-symbol "clear-temporary-overlay-map")) - (overlaysym (make-symbol "t")) - (alist (list (cons overlaysym map))) - (clearfun - `(lambda () - (unless ,(cond ((null keep-pred) nil) - ((eq t keep-pred) - `(eq this-command - (lookup-key ',map - (this-command-keys-vector)))) - (t `(funcall ',keep-pred))) - (remove-hook 'pre-command-hook ',clearfunsym) - (setq emulation-mode-map-alists - (delq ',alist emulation-mode-map-alists)))))) - (set overlaysym overlaysym) - (fset clearfunsym clearfun) - (add-hook 'pre-command-hook clearfunsym) - - (push alist emulation-mode-map-alists))))) - -(advice-add 'keyboard-quit :before #'er--collapse-region-before) -(advice-add 'cua-cancel :before #'er--collapse-region-before) -(defun er--collapse-region-before (&rest _) - ;; FIXME: Re-use `er--first-invocation'? - (when (memq last-command '(er/expand-region er/contract-region)) - (er/contract-region 0))) - -(advice-add 'minibuffer-keyboard-quit - :around #'er--collapse-region-minibuffer-keyboard-quit) -(defun er--collapse-region-minibuffer-keyboard-quit (orig-fun &rest args) - ;; FIXME: Re-use `er--first-invocation'? - (if (memq last-command '(er/expand-region er/contract-region)) - (er/contract-region 0) - (apply orig-fun args))) - - -(defun er/clear-history (&rest _) - "Clear the history." - (setq er/history '()) - (remove-hook 'after-change-functions #'er/clear-history t)) - -(defun er--point-is-surrounded-by-white-space () - (and (or (memq (char-before) er--blank-list) - (eq (point) (point-min))) - (memq (char-after) er--blank-list))) - -(defun er/enable-mode-expansions (mode add-fn) - (add-hook (intern (format "%s-hook" mode)) add-fn) - (save-window-excursion ;; FIXME: Why? - (dolist (buffer (buffer-list)) - (with-current-buffer buffer - (when (derived-mode-p mode) - (funcall add-fn)))))) - -(defun er/enable-minor-mode-expansions (mode add-fn) - (add-hook (intern (format "%s-hook" mode)) add-fn) - (save-window-excursion - (dolist (buffer (buffer-list)) - (with-current-buffer buffer - (when (symbol-value mode) - (funcall add-fn)))))) - -;; Some more performant version of `looking-back' - -(defun er/looking-back-on-line (regexp) - "Version of `looking-back' that only checks current line." - (looking-back regexp (line-beginning-position))) - -(defun er/looking-back-exact (s) - "Version of `looking-back' that only looks for exact matches, no regexp." - (string= s (buffer-substring (- (point) (length s)) - (point)))) - -(defun er/looking-back-max (regexp count) - "Version of `looking-back' that only check COUNT chars back." - (looking-back regexp (max 1 (- (point) count)))) - -(provide 'expand-region-core) - -;;; expand-region-core.el ends here blob - be7413495850155d8cd00e10e3a488eda386f1d3 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/expand-region-custom.el +++ /dev/null @@ -1,115 +0,0 @@ -;;; expand-region-custom.el --- Increase selected region by semantic units. -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This file holds customization variables. - -;;; Code: - -;;;###autoload -(defgroup expand-region nil - "Increase selected region by semantic units." - :group 'tools) - -;;;###autoload -(defcustom expand-region-preferred-python-mode 'python - "The name of your preferred python mode" - :type '(choice (const :tag "Emacs' python.el" python) - (const :tag "fgallina's python.el" fgallina-python) - (const :tag "python-mode.el" python-mode))) - -;;;###autoload -(defcustom expand-region-guess-python-mode t - "If expand-region should attempt to guess your preferred python mode" - :type '(choice (const :tag "Guess" t) - (const :tag "Do not guess" nil))) - -(defun expand-region-guess-python-mode () - "Guess the user's preferred python mode." - (setq expand-region-preferred-python-mode - (if (fboundp 'python-setup-brm) - 'python - 'fgallina-python))) - -;;;###autoload -(defcustom expand-region-autocopy-register "" - "If set to a string of a single character (try \"e\"), then the -contents of the most recent expand or contract command will -always be copied to the register named after that character." - :type 'string) - -;;;###autoload -(defcustom expand-region-skip-whitespace t - "If expand-region should skip past whitespace on initial expansion" - :type '(choice (const :tag "Skip whitespace" t) - (const :tag "Do not skip whitespace" nil))) - -;;;###autoload -(defcustom expand-region-fast-keys-enabled t - "If expand-region should bind fast keys after initial expand/contract" - :type '(choice (const :tag "Enable fast keys" t) - (const :tag "Disable fast keys" nil))) - -;;;###autoload -(defcustom expand-region-contract-fast-key "-" - "Key to use after an initial expand/contract to contract once more." - :type 'string) - -;;;###autoload -(defcustom expand-region-reset-fast-key "0" - "Key to use after an initial expand/contract to undo." - :type 'string) - -;;;###autoload -(defcustom expand-region-exclude-text-mode-expansions - '(html-mode nxml-mode) - "List of modes which derive from `text-mode' for which text mode expansions are not appropriate." - :type '(repeat (symbol :tag "Major Mode" unknown))) - -;;;###autoload -(defcustom expand-region-smart-cursor nil - "Defines whether the cursor should be placed intelligently after expansion. - -If set to t, and the cursor is already at the beginning of the new region, -keep it there; otherwise, put it at the end of the region. - -If set to nil, always place the cursor at the beginning of the region." - :type '(choice (const :tag "Smart behaviour" t) - (const :tag "Standard behaviour" nil))) - -;;;###autoload -(define-obsolete-variable-alias 'er/enable-subword-mode? - 'expand-region-subword-enabled "2019-03-23") - -;;;###autoload -(defcustom expand-region-subword-enabled nil - "Whether expand-region should use subword expansions." - :type '(choice (const :tag "Enable subword expansions" t) - (const :tag "Disable subword expansions" nil))) - -(defcustom expand-region-show-usage-message t - "Whether expand-region should show usage message." - :group 'expand-region - :type 'boolean) - -(provide 'expand-region-custom) - -;;; expand-region-custom.el ends here blob - c97eb4d22cc7c858256648b7aab991bfbc226a84 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/expand-region-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from expand-region.el -*- no-byte-compile: t -*- -(define-package "expand-region" "1.0.0" "Increase selected region by semantic units." '((emacs "24.4")) :commit "9e3f86c02c5e2ab6f0d95da8a34045b54f6166d1" :authors '(("Magnar Sveen" . "magnars@gmail.com")) :maintainer '("Magnar Sveen" . "magnars@gmail.com") :keywords '("marking" "region") :url "https://github.com/magnars/expand-region.el") blob - ebaaca29677be9754ff566144d72d58bd51d15f2 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/expand-region.el +++ /dev/null @@ -1,199 +0,0 @@ -;;; expand-region.el --- Increase selected region by semantic units. -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region -;; URL: https://github.com/magnars/expand-region.el -;; Version: 1.0.0 -;; Package-Requires: ((emacs "24.4")) - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Expand region increases the selected region by semantic units. Just keep -;; pressing the key until it selects what you want. - -;; An example: - -;; (setq alphabet-start "abc def") - -;; With the cursor at the `c`, it starts by marking the entire word `abc`, then -;; expand to the contents of the quotes `abc def`, then to the entire quote -;; `"abc def"`, then to the contents of the sexp `setq alphabet-start "abc def"` -;; and finally to the entire sexp. - -;; You can set it up like this: - -;; (require 'expand-region) -;; (global-set-key (kbd "C-=") 'er/expand-region) - -;; There's also `er/contract-region` if you expand too far. - -;; ## Video - -;; You can [watch an intro to expand-region at Emacs Rocks](http://emacsrocks.com/e09.html). - -;; ## Language support - -;; Expand region works fairly well with most languages, due to the general -;; nature of the basic expansions: - -;; er/mark-word -;; er/mark-symbol -;; er/mark-method-call -;; er/mark-inside-quotes -;; er/mark-outside-quotes -;; er/mark-inside-pairs -;; er/mark-outside-pairs - -;; However, most languages also will benefit from some specially crafted -;; expansions. For instance, expand-region comes with these extra expansions for -;; html-mode: - -;; er/mark-html-attribute -;; er/mark-inner-tag -;; er/mark-outer-tag - -;; You can add your own expansions to the languages of your choice simply by -;; creating a function that looks around point to see if it's inside or looking -;; at the construct you want to mark, and if so - mark it. - -;; There's plenty of examples to look at in these files. - -;; After you make your function, add it to a buffer-local version of -;; the `er/try-expand-list`. - -;; **Example:** - -;; Let's say you want expand-region to also mark paragraphs and pages in -;; text-mode. Incidentally Emacs already comes with `mark-paragraph` and -;; `mark-page`. To add it to the try-list, do this: - -;; (defun er/add-text-mode-expansions () -;; (setq-local er/try-expand-list (append -;; er/try-expand-list -;; '(mark-paragraph -;; mark-page)))) - -;; (er/enable-mode-expansions 'text-mode #'er/add-text-mode-expansions) - -;; Add that to its own file, and require it at the bottom of this one, -;; where it says "Mode-specific expansions" - -;; **Warning:** Badly written expansions might slow down expand-region -;; dramatically. Remember to exit quickly before you start traversing -;; the entire document looking for constructs to mark. - -;; ## Contribute - -;; If you make some nice expansions for your favorite mode, it would be -;; great if you opened a pull-request. The repo is at: - -;; https://github.com/magnars/expand-region.el - -;; Changes to `expand-region-core` itself must be accompanied by feature tests. -;; They are written in [Ecukes](http://ecukes.info), a Cucumber for Emacs. - -;; To fetch the test dependencies: - -;; $ cd /path/to/expand-region -;; $ git submodule init -;; $ git submodule update - -;; Run the tests with: - -;; $ ./util/ecukes/ecukes features - -;; If you want to add feature-tests for your mode-specific expansions as well, -;; that is utterly excellent. - -;; ## Contributors - -;; * [Josh Johnston](https://github.com/joshwnj) contributed `er/contract-region` -;; * [Le Wang](https://github.com/lewang) contributed consistent handling of the mark ring, expanding into pairs/quotes just left of the cursor, and general code clean-up. -;; * [Matt Briggs](https://github.com/mbriggs) contributed expansions for ruby-mode. -;; * [Ivan Andrus](https://github.com/gvol) contributed expansions for python-mode, text-mode, LaTeX-mode and nxml-mode. -;; * [Raimon Grau](https://github.com/kidd) added support for when transient-mark-mode is off. -;; * [Gleb Peregud](https://github.com/gleber) contributed expansions for erlang-mode. -;; * [fgeller](https://github.com/fgeller) and [edmccard](https://github.com/edmccard) contributed better support for python and its multiple modes. -;; * [François Févotte](https://github.com/ffevotte) contributed expansions for C and C++. -;; * [Roland Walker](https://github.com/rolandwalker) added option to copy the contents of the most recent action to a register, and some fixes. -;; * [Damien Cassou](https://github.com/DamienCassou) added option to continue expanding/contracting with fast keys after initial expand. - -;; Thanks! - -;;; Code: - -(require 'expand-region-core) -(require 'expand-region-custom) -(require 'er-basic-expansions) - -;;;###autoload -(defun er/expand-region (arg) - "Increase selected region by semantic units. - -With prefix argument expands the region that many times. -If prefix argument is negative calls `er/contract-region'. -If prefix argument is 0 it resets point and mark to their state -before calling `er/expand-region' for the first time." - (interactive "p") - (if (< arg 1) - (er/contract-region (- arg)) - (er--prepare-expanding) - (while (>= arg 1) - (setq arg (- arg 1)) - (when (eq 'early-exit (er--expand-region-1)) - (setq arg 0))) - (when (and expand-region-fast-keys-enabled - (not (memq last-command '(er/expand-region er/contract-region)))) - (er/prepare-for-more-expansions)))) - -(eval-after-load 'clojure-mode '(require 'clojure-mode-expansions)) -(eval-after-load 'css-mode '(require 'css-mode-expansions)) -(eval-after-load 'erlang-mode '(require 'erlang-mode-expansions)) -(eval-after-load 'feature-mode '(require 'feature-mode-expansions)) -(eval-after-load 'sgml-mode '(require 'html-mode-expansions)) ;; html-mode is defined in sgml-mode.el -(eval-after-load 'rhtml-mode '(require 'html-mode-expansions)) -(eval-after-load 'nxhtml-mode '(require 'html-mode-expansions)) -(eval-after-load 'web-mode '(require 'web-mode-expansions)) -(eval-after-load 'js '(require 'js-mode-expansions)) -(eval-after-load 'js2-mode '(require 'js-mode-expansions)) -(eval-after-load 'js2-mode '(require 'js2-mode-expansions)) -(eval-after-load 'js3-mode '(require 'js-mode-expansions)) -(eval-after-load 'latex '(require 'latex-mode-expansions)) -(eval-after-load 'nxml-mode '(require 'nxml-mode-expansions)) -(eval-after-load 'octave-mod '(require 'octave-expansions)) -(eval-after-load 'octave '(require 'octave-expansions)) -(eval-after-load 'python '(progn - (when expand-region-guess-python-mode - (expand-region-guess-python-mode)) - (if (eq 'python expand-region-preferred-python-mode) - (require 'python-el-expansions) - (require 'python-el-fgallina-expansions)))) -(eval-after-load 'python-mode '(require 'python-mode-expansions)) -(eval-after-load 'ruby-mode '(require 'ruby-mode-expansions)) -(eval-after-load 'org '(require 'the-org-mode-expansions)) -(eval-after-load 'cc-mode '(require 'cc-mode-expansions)) -(eval-after-load 'text-mode '(require 'text-mode-expansions)) -(eval-after-load 'cperl-mode '(require 'cperl-mode-expansions)) -(eval-after-load 'sml-mode '(require 'sml-mode-expansions)) -(eval-after-load 'enh-ruby-mode '(require 'enh-ruby-mode-expansions)) -(eval-after-load 'subword '(require 'subword-mode-expansions)) -(eval-after-load 'yaml-mode '(require 'yaml-mode-expansions)) - -(provide 'expand-region) - -;;; expand-region.el ends here blob - a92cec226594f4d16c26881a9e55737a7f86c736 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/feature-mode-expansions.el +++ /dev/null @@ -1,68 +0,0 @@ -;;; feature-mode-expansions.el --- cucumber-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Raimon Grau -;; Based on js-mode-expansions by: Raimon Grau -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - - -;; expanders to mark feature semantic objects like step or scenario -;; -;; Expansions: -;; -;; -;; er/mark-feature-scenario -;; er/mark-feature-step - -(require 'expand-region-core) - -(defun er--block-between-keywords (start-keywords-regexp &optional end-keywords-regexp) - (let* ((start-key-words (concat "^\\( \\)*" start-keywords-regexp)) - (end-key-words (concat "^\\( \\)*" (or end-keywords-regexp start-keywords-regexp)))) - (when (looking-at-p "[^\\s-]") - (skip-syntax-forward "w.")) - (if (looking-at-p start-keywords-regexp) - (progn (beginning-of-line) - (exchange-point-and-mark)) - (re-search-backward start-key-words) - (set-mark (point)) - (re-search-forward start-key-words)) - (unless (re-search-forward end-key-words (point-max) t) - (goto-char (point-max))) - (forward-line 0) - (exchange-point-and-mark))) - -(defun er/mark-feature-scenario () - (interactive) - (er--block-between-keywords "\\(Background:\\|Scenario:\\|Feature:\\)")) - -(defun er/mark-feature-step () - (interactive) - (er--block-between-keywords "\\(And\\|Given\\|When\\|Then\\)" "\\(And\\|Given\\|When\\|Then\\|Scenario:\\)")) - -(defun er/add-feature-mode-expansions () - "Adds cucumber-specific expansions for buffers in feature-mode" - (set (make-local-variable 'er/try-expand-list) (append - er/try-expand-list - '(er/mark-feature-scenario - er/mark-feature-step)))) - -(er/enable-mode-expansions 'feature-mode #'er/add-feature-mode-expansions) - -(provide 'feature-mode-expansions) blob - e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644) blob + /dev/null blob - a6abc54c5052cb1c77922f8090c04a6c855274b4 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/c++-mode-expansions.feature +++ /dev/null @@ -1,49 +0,0 @@ -Feature: C++-mode expansions - Background: - Given there is no region selected - And I turn on c++-mode - And I insert: - """ - #include - - namespace Foo { - struct Bar { - static float val (int x, double y) { return 42.; } - }; - } - - int main (int argc, char **argv) { - int x = 0; - double y = 1.; - float z = Foo::Bar::val (x, y); - char t = argv [x + 3]; - - int i = 0; - for ( ; i" - And I place the cursor between " " and "id" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "id="5"" - - Scenario: Mark html attribute from end - Given I turn on html-mode - And there is no region selected - When I insert "
" - And I go to point "12" - And I press "C-@" - And I press "C-@" - Then the region should be "id="5"" - - Scenario: Mark html tags, part 1 - Given I turn on html-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - Then the region should be "" - - Scenario: Mark html tags, part 2 - Given I turn on html-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - Then the region should be "" - - Scenario: Mark html tags, part 3 - Given I turn on html-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "before " - - Scenario: Mark html tags, part 4 - Given I turn on html-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "
before
" - - Scenario: Mark html tags, part 5 - Given I turn on html-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "
before
after" - - Scenario: Mark html tags, part 6 - Given I turn on html-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "
before
after
" - - Scenario: Text mode expansions shouldn't be here - Given I turn on html-mode - And there is no region selected - When I insert "Sentence the first. Sentence the second" - And I place the cursor between "first. " and "Sentence" - And I press "C-@" - And I press "C-@" - Then the region should be "Sentence the first. Sentence the second" blob - a3ac8146380733256b6918ee19712fc8a68a4685 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/latex-mode-expansions.feature +++ /dev/null @@ -1,14 +0,0 @@ -Feature: latex-mode expansions - Background: - Given there is no region selected - And I turn on latex-mode - - Scenario: Mark simple math - When I insert "$E=mc^2$" - And I place the cursor before "=" - And I press "C-@" - Then the region should be "E" - And I press "C-@" - Then the region should be "E=mc" - And I press "C-@" - Then the region should be "$E=mc^2$" blob - 431004950d62581fbcae60dc32e7d251278ffc2c (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/mark-pairs.feature +++ /dev/null @@ -1,70 +0,0 @@ -Feature: Mark pairs - In order to quickly and precisely mark pairs - As an Emacs user - I want to expand to them - - Scenario: Mark pair when looking at it - Given there is no region selected - When I insert "... (some parens) ..." - And I go to point "5" - And I press "C-@" - Then the region should be "(some parens)" - - Scenario: Mark pair when looking behind at it - Given there is no region selected - When I insert "... (some parens) ..." - And I go to point "18" - And I press "C-@" - Then the region should be "(some parens)" - - Scenario: Mark inside pairs - Given there is no region selected - When I insert "... (some parens) ..." - And I go to point "10" - And I press "C-@" - And I press "C-@" - Then the region should be "some parens" - - Scenario: Mark child in nested pairs - Given there is no region selected - When I insert "... (some (more parens)) ..." - And I go to point "11" - And I press "C-@" - Then the region should be "(more parens)" - - Scenario: Mark inner parent in nested pairs - Given there is no region selected - When I insert "... (some (more parens)) ..." - And I go to point "11" - And I press "C-@" - And I press "C-@" - Then the region should be "some (more parens)" - - Scenario: Mark outer parent in nested pairs - Given there is no region selected - When I insert "... (some (more parens)) ..." - And I go to point "11" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "(some (more parens))" - - Scenario: Mark outer parent in nested pairs (leftie) - Given there is no region selected - When I insert "... ((some more) parens) ..." - And I go to point "6" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "((some more) parens)" - - Scenario: Mark from behind multiline - Given there is no region selected - When I insert: - """ - (let ((test :test)) - (testing)) - """ - And I place the cursor after ":test))" - And I press "C-@" - Then the region should be "((test :test))" blob - 5ab323a6e7234f818568856f94a852990d3a49a4 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/nxml-mode-expansions.feature +++ /dev/null @@ -1,112 +0,0 @@ -Feature: nxml-mode expansions - In order to quickly and precisely mark xml units - As an Emacs user - I want to expand to them - - Scenario: Mark xml attribute inside quotes - Given I turn on nxml-mode - And there is no region selected - When I insert "" - And I place the cursor after "my" - And I press "C-@" - Then the region should be "myAttr" - - Scenario: Mark xml attribute with quotes - Given I turn on nxml-mode - And there is no region selected - When I insert "" - And I place the cursor after "my" - And I press "C-@" - And I press "C-@" - Then the region should be ""myAttr"" - - Scenario: Mark xml attribute with xpath inside quotes - Given I turn on nxml-mode - And there is no region selected - When I insert "" - And I place the cursor after "a/" - And I press "C-@" - And I press "C-@" - Then the region should be "a/b/c" - - Scenario: Mark xml attribute with xpath inside quotes - Given I turn on nxml-mode - And there is no region selected - When I insert "" - And I place the cursor after "a/" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be ""a/b/c"" - - Scenario: Mark xml attribute from start - Given I turn on nxml-mode - And there is no region selected - When I insert "
" - And I place the cursor between " " and "id" - And I press "C-@" - And I press "C-@" - Then the region should be "id="5"" - - Scenario: Mark xml tags, part 1 - Given I turn on nxml-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - Then the region should be "" - - Scenario: Mark xml tags, part 2 - Given I turn on nxml-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - Then the region should be "" - - Scenario: Mark xml tags, part 3 - Given I turn on nxml-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "before " - - Scenario: Mark xml tags, part 4 - Given I turn on nxml-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "
before
" - - Scenario: Mark xml tags, part 5 - Given I turn on nxml-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "
before
after" - - Scenario: Mark xml tags, part 6 - Given I turn on nxml-mode - And there is no region selected - When I insert "...
before
after
..." - And I place the cursor between "before " and "" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be "
before
after
" blob - c56d89be567079044b79ab319b37a49306f8a71c (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/octave-mode-expansions.feature +++ /dev/null @@ -1,99 +0,0 @@ -Feature: octave-mod expansions - In order to quickly and precisely mark octave units - As an Emacs user - I want to expand to them - - Scenario: Mark block from inside - Given I turn on octave-mode - And there is no region selected - When I insert: - """ - exprBefore; - for i=1:n, - something; - end; - exprAfter; - """ - And I go to point "26" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - for i=1:n, - something; - end - """ - - - Scenario: Mark block when looking at it - Given I turn on octave-mode - And there is no region selected - When I insert: - """ - exprBefore; - for i=1:n, - something; - end; - exprAfter; - """ - And I go to point "13" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - for i=1:n, - something; - end - """ - - - Scenario: Mark block when looking at it inside another block - Given I turn on octave-mode - And there is no region selected - When I insert: - """ - exprBefore; - for i=1:n, - for j=i:k, - something; - end; - end; - exprAfter; - """ - And I go to point "26" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - for j=i:k, - something; - end - """ - - - Scenario: Mark block from inside while looking at another - Given I turn on octave-mode - And there is no region selected - When I insert: - """ - exprBefore; - for i=1:n, - for j=i:k, - something; - end; - end; - exprAfter; - """ - And I go to point "26" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - for i=1:n, - for j=i:k, - something; - end; - end - """ - blob - b161153f2420829ca612e2e0ddbf73f2c2a16de1 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/org-mode-expansions.feature +++ /dev/null @@ -1,55 +0,0 @@ -Feature: org-mode expansions - In order to quickly and precisely mark org mode sections - As an Emacs user - I want to expand to them - - Scenario: Org level 3 - Given I turn on org-mode - When I insert: - """ - * lvl 1 - ** lvl 2 - *** lvl 3 - """ - And I place the cursor before "*** lvl 3" - And I press "C-@" - And I press "C-@" - Then the region should be "*** lvl 3" - - Scenario: Org level 2 - Given I turn on org-mode - When I insert: - """ - * lvl 1 - ** lvl 2 - *** lvl 3 - """ - And I place the cursor before "*** lvl 3" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - ** lvl 2 - *** lvl 3 - """ - - Scenario: Org level 1 - Given I turn on org-mode - When I insert: - """ - * lvl 1 - ** lvl 2 - *** lvl 3 - """ - And I place the cursor before "*** lvl 3" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - * lvl 1 - ** lvl 2 - *** lvl 3 - """ blob - 59442d0c8ee344bc728d221d38721376b15c87a4 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/ruby-mode-expansions.feature +++ /dev/null @@ -1,313 +0,0 @@ -Feature: ruby-mode expansions - In order to quickly and precisely mark ruby code blocks - As an Emacs user - I want to expand to them - - Scenario: Mark instance variable - Given I turn on ruby-mode - When I insert: - """ - class Bar - def initialize - @foo = 123 - end - end - """ - And I place the cursor before "@foo" - And I press "C-@" - Then the region should be "@foo" - - Scenario: Mark ruby block - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - module Bar - something do - foo - end - end - """ - And I place the cursor after "something" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - something do - foo - end - - """ - - Scenario: Mark ruby block from end - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - module Bar - something do - foo - end - end - """ - And I place the cursor after "end" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - something do - foo - end - - """ - - Scenario: Mark ruby block from within - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - module Bar - something do - foo - end - end - """ - And I go to line "2" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - something do - foo - end - - """ - - Scenario: Mark empty ruby block from within - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - module Bar - something do - - end - end - """ - And I go to line "3" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - something do - - end - - """ - - Scenario: Mark ruby block with using curly brackets - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - module Bar - something { - foo - } - end - """ - And I go to line "3" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - something { - foo - } - - """ - - Scenario: Mark ruby function at the beginning - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - module Bar - def foo - bar - end - end - """ - And I go to word "def" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - def foo - bar - end - - """ - - Scenario: Mark ruby function at definition - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - module Bar - def foo - bar - end - end - """ - And I go to line "3" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - def foo - bar - end - - """ - - Scenario: Mark ruby expand up 1 level - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - #comment foo - module Bar - def foo - bar - end - end - - """ - And I go to line "3" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - module Bar - def foo - bar - end - end - - """ - - Scenario: Mark ruby expand up 3 levels - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - #comment foo - module Bar - - attr_reader :blah - - foo_arr.each do |element| - blah { - puts something - } - end - - def foo - bar - end - end - - """ - And I go to line "8" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - module Bar - - attr_reader :blah - - foo_arr.each do |element| - blah { - puts something - } - end - - def foo - bar - end - end - - """ - - Scenario: Mark ruby expand heredoc - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - def foo - blah(<<-end_block) - CONTENT - end_block - end - """ - And I place the cursor before "CONTENT" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - CONTENT - - """ - - Scenario: Mark ruby expand to whole buffer - Given I turn on ruby-mode - And there is no region selected - When I insert: - """ - class Foo - def blah - [1,2,3].each do |num| - puts num - end - end - end - - #comment foo - module Bar - def foo - bar - end - end - - """ - And I go to line "12" - And I press "C-@" - And I press "C-@" - And I press "C-@" - And I press "C-@" - Then the region should be: - """ - class Foo - def blah - [1,2,3].each do |num| - puts num - end - end - end - - #comment foo - module Bar - def foo - bar - end - end - - """ blob - 97302cc9ea4ec6a23b8cba8b349c03b883519f3f (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/step-definitions/expand-region-steps.el +++ /dev/null @@ -1,91 +0,0 @@ -;; Copyright (C) 2012-2023 Free Software Foundation, Inc -*- lexical-binding: t; -*- - -(Given "^mark is inactive by default$" - (lambda () - (setq set-mark-default-inactive t))) - -(Given "^cursor behaviour is set to smart$" - (lambda () - (setq expand-region-smart-cursor t))) - -(When "^I expand the region$" - (lambda () - (cl-flet ((message (&rest args) nil)) - (er/expand-region 1)))) - -(When "^I quit$" - (lambda () - (cl-flet ((signal (&rest args) nil)) - (keyboard-quit)))) - -(When "^I expand the region \\([0-9]+\\) times$" - (lambda (arg) - (cl-flet ((message (&rest args) nil)) - (er/expand-region (string-to-number arg))))) - -(And "^I contract the region$" - (lambda () - (er/contract-region 1))) - -(When "^I place the cursor after \"\\(.+\\)\"$" - (lambda (arg) - (goto-char (point-min)) - (let ((search (search-forward arg nil t)) - (message "Can not place cursor after '%s', because there is no such point: '%s'")) - (cl-assert search nil message arg (espuds-buffer-contents))))) - -(When "^I place the cursor before \"\\(.+\\)\"$" - (lambda (arg) - (goto-char (point-max)) - (let ((search (search-backward arg nil t)) - (message "Can not place cursor before '%s', because there is no such point: '%s'")) - (cl-assert search nil message arg (espuds-buffer-contents))))) - -(When "^I pop the mark$" - (lambda () - (set-mark-command 4))) - -(When "^I deactivate the mark$" - (lambda () - (deactivate-mark))) - -(When "^I activate the mark$" - (lambda () - (activate-mark))) - -(Then "^the region should not be active$" - (lambda () - (should - (not (region-active-p))))) - -(Then "^cursor should be at point \"\\(.+\\)\"$" - (lambda (arg) - (should - (= - (string-to-number arg) - (point))))) - -(And "^autocopy-register is \"\\(.\\)\"$" - (lambda (reg) - (setq expand-region-autocopy-register reg) - (set-register (aref reg 0) nil))) - -(Then "^register \"\\(.\\)\" should be \"\\(.+\\)\"$" - (lambda (reg contents) - (should - (equal contents (get-register (aref reg 0)))))) - -(When "^I go to the \\(front\\|end\\) of the word \"\\(.+\\)\"$" - (lambda (pos word) - (goto-char (point-min)) - (let ((search (re-search-forward (format "%s" word) nil t)) - (message "Can not go to character '%s' since it does not exist in the current buffer: %s")) - (cl-assert search nil message word (espuds-buffer-contents)) - (if (string-equal "front" pos) (backward-word))))) - -(When "^I set \\(.+\\) to \\(.+\\)$" - (lambda (var val) - (set (intern var) (read val)))) -;; Local Variables: -;; no-byte-compile: t -;; End: blob - 00aa858a3f5aa215f50bd68f09f84a86c21ce08a (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/support/env.el +++ /dev/null @@ -1,34 +0,0 @@ -;; Copyright (C) 2012-2023 Free Software Foundation, Inc -*- lexical-binding: t; -*- - -(let* ((current-directory (file-name-directory load-file-name)) - (features-directory (expand-file-name ".." current-directory)) - (project-directory (expand-file-name ".." features-directory))) - (setq expand-region-root-path project-directory)) - -(add-to-list 'load-path expand-region-root-path) - -(require 'undercover) -(undercover "*.el") - -(require 'expand-region) -(require 'espuds) -(require 'ert) - -(Before - (global-set-key (kbd "C-@") 'er/expand-region) - (global-set-key (kbd "C-S-@") 'er/contract-region) - (switch-to-buffer - (get-buffer-create "*expand-region*")) - (erase-buffer) - (fundamental-mode) - (transient-mark-mode 1) - (cua-mode 0) - (setq er--show-expansion-message t) - (setq expand-region-smart-cursor nil) - (setq set-mark-default-inactive nil) - (deactivate-mark)) - -(After) -;; Local Variables: -;; no-byte-compile: t -;; End: blob - 13515b66426981809429f5992fa9bec85e6bef93 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/features/text-mode-expansions.feature +++ /dev/null @@ -1,97 +0,0 @@ -Feature: Text-mode expansions - Background: - Given there is no region selected - And I turn on text-mode - And I insert: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Here is a sentence. Here is another. And one with Dr. Baker. - - Another paragraph. With 2 sentences. - - "We're on a different page," said the man. - """ - - Scenario: Mark sentence ending on a line - When I place the cursor after "consectetur" - And I press "C-@" - Then the region should be "consectetur" - And I press "C-@" - Then the region should be "Lorem ipsum dolor sit amet, consectetur adipiscing elit." - And I press "C-@" - Then the region should be: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Here is a sentence. Here is another. And one with Dr. Baker. - - """ - - Scenario: Mark sentence ending on a line 2 - When I place the cursor before "Lorem" - And I press "C-@" - Then the region should be "Lorem" - And I press "C-@" - Then the region should be "Lorem ipsum dolor sit amet, consectetur adipiscing elit." - And I press "C-@" - Then the region should be: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Here is a sentence. Here is another. And one with Dr. Baker. - - """ - - Scenario: Mark sentence beginning a line - When I place the cursor after "sentence." - And I press "C-@" - Then the region should be "sentence." - And I press "C-@" - Then the region should be "Here is a sentence." - And I press "C-@" - Then the region should be: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Here is a sentence. Here is another. And one with Dr. Baker. - - """ - - Scenario: Mark sentence in the middle of a line - When I place the cursor before "is another" - And I press "C-@" - Then the region should be "is" - And I press "C-@" - Then the region should be "Here is another." - And I press "C-@" - Then the region should be: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Here is a sentence. Here is another. And one with Dr. Baker. - - """ - - Scenario: Mark sentence in the middle of a line - When I place the cursor after "Baker." - And I press "C-@" - Then the region should be "Baker." - And I press "C-@" - Then the region should be "And one with Dr. Baker." - And I press "C-@" - Then the region should be: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Here is a sentence. Here is another. And one with Dr. Baker. - - """ - - Scenario: Sentence endings - When I place the cursor before "Dr." - And I set sentence-end-double-space to nil - And I press "C-u 3 C-@" - Then the region should be "And one with Dr." - - Scenario: Sentence endings 2 - When I place the cursor before "Dr." - And I set sentence-end-double-space to t - And I press "C-u 3 C-@" - Then the region should be "And one with Dr. Baker." - # I turned sentence-end-double-space back to the default here in - # case it comes into play in other tests. blob - 19b4d028ee689d79bf140b2b80abe9e58fbfd25b (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/html-mode-expansions.el +++ /dev/null @@ -1,104 +0,0 @@ -;;; html-mode-expansions.el --- HTML-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Extra expansions for HTML that I've found useful so far: -;; -;; er/mark-html-attribute -;; er/mark-inner-tag -;; er/mark-outer-tag -;; -;; Feel free to contribute any other expansions for HTML at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(require 'sgml-mode) - -(defun er/mark-html-attribute () - "Mark html-attribute. -Presumes that point is at the assignment part of attr=\"value\". -If point is inside the value-string, the quotes will be marked -first anyway. Does not support html-attributes with spaces -around the equal sign or unquoted attributes atm." - (interactive) - (when (or (looking-at "\\(\\s_\\|\\sw\\)*=") - (er/looking-back-exact "=")) - (search-backward " ") - (forward-char 1) - (set-mark (point)) - (search-forward "=") - (forward-sexp 1) - (exchange-point-and-mark))) - -(defun er--looking-at-marked-tag () - "Is point looking at a tag that is entirely marked?" - (and (looking-at "<") - (>= (mark) - (save-excursion - (sgml-skip-tag-forward 1) - (point))))) - -(defun er--inside-tag-p () - "Is point inside a tag?" - (save-excursion - (not (null (sgml-get-context))))) - -(defun er/mark-outer-tag () - "Mark from opening to closing tag, including the tags." - (interactive) - (when (and (er--inside-tag-p) - (or (not (looking-at "<")) - (er--looking-at-marked-tag))) - (goto-char (aref (car (last (sgml-get-context))) 2))) - (when (looking-at "<") - (set-mark (point)) - (sgml-skip-tag-forward 1) - (exchange-point-and-mark))) - -(defun er/mark-inner-tag () - "Mark the contents of an open tag, not including the tags." - (interactive) - (goto-char (aref (car (last (sgml-get-context))) 3)) - (set-mark (point)) - (backward-char 1) - (sgml-skip-tag-forward 1) - (search-backward " -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Extra expansions for JavaScript that I've found useful so far: -;; -;; er/mark-js-function -;; er/mark-js-object-property-value -;; er/mark-js-object-property -;; er/mark-js-if -;; er/mark-js-inner-return -;; er/mark-js-outer-return -;; -;; Feel free to contribute any other expansions for JavaScript at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(require 'er-basic-expansions) - -(defun er/mark-js-function () - "Mark the current JavaScript function." - (interactive) - (condition-case nil - (forward-char 8) - (error nil)) - (word-search-backward "function") - (while (or (er--point-inside-string-p) - (er--point-is-in-comment-p)) - (word-search-backward "function")) - (set-mark (point)) - (while (not (looking-at "{")) - (forward-char)) - (forward-list) - (exchange-point-and-mark)) - -(defun er/mark-js-outer-return () - "Mark the current return statement, including return and ending semi-colon" - (interactive) - (condition-case nil - (forward-char 6) - (error nil)) - (word-search-backward "return") - (while (or (er--point-inside-string-p) - (er--point-is-in-comment-p)) - (word-search-backward "return")) - (set-mark (point)) - (while (not (looking-at ";")) - (if (looking-at "\\s(") - (forward-list) - (forward-char))) - (forward-char) - (exchange-point-and-mark)) - -(defun er/mark-js-inner-return () -` "Mark contents of the current return statement. -Does not include return or semi-colon." - (interactive) - (condition-case nil - (forward-char 6) - (error nil)) - (word-search-backward "return") - (while (or (er--point-inside-string-p) - (er--point-is-in-comment-p)) - (word-search-backward "return")) - (search-forward " ") - (set-mark (point)) - (while (not (looking-at ";")) - (if (looking-at "\\s(") - (forward-list) - (forward-char))) - (exchange-point-and-mark)) - -(defun er/mark-js-if () - "Mark the current if-statement." - (interactive) - (condition-case nil - (forward-char 2) - (error nil)) - (word-search-backward "if") - (while (or (er--point-inside-string-p) - (er--point-is-in-comment-p)) - (word-search-backward "if")) - (set-mark (point)) - (while (not (looking-at "(")) - (forward-char)) - (forward-list) - (while (not (looking-at "{")) - (forward-char)) - (forward-list) - (exchange-point-and-mark)) - -(defun er/mark-js-object-property-value () - "Mark the current object property value, ie. from : to , or }" - (interactive) - (unless (er--point-inside-pairs-p) - (error "Point is not inside an object")) - (search-backward ":") - (forward-char) - (search-forward-regexp "[^\s]") - (backward-char) - (set-mark (point)) - (while (not (looking-at "[},]")) - (if (looking-at "\\s(") - (forward-list) - (forward-char))) - (when (er/looking-back-max "[\s\n]" 400) - (search-backward-regexp "[^\s\n]") - (forward-char)) - (exchange-point-and-mark)) - -(defun er/mark-js-object-property () - "Mark js-object-property. -Presumes that point is at the assignment part of key: value. -If point is inside the value, that will be marked first anyway." - (interactive) - (when (or (looking-at "\"?\\(\\s_\\|\\sw\\| \\)*\":") - (looking-at "\\(\\s_\\|\\sw\\)*:") - (er/looking-back-max ": ?" 2)) - (search-backward-regexp "[{,]") - (forward-char) - (search-forward-regexp "[^\s\n]") - (backward-char) - (set-mark (point)) - (search-forward ":") - (while (or (not (looking-at "[},]")) - (er--point-inside-string-p)) - (if (looking-at "\\s(") - (forward-list) - (forward-char))) - (when (er/looking-back-max "[\s\n]" 400) - (search-backward-regexp "[^\s\n]") - (forward-char)) - (exchange-point-and-mark))) - -(defun er/mark-js-call () - "Mark the current symbol (including dots) and then parens or squares." - (interactive) - (let ((symbol-regexp "\\(\\s_\\|\\sw\\|\\.\\)+")) - (when (or (looking-at symbol-regexp) - (er/looking-back-on-line symbol-regexp)) - (skip-syntax-backward "_w.") - (when (looking-at "!") - (forward-char 1)) - (set-mark (point)) - (when (looking-at symbol-regexp) - (goto-char (match-end 0))) - (if (looking-at "\\[\\|(") - (forward-list)) - (exchange-point-and-mark)))) - -(defun er/add-js-mode-expansions () - "Adds JS-specific expansions for buffers in js-mode" - (set (make-local-variable 'er/try-expand-list) (append - er/try-expand-list - '(er/mark-js-function - er/mark-js-object-property-value - er/mark-js-object-property - er/mark-js-if - er/mark-js-inner-return - er/mark-js-outer-return - er/mark-js-call)))) - -(er/enable-mode-expansions 'js-mode #'er/add-js-mode-expansions) -(er/enable-mode-expansions 'js2-mode #'er/add-js-mode-expansions) -(er/enable-mode-expansions 'js3-mode #'er/add-js-mode-expansions) - -(provide 'js-mode-expansions) - -;; js-mode-expansions.el ends here blob - 122d65d0128789b232c6002d326736fdfb9a427f (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/js2-mode-expansions.el +++ /dev/null @@ -1,59 +0,0 @@ -;;; js2-mode-expansions.el --- Additional expansions for js2-mode -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Extra expansions specifically for js2-mode, since it has -;; a semantic parser. -;; -;; Feel free to contribute any other expansions for JavaScript at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(declare-function js2-node-parent-stmt "js2-mode") -(declare-function js2-node-at-point "js2-mode") -(declare-function js2-node-abs-pos "js2-mode") -(declare-function js2-node-len "js2-mode") - -(defun js2-mark-parent-statement () - (interactive) - (let* ((parent-statement (if (not (er/looking-back-exact ";")) - (js2-node-parent-stmt (js2-node-at-point)) - (forward-char -1) - (js2-node-at-point))) - (beg (js2-node-abs-pos parent-statement)) - (end (+ beg (js2-node-len parent-statement)))) - (goto-char beg) - (set-mark end))) - -(defun er/add-js2-mode-expansions () - "Adds expansions for buffers in js2-mode" - (set (make-local-variable 'er/try-expand-list) (append - er/try-expand-list - '(js2-mark-parent-statement)))) - -(er/enable-mode-expansions 'js2-mode #'er/add-js2-mode-expansions) - -(provide 'js2-mode-expansions) - -;; js2-mode-expansions.el ends here blob - 23309d36d4b18a9c23330fde51e39554f4f7d259 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/jsp-expansions.el +++ /dev/null @@ -1,64 +0,0 @@ -;;; jsp-expansions.el --- JSP-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Extra expansions for editing JSP files. To be used in conjunction -;; with the html-mode expansions -;; -;; er/mark-jstl-escape -;; -;; These expansions aren't loaded by default, so you'll have to explicitly -;; ask for them in your init file with: -;; -;; (eval-after-load 'sgml-mode '(require 'jsp-expansions)) -;; -;; Feel free to contribute any other expansions for JSP at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) - -(defun er/mark-jstl-escape () - "Mark jstl-escape presumes that point is outside the brackets. -If point is inside the brackets, they will be marked first anyway." - (interactive) - (when (or (looking-at "\\${") - (er/looking-back-exact "$")) - (forward-char 1) - (search-backward "\$") - (set-mark (point)) - (forward-char 1) - (forward-list) - (exchange-point-and-mark))) - -(defun er/add-jsp-expansions () - "Adds JSP-specific expansions to the buffer" - (set (make-local-variable 'er/try-expand-list) (append - er/try-expand-list - '(er/mark-jstl-escape)))) - -(er/enable-mode-expansions 'html-mode #'er/add-jsp-expansions) - -(provide 'jsp-expansions) - -;; jsp-expansions.el ends here blob - 48cc71308250de41b1ba1bde37c2aa6f410ea258 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/latex-mode-expansions.el +++ /dev/null @@ -1,104 +0,0 @@ -;;; latex-mode-expansions.el --- LaTeX-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Ivan Andrus -;; Based on js-mode-expansions by: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; This is for AUCTeX, not the builtin latex-mode. - -;; Feel free to contribute any other expansions for LaTeX at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -;referenced free variables and functions defined in mode -(defvar texmathp-why) -(defvar texmathp-tex-commands1) -(defvar texmathp-onoff-regexp) -(defvar LaTeX-mode-hook) -(declare-function LaTeX-mark-environment "latex") -(declare-function texmathp "texmathp") - -(defun er/mark-LaTeX-inside-environment () - "Like `LaTeX-mark-environment' but marks the inside of the environment. -Skips past [] and {} arguments to the environment." - (interactive) - (LaTeX-mark-environment) - (when (looking-at "\\\\begin{") - (forward-sexp 2) - ;; Assume these are arguments - (while (looking-at "[ \t\n]*[{[]") - (forward-sexp 1)) - ;; Go to next line if there is nothing interesting on this one - (skip-syntax-forward " ") ;; newlines are ">" i.e. end comment - (when (looking-at "%\\|$") - (forward-line)) - ;; Clean up the end portion - (exchange-point-and-mark) - (backward-sexp 2) - (skip-syntax-backward " ") - (exchange-point-and-mark))) - -(defun er/mark-LaTeX-math () - "Mark current math environment." - (interactive) - (when (texmathp) - (let* ((string (car texmathp-why)) - (pos (cdr texmathp-why)) - (reason (assoc string texmathp-tex-commands1)) - (type (cadr reason))) - (cond - ((eq type 'env-on) ;; environments equation, align, etc. - (er/mark-LaTeX-inside-environment)) - ((eq type 'arg-on) ;; \ensuremath etc. - (goto-char pos) - (set-mark (point)) - (forward-sexp 2) - (exchange-point-and-mark)) - ((eq type 'sw-toggle) ;; $ and $$ - (goto-char pos) - (set-mark (point)) - (forward-sexp 1) - (exchange-point-and-mark)) - ((eq type 'sw-on) ;; \( and \[ - (re-search-forward texmathp-onoff-regexp) - (set-mark pos) - (exchange-point-and-mark)) - (t (error (format "Unknown reason to be in math mode: %s" type))))))) - -(defun er/add-latex-mode-expansions () - "Adds expansions for buffers in latex-mode" - (set (make-local-variable 'er/try-expand-list) - (append - er/try-expand-list - '(LaTeX-mark-environment - LaTeX-mark-section - er/mark-LaTeX-inside-environment - er/mark-LaTeX-math)))) - -(let ((latex-mode-hook LaTeX-mode-hook)) - (er/enable-mode-expansions 'latex-mode #'er/add-latex-mode-expansions) - (setq LaTeX-mode-hook latex-mode-hook)) - -(provide 'latex-mode-expansions) - -;; latex-mode-expansions.el ends here blob - 7fe2deedd3e043af7485d685f1d15fa5d275deca (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/nxml-mode-expansions.el +++ /dev/null @@ -1,125 +0,0 @@ -;;; nxml-mode-expansions.el --- Nxml-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Ivan Andrus -;; Based on js-mode-expansions by: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Feel free to contribute any other expansions for Nxml at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'cl-lib) -(require 'expand-region-core) -(require 'html-mode-expansions) -(require 'nxml-mode) - -(defun er/mark-nxml-tag () - "Marks one nxml element e.g.

" - (interactive) - (cond ((looking-at "<") - (nxml-mark-token-after)) - ((er/looking-back-exact ">") - (backward-char 1) - (nxml-mark-token-after)) - ((er/looking-back-max "<[^<>]*" 1000) - (nxml-mark-token-after)))) - -(defun er/mark-nxml-element () - "Marks one nxml element e.g.

...

" - (interactive) - (if (not (looking-at "<[^/]")) - (er/mark-nxml-containing-element) - (set-mark (point)) - (nxml-forward-element) - (exchange-point-and-mark))) - -(defun er/mark-nxml-containing-element () - "Marks one nxml element, but always e.g.

...

" - (interactive) - (nxml-up-element) - (set-mark (point)) - (nxml-backward-element)) - -(defun er/mark-nxml-inside-element () - "Marks the inside Nxml statement, eg.

...

" - (interactive) - (let ((nxml-sexp-element-flag nil)) - (nxml-up-element) - (nxml-forward-balanced-item -1) - (set-mark (point)) - (nxml-backward-up-element) - (nxml-forward-balanced-item 1))) - -(defun er/inside-nxml-attribute-string? () - "Returns the attribute from `xmltok-attributes' array that -point is in, or otherwise nil" - (save-excursion - (forward-char 1) - (nxml-token-before)) - (cl-find-if (lambda (att) - (and (<= (xmltok-attribute-value-start att) (point)) - (>= (xmltok-attribute-value-end att) (point)))) - xmltok-attributes)) - -(defun er/mark-nxml-attribute-inner-string () - "Marks an attribute string" - (interactive) - (let ((attr (er/inside-nxml-attribute-string?))) - (when attr - (set-mark (xmltok-attribute-value-start attr)) - (goto-char (xmltok-attribute-value-end attr)) - (exchange-point-and-mark)))) - -(defun er/mark-nxml-attribute-string () - "Marks an attribute string inside quotes." - (interactive) - (let ((attr (er/inside-nxml-attribute-string?))) - (when attr - (set-mark (1- (xmltok-attribute-value-start attr))) - (goto-char (1+ (xmltok-attribute-value-end attr))) - (exchange-point-and-mark)))) - -(defun er/add-nxml-mode-expansions () - "Adds Nxml-specific expansions for buffers in nxml-mode" - (interactive) - (set (make-local-variable 'er/try-expand-list) - (append - '(nxml-mark-paragraph - ;; nxml-mark-token-after ;; Marks the current tag, etc. It's a bit schizophrenic - er/mark-nxml-tag - er/mark-nxml-inside-element - er/mark-nxml-element - er/mark-nxml-containing-element - er/mark-nxml-attribute-string - er/mark-nxml-attribute-inner-string - ;; Steal from html-mode-expansions - er/mark-html-attribute) - ;; some normal marks are more hindrance than help: - (remove 'er/mark-method-call - (remove 'er/mark-symbol-with-prefix - (remove 'er/mark-symbol er/try-expand-list)))))) - -(er/enable-mode-expansions 'nxml-mode #'er/add-nxml-mode-expansions) - -(provide 'nxml-mode-expansions) - -;; nxml-mode-expansions.el ends here blob - 8866d20b8c167df5ee78d75a6041d57568fdbc48 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/octave-expansions.el +++ /dev/null @@ -1,78 +0,0 @@ -;;; octave-expansions.el --- octave-mode expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Mark Hepburn -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Feel free to contribute any other expansions for Octave at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(declare-function octave-mark-block "octave-mod") - -;;; Octave-mod received a major rewrite between versions 23 and 24 of -;;; Emacs, for example using the new smie package instead of -;;; hand-coding a lot of motion commands. Unfortunately for our -;;; purposes here, in the process the behaviour of `octave-mark-block' -;;; changed slightly. So, in order to behave identically across both -;;; versions we need to check which is which in a few places and -;;; adjust accordingly: -(defconst er/old-octave-mod-p (fboundp 'octave-up-block)) - -(defalias 'er/up-block - (if er/old-octave-mod-p 'octave-up-block 'up-list)) - -(defun er/octave-mark-up-block () - "Mark the containing block, assuming the current block has -already been marked." - (interactive) - (when (use-region-p) - (when (< (point) (mark)) - (exchange-point-and-mark)) - (er/up-block -1) ; -1 means backwards, ie to the front - (octave-mark-block))) - -(defun er/octave-mark-block () - "Not for general use; this is a work-around for the different -behaviour of `octave-mark-block' between emacs versions 23 and -24." - (interactive) - (forward-word) - (octave-mark-block)) - -(defun er/add-octave-expansions () - "Adds octave/matlab-specific expansions for buffers in octave-mode" - (let ((try-expand-list-additions (if er/old-octave-mod-p - '(octave-mark-block - er/octave-mark-up-block - octave-mark-defun) - '(octave-mark-block - er/octave-mark-block - er/octave-mark-up-block - mark-defun)))) - (set (make-local-variable 'er/try-expand-list) - (append er/try-expand-list try-expand-list-additions)))) - -(er/enable-mode-expansions 'octave-mode #'er/add-octave-expansions) - -(provide 'octave-expansions) -;;; octave-expansions.el ends here blob - 21aaa22bfdaa5a175485932c869a470c5f1f514b (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/python-el-expansions.el +++ /dev/null @@ -1,92 +0,0 @@ -;;; python-el-expansions.el --- Python-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Authors: Ivan Andrus, Felix Geller, @edmccard -;; Based on js-mode-expansions by: Magnar Sveen -;; Keywords: marking region python - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; For python.el included with GNU Emacs -;; - Mark functionality taken from python.el: -;; - `python-mark-block' -;; - Additions implemented here: -;; - `er/mark-python-statement' -;; - `er/mark-inside-python-string' -;; - `er/mark-outside-python-string' -;; - Supports multi-line strings - -;; There is no need for a er/mark-python-defun since -;; er/mark-python-block will mark it - -;; Feel free to contribute any other expansions for Python at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(require 'python) - -(declare-function python-beginning-of-string "python-mode") - -(defvar er--python-string-delimiter "'\"") - -(defun er/mark-python-statement () - "Marks one Python statement, eg. x = 3" - (interactive) - (python-nav-end-of-statement) - (set-mark (point)) - (python-nav-beginning-of-statement)) - -(defun er/mark-outside-python-string () - "Marks region outside a (possibly multi-line) Python string" - (interactive) - (python-beginning-of-string) - (set-mark (point)) - (forward-sexp) - (exchange-point-and-mark)) - -(defun er/mark-inside-python-string () - "Marks region inside a (possibly multi-line) Python string" - (interactive) - (when (eq 'string (syntax-ppss-context (syntax-ppss))) - (python-beginning-of-string) - (let ((string-beginning (point))) - (forward-sexp) - (skip-chars-backward er--python-string-delimiter) - (set-mark (point)) - (goto-char string-beginning) - (skip-chars-forward er--python-string-delimiter)))) - -(defun er/add-python-mode-expansions () - "Adds Python-specific expansions for buffers in python-mode" - (let ((try-expand-list-additions '(er/mark-python-statement - er/mark-inside-python-string - er/mark-outside-python-string - python-mark-block))) - (set (make-local-variable 'expand-region-skip-whitespace) nil) - (set (make-local-variable 'er/try-expand-list) - (remove 'er/mark-inside-quotes - (remove 'er/mark-outside-quotes - (append er/try-expand-list try-expand-list-additions)))))) - -(er/enable-mode-expansions 'python-mode #'er/add-python-mode-expansions) - -(provide 'python-el-expansions) - -;; python-el-expansions.el ends here blob - 148dccf2a6e27d3d943b0fad9542bf7b6ca29720 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/python-el-fgallina-expansions.el +++ /dev/null @@ -1,193 +0,0 @@ -;;; python-el-fgallina-expansions.el --- fgallina/python.el-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Felix Geller -;; Keywords: marking region python - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: -;; -;; - Additions implemented here: -;; - `er/mark-inside-python-string' -;; - `er/mark-outside-python-string' -;; - `er/mark-python-statement' -;; - `er/mark-python-block' -;; - `er/mark-outer-python-block' -;; - `er/mark-python-block-and-decorator' -;; - Supports multi-line strings - -;;; Code: - -(require 'expand-region-core) - -(if (not (fboundp 'python-syntax-context)) - (defalias 'python-syntax-context #'python-info-ppss-context)) -(if (not (fboundp 'python-indent-offset)) - (defalias 'python-indent-offset #'python-indent)) - -(defvar er--python-string-delimiter - "'\"" - "Characters that delimit a Python string.") - -;; copied from @fgallina's python.el as a quick fix. The variable -;; `python-rx-constituents' is not bound when we use the python-rx -;; macro from here, so we have to construct the regular expression -;; manually. -(defvar er--python-block-start-regex - (rx symbol-start - (or "def" "class" "if" "elif" "else" "try" - "except" "finally" "for" "while" "with") - symbol-end) - "Regular expression string to match the beginning of a Python block.") - -(defun er/mark-python-string (mark-inside) - "Mark the Python string that surrounds point. - -If the optional MARK-INSIDE is not nil, only mark the region -between the string delimiters, otherwise the region includes the -delimiters as well." - (let ((beginning-of-string (python-syntax-context 'string (syntax-ppss)))) - (when beginning-of-string - (goto-char beginning-of-string) - ;; Move inside the string, so we can use ppss to find the end of - ;; the string. - (skip-chars-forward er--python-string-delimiter) - (while (python-syntax-context 'string (syntax-ppss)) - (forward-char 1)) - (when mark-inside (skip-chars-backward er--python-string-delimiter)) - (set-mark (point)) - (goto-char beginning-of-string) - (when mark-inside (skip-chars-forward er--python-string-delimiter))))) - -(defun er/mark-inside-python-string () - "Mark the inside of the Python string that surrounds point. - -Command that wraps `er/mark-python-string'." - (interactive) - (er/mark-python-string t)) - -(defun er/mark-outside-python-string () - "Mark the outside of the Python string that surrounds point. - -Command that wraps `er/mark-python-string'." - (interactive) - (er/mark-python-string nil)) - -(defun er/mark-python-statement () - "Mark the Python statement that surrounds point." - (interactive) - (python-nav-end-of-statement) - (set-mark (point)) - (python-nav-beginning-of-statement)) - -(defun er/mark-python-block (&optional next-indent-level) - "Mark the Python block that surrounds point. - -If the optional NEXT-INDENT-LEVEL is given, select the -surrounding block that is defined at an indentation that is less -than NEXT-INDENT-LEVEL." - (interactive) - (back-to-indentation) - (let ((next-indent-level - (or - ;; Use the given level - next-indent-level - ;; Check whether point is at the start of a Python block. - (if (looking-at er--python-block-start-regex) - ;; Block start means that the next level is deeper. - (+ (current-indentation) python-indent-offset) - ;; Assuming we're inside the block that we want to mark - (current-indentation))))) - ;; Move point to next Python block start at the correct indent-level - (while (>= (current-indentation) next-indent-level) - (re-search-backward er--python-block-start-regex)) - ;; Mark the beginning of the block - (set-mark (point)) - ;; Save indentation and look for the end of this block - (let ((block-indentation (current-indentation))) - (forward-line 1) - (while (and - ;; No need to go beyond the end of the buffer. Can't use - ;; eobp as the loop places the point at the beginning of - ;; line, but eob might be at the end of the line. - (not (= (point-max) (line-end-position))) - ;; Proceed if: indentation is too deep - (or (> (current-indentation) block-indentation) - ;; Looking at an empty line - (looking-at (rx line-start (* whitespace) line-end)) - ;; We're not looking at the start of a Python block - ;; and the indent is deeper than the block's indent - (and (not (looking-at er--python-block-start-regex)) - (> (current-indentation) block-indentation)))) - (forward-line 1) - (back-to-indentation)) - ;; Find the end of the block by skipping comments backwards - (python-util-forward-comment -1) - (exchange-point-and-mark)))) - -(defun er/mark-outer-python-block () - "Mark the Python block that surrounds the Python block around point. - -Command that wraps `er/mark-python-block'." - (interactive) - (er/mark-python-block (current-indentation))) - -(defun er/mark-python-block-and-decorator () - (interactive) - (back-to-indentation) - (if (or (er--python-looking-at-decorator) (er--python-looking-at-decorator -1)) - (progn - (while (er--python-looking-at-decorator -1) - (forward-line -1) - (back-to-indentation) - ) - (set-mark (point)) - (while (er--python-looking-at-decorator) - (forward-line) - ) - (python-nav-end-of-block) - (exchange-point-and-mark)))) - -(defun er--python-looking-at-decorator (&optional line-offset) - (save-excursion - (if line-offset - (forward-line line-offset) - ) - (back-to-indentation) - (looking-at "@") - )) - -(defun er/add-python-mode-expansions () - "Adds python-mode-specific expansions for buffers in python-mode" - (let ((try-expand-list-additions '( - er/mark-inside-python-string - er/mark-outside-python-string - er/mark-python-statement - er/mark-python-block - er/mark-python-block-and-decorator - er/mark-outer-python-block - ))) - (set (make-local-variable 'expand-region-skip-whitespace) nil) - (set (make-local-variable 'er/try-expand-list) - (remove 'er/mark-inside-quotes - (remove 'er/mark-outside-quotes - (append er/try-expand-list try-expand-list-additions)))))) - -(er/enable-mode-expansions 'python-mode #'er/add-python-mode-expansions) - -(provide 'python-el-fgallina-expansions) - -;; python-el-fgallina-expansions.el ends here blob - 690fe9b0b0f61c41b745142ddfc8d3119e1d9ad2 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/python-mode-expansions.el +++ /dev/null @@ -1,147 +0,0 @@ -;;; python-mode-expansions.el --- python-mode-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Felix Geller -;; Based on python-mode-expansions by: Ivan Andrus -;; Keywords: marking region python - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Commentary: -;; cf. https://github.com/magnars/expand-region.el/pull/18 - -;; For python-mode: https://launchpad.net/python-mode -;; - Mark functionality taken from python-mode: -;; - `py-mark-expression' -;; - `py-mark-statement' -;; - `py-mark-block' -;; - `py-mark-class' -;; - Additions implemented here: -;; - `er/mark-inside-python-string' -;; - `er/mark-outside-python-string' -;; - `er/mark-outer-python-block' -;; - Supports multi-line strings -;; - Supports incremental expansion of nested blocks - -;;; Code: - -(require 'expand-region-core) - -(defvar er--python-string-delimiter "'\"") - -(defalias 'py-goto-beyond-clause #'py-end-of-clause-bol) - -(declare-function py-in-string-p "python-mode") -(declare-function py-beginning-of-block "python-mode") -(declare-function py-end-of-block "python-mode") -(declare-function py-mark-block-or-clause "python-mode") -(declare-function py-end-of-clause-bol "python-mode") -(defvar py-indent-offset) - -(defun er/mark-outside-python-string () - "Marks region outside a (possibly multi-line) Python string" - (interactive) - (let ((string-beginning (py-in-string-p))) - (when string-beginning - (goto-char string-beginning) - (set-mark (point)) - (forward-sexp) - (exchange-point-and-mark)))) - -(defun er/mark-inside-python-string () - "Marks region inside a (possibly multi-line) Python string" - (interactive) - (let ((string-beginning (py-in-string-p))) - (when string-beginning - (goto-char string-beginning) - (forward-sexp) - (skip-chars-backward er--python-string-delimiter) - (set-mark (point)) - (goto-char string-beginning) - (skip-chars-forward er--python-string-delimiter)))) - -(defun er--move-to-beginning-of-outer-python-block (start-column) - "Assumes that point is in a python block that is surrounded by -another that is not the entire module. Uses `py-indent-offset' to -find the beginning of the surrounding block because -`py-beginning-of-block-position' just looks for the previous -block-starting key word syntactically." - (while (> (current-column) (- start-column py-indent-offset)) - (forward-line -1) - (py-beginning-of-block))) - -(defun er/mark-outer-python-block () - "Attempts to mark a surrounding block by moving to the previous -line and selecting the surrounding block." - (interactive) - (let ((start-column (current-column))) - (when (> start-column 0) ; outer block is the whole buffer - (er--move-to-beginning-of-outer-python-block start-column) - (let ((block-beginning (point))) - (py-end-of-block) - (set-mark (point)) - (goto-char block-beginning))))) - -(defun er/mark-x-python-compound-statement () - "Mark the current compound statement (if, while, for, try) and all clauses." - (interactive) - (let ((secondary-re - (save-excursion - (py-mark-block-or-clause) - (cond ((looking-at "if\\|for\\|while\\|else\\|elif") "else\\|elif") - ((looking-at "try\\|except\\|finally") "except\\|finally")))) - start-col) - (when secondary-re - (py-mark-block-or-clause) - (setq start-col (current-column)) - (while (looking-at secondary-re) - (forward-line -1) (back-to-indentation) - (while (> (current-column) start-col) - (forward-line -1) (back-to-indentation))) - (set-mark (point)) - (py-end-of-clause-bol) (forward-line) (back-to-indentation) - (while (and (looking-at secondary-re) - (>= (current-column) start-col)) - (py-end-of-clause-bol) (forward-line) (back-to-indentation)) - (forward-line -1) (end-of-line) - (exchange-point-and-mark)))) - -(defun er/add-python-mode-expansions () - "Adds python-mode-specific expansions for buffers in python-mode" - (let ((try-expand-list-additions '( - er/mark-inside-python-string - er/mark-outside-python-string - py-mark-expression - py-mark-statement - py-mark-block - py-mark-def - py-mark-clause - er/mark-x-python-compound-statement - er/mark-outer-python-block - py-mark-class - ))) - (set (make-local-variable 'expand-region-skip-whitespace) nil) - (set (make-local-variable 'er/try-expand-list) - (remove 'er/mark-inside-quotes - (remove 'er/mark-outside-quotes - (append er/try-expand-list try-expand-list-additions)))))) - -(er/enable-mode-expansions 'python-mode #'er/add-python-mode-expansions) - -(provide 'python-mode-expansions) - -;; python-mode-expansions.el ends here blob - 03f38f096b4e8822b92945d6ae56ac1a2d684ec2 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/ruby-mode-expansions.el +++ /dev/null @@ -1,204 +0,0 @@ -;;; ruby-mode-expansions.el --- ruby-specific expansions for expand-region -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2023 Free Software Foundation, Inc - -;; Author: Matt Briggs -;; Based on js-mode-expansions by: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - - -;; LeWang: -;; -;; I think `er/ruby-backward-up' and `er/ruby-forward-up' are nifty -;; functions in their own right. -;; -;; I would bind them to C-M-u and C-M-d respectively. - -;; Expansions: -;; -;; -;; er/mark-ruby-block-up -;; - -;;; Code: -(eval-when-compile (require 'cl-lib)) -(require 'expand-region-core) -(require 'er-basic-expansions) -(require 'ruby-mode) - -(defvar er/ruby-block-end-re - (concat ruby-block-end-re "\\|}") - "like ruby-mode's but also for '}'") - -(defun er/ruby-skip-past-block-end () - "If line is blockend, move point to next line." - (when (looking-at er/ruby-block-end-re) - (forward-line 1))) - -(defun er/ruby-end-of-block (&optional arg) - "By default `ruby-end-of-block' goes to BOL of line containing end-re. - -This moves point to the next line to include the end of the block" - (interactive "p") - ;; Workaround for `ruby-end-of-block' in Emacs 23. - (when (re-search-forward (concat "\\<\\(" ruby-block-beg-re "\\)\\>") - (line-end-position) t) - (goto-char (match-beginning 0))) - (ruby-end-of-block (or arg 1)) - (er/ruby-skip-past-block-end)) - -(defun er/point-at-indentation () - "Return the point where current line's indentation ends." - (save-excursion - (back-to-indentation) - (point))) - -(defun er/ruby-backward-up () - "a la `paredit-backward-up'" - (interactive) - ;; if our current line ends a block, we back a line, otherwise we - (when (save-excursion - (back-to-indentation) - (looking-at-p ruby-block-end-re)) - (forward-line -1)) - (let ((orig-point (point)) - progress-beg - progress-end) - - ;; cover the case when point is in the line of beginning of block - (unless (progn (ruby-end-of-block) - (ruby-beginning-of-block) - ;; "Block beginning" is often not at indentation in Emacs 24. - (< (er/point-at-indentation) orig-point)) - (cl-loop - (ruby-beginning-of-block) - (setq progress-beg (point)) - (when (= (point) (point-min)) - (cl-return)) - (ruby-end-of-block) - (setq progress-end (line-beginning-position - (if (looking-at-p er/ruby-block-end-re) 0 1))) - (goto-char progress-beg) - (when (> progress-end orig-point) - (cl-return)))))) - -;; This command isn't used here explicitly, but it's symmetrical with -;; `er/ruby-backward-up', and nifty for interactive use. -(defun er/ruby-forward-up () - "a la `paredit-forward-up'" - (interactive) - (er/ruby-backward-up) - (er/ruby-end-of-block)) - -(defun er/get-ruby-block (&optional pos) - "return (beg . end) of current block" - (setq pos (or pos (point))) - (save-excursion - (goto-char pos) - (cons (progn - (er/ruby-backward-up) - (er/point-at-indentation)) - (progn - (er/ruby-end-of-block) - (point))))) - -(defun er/mark-ruby-block-up-1 () - (er/ruby-backward-up) - (set-mark (er/point-at-indentation)) - (er/ruby-end-of-block) - (exchange-point-and-mark)) - -(defun er/mark-ruby-block-up (&optional no-recurse) - "mark the next level up." - (interactive) - (if (use-region-p) - (let* ((orig-end (region-end)) - (orig-beg (region-beginning)) - (orig-len (- orig-end orig-beg)) - (prev-block-point - (or (save-excursion - (goto-char orig-end) - (forward-line 0) - (back-to-indentation) - (cond ((looking-at-p er/ruby-block-end-re) - (line-beginning-position 0)) - ((re-search-forward - (concat "\\<\\(" ruby-block-beg-re "\\)\\>") - (line-end-position) - t) - (line-beginning-position 2))) ) - (point))) - (prev-block-info (er/get-ruby-block prev-block-point)) - (prev-block-beg (car prev-block-info)) - (prev-block-end (cdr prev-block-info)) - (prev-block-len (- prev-block-end prev-block-beg))) - (if (and (>= orig-beg prev-block-beg) - (<= orig-end prev-block-end) - (< orig-len prev-block-len)) - ;; expand to previous block if it contains and grows current - ;; region - (progn - (deactivate-mark) - (goto-char prev-block-point) - (or no-recurse - (er/mark-ruby-block-up 'no-recurse))) - (er/mark-ruby-block-up-1))) - (er/mark-ruby-block-up-1))) - -(defun er/mark-ruby-instance-variable () - "Marks instance variables in ruby. -Assumes that point is at the @ - if it is inside the word, that will -be marked first anyway." - (when (looking-at "@") - (forward-char 1)) - (when (er/looking-back-exact "@") - (er/mark-symbol) - (forward-char -1))) - -(defun er/mark-ruby-heredoc () - "Marks a heredoc, since `er/mark-inside-quotes' assumes single quote chars." - (let ((ppss (syntax-ppss))) - (when (elt ppss 3) - (let ((s-start (elt ppss 8))) - (goto-char s-start) - (when (save-excursion - (beginning-of-line) - (re-search-forward "<<\\(-?\\)['\"]?\\([a-zA-Z0-9_]+\\)" s-start nil)) - (let ((allow-indent (string= "-" (match-string 1))) - (terminator (match-string 2)) - (heredoc-start (save-excursion - (forward-line) - (point)))) - (forward-sexp 1) - (forward-line -1) - (when (looking-at (concat "^" (if allow-indent "[ \t]*" "") terminator "$")) - (set-mark heredoc-start) - (exchange-point-and-mark)))))))) - -(defun er/add-ruby-mode-expansions () - "Adds Ruby-specific expansions for buffers in ruby-mode" - (set (make-local-variable 'er/try-expand-list) - (remove 'er/mark-defun - (append - (default-value 'er/try-expand-list) - '(er/mark-ruby-instance-variable - er/mark-ruby-block-up - er/mark-ruby-heredoc))))) - -(er/enable-mode-expansions 'ruby-mode #'er/add-ruby-mode-expansions) -(provide 'ruby-mode-expansions) blob - f4ec5a4469992ddf3f12cc13642c8893dec215e3 (mode 755) blob + /dev/null --- elpa/expand-region-1.0.0/run-tests.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -e - -cask exec ecukes "$@" blob - db1119a43b16c532e239c3311cd2b56270907602 (mode 755) blob + /dev/null --- elpa/expand-region-1.0.0/run-travis-ci.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -e - -cd "$(dirname "$0")" - -ECUKES_EMACS=${EMACS:-$(which emacs)} -export ECUKES_EMACS - -echo "*** Emacs version ***" -echo "ECUKES_EMACS = $ECUKES_EMACS" -"$ECUKES_EMACS" --version -echo - -exec ./run-tests.sh $TAGS blob - 1af2fc2eba06490554fb8aba3d225a62b22c787f (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/sml-mode-expansions.el +++ /dev/null @@ -1,64 +0,0 @@ -;;; sml-mode-expansions.el --- Expansions for expand-region to be used in sml-mode -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Alexis Gallagher -;; Based on js-mode-expansions by: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides extra expansions for sml-mode: -;; - various expression (case, if, let) -;; - fun bindings -;; -;; Tested with sml-mode version 6.3 -;; -;; Feel free to contribute any other expansions for SML at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(declare-function sml-find-matching-starter "sml-mode") - -;; TODO: comma-delimited elements within a list,tuple,record -;; TODO: match expression, patterns -;; TODO: individual field, record type -;; TODO: head-or-tail, then cons expression - -(defun er/sml-mark-keyword-prefixed-expression () - "Mark the surrounding expression." - (interactive) - (progn - (sml-find-matching-starter '("case" "let" "if" "raise")) - (mark-sexp))) - - -(defun er/add-sml-mode-expansions () - "Adds expansions for buffers in `sml-mode'." - (set (make-local-variable 'er/try-expand-list) - (append er/try-expand-list - '(sml-mark-function - er/sml-mark-keyword-prefixed-expression - mark-sexp)))) - -(er/enable-mode-expansions 'sml-mode #'er/add-sml-mode-expansions) - -(provide 'sml-mode-expansions) - -;; sml-mode-expansions.el ends here blob - 2e6600251c4b5c2e8d406d70e05351c16482ab46 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/subword-mode-expansions.el +++ /dev/null @@ -1,52 +0,0 @@ -;;; subword-mode-expansions.el --- Expansions for subword-mode to be used for CamelCase -*- lexical-binding: t; -*- - -;; Copyright (C) 2014-2023 Free Software Foundation, Inc - -;; Author: Lefteris Karapetsas -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Provides extra expansions for subword mode so that when -;; subword-mode is non-nil different words can be selected in CamelCase. -;; Feel free to contribute any other expansions: -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(require 'subword) - -(defun er/mark-subword () - "Mark a subword, a part of a CamelCase identifier." - (interactive) - (when (and subword-mode - expand-region-subword-enabled) - (subword-right 1) - (set-mark (point)) - (subword-left 1))) - -(defun er/add-subword-mode-expansions () - "Add expansions for buffers in `subword-mode'." - (set (make-local-variable 'er/try-expand-list) - (append er/try-expand-list - '(er/mark-subword)))) - -(er/enable-minor-mode-expansions 'subword-mode 'er/add-subword-mode-expansions) - -(provide 'subword-mode-expansions) -;;; subword-mode-expansions.el ends here blob - 628443bb5787ff4638ff82c45e9a2715cb972be5 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/text-mode-expansions.el +++ /dev/null @@ -1,65 +0,0 @@ -;;; text-mode-expansions.el --- Expansions for expand-region to be used in text -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Ivan Andrus -;; Based on js-mode-expansions by: Magnar Sveen -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; Feel free to contribute any other expansions for normal text at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) - -(defun er/mark-text-sentence () - "Marks one sentence." - (interactive) - ;; The obvious - ;; (backward-sentence 1) (mark-end-of-sentence 1) - ;; doesn't work here because it's repeated and the selection keeps - ;; growing by sentences, which isn't what's wanted. - (forward-sentence 1) - (set-mark (point)) - (backward-sentence 1)) - -(defun er/mark-text-paragraph () - "Marks one paragraph." - (interactive) - (mark-paragraph) - (skip-chars-forward er--space-str)) - -(defun er/add-text-mode-expansions () - "Adds expansions for buffers in `text-mode' except for `html-mode'. -Unfortunately `html-mode' inherits from `text-mode' and -text-mode-expansions don't work well in `html-mode'." - (unless (member major-mode expand-region-exclude-text-mode-expansions) - (set (make-local-variable 'er/try-expand-list) - (append - er/try-expand-list - '(er/mark-text-sentence - er/mark-text-paragraph - mark-page))))) - -(er/enable-mode-expansions 'text-mode #'er/add-text-mode-expansions) - -(provide 'text-mode-expansions) - -;; text-mode-expansions.el ends here blob - a3b09cdf562f3f245f3e3e9881e7bf2a24b0bdea (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/the-org-mode-expansions.el +++ /dev/null @@ -1,120 +0,0 @@ -;;; the-org-mode-expansions.el --- Expansions for expand-region to be used in org-mode -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Author: Magnar Sveen -;; Based on text-mode-expansions by: Ivan Andrus -;; Keywords: marking region - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: - -;; The file needs to be weirdly name (prefixed with the-) to avoid -;; conflict with org-reload, which bases its functionality on the names -;; of files, for some reason. -;; -;; Feel free to contribute any other expansions for org-mode at -;; -;; https://github.com/magnars/expand-region.el - -;;; Code: - -(require 'expand-region-core) -(require 'er-basic-expansions) -(require 'org-macs) -(require 'org-element) - -(declare-function org-up-element "org") -(declare-function org-mark-subtree "org") - -(defun er/mark-org-element () - (interactive) - (let* ((el (org-element-at-point)) - (begin (plist-get (cadr el) :begin)) - (end (plist-get (cadr el) :end))) - (goto-char begin) - (set-mark (point)) - (goto-char end) - (exchange-point-and-mark))) - -(defun er/mark-org-element-parent () - (interactive) - (let* ((el (plist-get (cadr (org-element-at-point)) :parent)) - (begin (plist-get (cadr el) :begin)) - (end (plist-get (cadr el) :end))) - (when (and begin end) - (goto-char begin) - (set-mark (point)) - (goto-char end) - (exchange-point-and-mark)))) - -(defun er/mark-sentence () - "Marks one sentence." - (interactive) - (forward-char 1) - (backward-sentence 1) - (set-mark (point)) - (forward-sentence 1) - (exchange-point-and-mark)) - -(defun er/mark-paragraph () - "Marks one paragraph." - (interactive) - (mark-paragraph) - (exchange-point-and-mark) - (skip-chars-backward er--space-str) - (exchange-point-and-mark) - (skip-chars-forward er--space-str)) - -(defun er/mark-org-code-block () - "Marks an org-code-block." - (interactive) - (let ((case-fold-search t) - (re "#\\+begin_\\(\\sw+\\)")) - (unless (looking-at re) - (search-backward-regexp re)) - (set-mark (point)) - (search-forward (concat "#+end_" (match-string 1))) - (exchange-point-and-mark))) - -(defun er/mark-org-parent () - "Marks a heading 1 level up from current subheading" - (interactive) - (org-up-element) - (org-mark-subtree)) - -(defun er/save-org-mode-excursion (action) - "Save outline visibility while expanding in org-mode" - (org-save-outline-visibility t - (funcall action))) - -(defun er/add-org-mode-expansions () - "Adds org-specific expansions for buffers in org-mode" - (set (make-local-variable 'er/try-expand-list) - (append - (remove #'er/mark-defun er/try-expand-list) - '(org-mark-subtree - er/mark-org-element - er/mark-org-element-parent - er/mark-org-code-block - er/mark-sentence - er/mark-org-parent - er/mark-paragraph))) - (set (make-local-variable 'er/save-mode-excursion) - #'er/save-org-mode-excursion)) - -(er/enable-mode-expansions 'org-mode #'er/add-org-mode-expansions) - -(provide 'the-org-mode-expansions) blob - d1f2762c5f2c023b86f37d243964b3b85ad1d211 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/watch-tests.watchr +++ /dev/null @@ -1,45 +0,0 @@ -ENV["WATCHR"] = "1" -system 'clear' - -def run(cmd) - `#{cmd}` -end - -def run_all_tests - system('clear') - result = run "./run-tests.sh" - puts result -end - -def run_test(file) - system('clear') - result = run "./run-tests.sh #{file} --verbose" - puts result -end - -run_all_tests -watch('.*.feature') { |file| run_test file } -watch('.*.el') { run_all_tests } - -# Ctrl-\ -Signal.trap 'QUIT' do - puts " --- Running all tests ---\n\n" - run_all_tests -end - -@interrupted = false - -# Ctrl-C -Signal.trap 'INT' do - if @interrupted then - @wants_to_quit = true - abort("\n") - else - puts "Interrupt a second time to quit" - @interrupted = true - Kernel.sleep 1.5 - # raise Interrupt, nil # let the run loop catch it - run_all_tests - @interrupted = false - end -end blob - 8fad2efb36298e7937d8843e197b39ea68ffdb54 (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/web-mode-expansions.el +++ /dev/null @@ -1,32 +0,0 @@ -;;; web-mode-expansions.el --- Thin layer for adapting fxbois's web-mode-mark-and-expand function -*- lexical-binding: t; -*- -;;; to expand-region - -;; Copyright (C) 2012-2023 Free Software Foundation, Inc - -;; Authors: Rotem Yaari -;; Based on, and makes use of web-mode.el by fxbois - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Code: - -(require 'expand-region-core) - -(defun er/add-web-mode-expansions () - (set (make-local-variable 'er/try-expand-list) - (cons 'web-mode-mark-and-expand er/try-expand-list))) - -(er/enable-mode-expansions 'web-mode #'er/add-web-mode-expansions) - -(provide 'web-mode-expansions) blob - 7bc4aeb76edd1af52c6a3dc347ffec991ba06d3f (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0/yaml-mode-expansions.el +++ /dev/null @@ -1,194 +0,0 @@ -;;; yaml-mode-expansions.el --- expansions for yaml mode -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2023 Free Software Foundation, Inc. - -;; Author: Aaron Gonzales -;; Keywords: marking region yaml YAML expand - -;; This program 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 of the License, or -;; (at your option) any later version. - -;; This program 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 this program. If not, see . - -;;; Commentary: -;; -;; - Additions implemented here: -;; - er/mark-yaml-key-value -;; - er/mark-yaml-list-item -;; - er/mark-yaml-block -;; - er/mark-yaml-outer-block -;; - er/mark-yaml-inner-block - - -;;; Code: - -(require 'expand-region-core) - -(defconst yaml-indent 2) - -(unless (fboundp 'yaml-indent-offset) - (defalias 'yaml-indent-offset #'yaml-indent)) - -(defvar er--yaml-key-value-regex - (rx (one-or-more - (any "0-9A-Za-z")) - ":" - (zero-or-more " ") - (one-or-more - (any "0-9A-Za-z" " '_-")))) - -(defvar er--yaml-list-item-regex - (rx (seq "- " - (one-or-more - (any "0-9A-Za-z" "\"':=_-"))))) - -(defvar er--yaml-block-regex - (rx (seq (zero-or-more - (any " -")) - (one-or-more - (any "0-9A-Za-z" " '_-")) - ":\n"))) - -(defun er--get-regex-indentation-level (regex) - "Return the indentation level of the code with respect to the REGEX passed." - (when (looking-at regex) - ;; Block start means that the next level is deeper. - (+ (current-indentation) yaml-indent-offset) ;FIXME: Unused? - ;; Assuming we're inside the block that we want to mark - (current-indentation))) - -(defun er/mark-yaml-line-base (regex) - "Mark line of yaml file based on simple REGEX." - (back-to-indentation) - (when (looking-at regex) - (set-mark (line-end-position)))) - -(defun er/mark-yaml-block-static-base (regex) - "Mark yaml block based on REGEX passed." - ;; go bac to indentation so always can get regexp - (back-to-indentation) - ;; make sure the cursor is set inside the block - ;; mark point at this higher code block - (set-mark (point)) - ;; save level of this blocks indentation - (let ((block-indentation (current-indentation))) - (forward-line 1) - (while (and - ;; No need to go beyond the end of the buffer. Can't use - ;; eobp as the loop places the point at the beginning of - ;; line, but eob might be at the end of the line. - (not (= (point-max) (line-end-position))) - ;; Proceed if: indentation is too deep - (or (> (current-indentation) block-indentation) - ;; Looking at an empty line - (looking-at (rx line-start (* whitespace) line-end)) - ;; We're not looking at the start of a YAML block - ;; and the indent is deeper than the block's indent - (and (not (looking-at regex)) - (> (current-indentation) block-indentation)))) - (forward-line 1) - (back-to-indentation)) - ;; Find the end of the block by skipping comments backwards - (python-util-forward-comment -1) - (exchange-point-and-mark)) - (back-to-indentation)) - -(defun er/mark-yaml-block-base (regex &optional next-indent-level) - "Mark yaml block based on REGEX passed. -NEXT-INDENT-LEVEL can be used to search outer blocks when necessary." - ;; go bac to indentation so always can get regexp - (back-to-indentation) - ;; make sure the cursor is set inside the block - (let ((next-indent-level - (or - ;; Use the given level - next-indent-level - ;; used to mark current block - (er--get-regex-indentation-level regex)))) - ;; if true then at start of block and wanna mark itself - ;; else were are inside the block already and will mark it))) - ;; move up the code unti a parent code block is reached - (while (and (>= (current-indentation) next-indent-level) - (not (eq (current-indentation) 0))) - (re-search-backward regex (point-min) t) - (back-to-indentation)) - ;; mark point at this higher code block - (set-mark (point)) - ;; save level of this blocks indentation - (let ((block-indentation (current-indentation))) - (forward-line 1) - (while (and - ;; No need to go beyond the end of the buffer. Can't use - ;; eobp as the loop places the point at the beginning of - ;; line, but eob might be at the end of the line. - (not (= (point-max) (line-end-position))) - ;; Proceed if: indentation is too deep - (or (> (current-indentation) block-indentation) - ;; Looking at an empty line - (looking-at (rx line-start (* whitespace) line-end)) - ;; We're not looking at the start of a YAML block - ;; and the indent is deeper than the block's indent - (and (not (looking-at regex)) - (> (current-indentation) block-indentation)))) - (forward-line 1) - (back-to-indentation)) - ;; Find the end of the block by skipping comments backwards - (python-util-forward-comment -1) - (exchange-point-and-mark))) - (back-to-indentation)) - -(defun er/mark-yaml-key-value () - "Mark a yaml key-value pair." - (interactive) - (er/mark-yaml-line-base er--yaml-key-value-regex)) - -(defun er/mark-yaml-list-item () - "Mark a yaml list item." - (interactive) - (er/mark-yaml-line-base er--yaml-list-item-regex)) - -(defun er/mark-yaml-inner-block () - "Mark the yaml contents of the block at point. -Command that wraps `er/mark-yaml-block-base'." - (interactive) - (er/mark-yaml-block-base er--yaml-block-regex (current-indentation)) - (forward-line) - (back-to-indentation)) - -(defun er/mark-yaml-block () - "Mark the yaml block that point is currently at the top of. -Command that wraps `er/mark-yaml-block-base'." - (interactive) - (er/mark-yaml-block-static-base er--yaml-block-regex)) - -(defun er/mark-yaml-outer-block () - "Mark the outer yaml block that surrounds the block around point. -Command that wraps `er/mark-yaml-block-base'." - (interactive) - (er/mark-yaml-block-base er--yaml-block-regex (current-indentation))) - -(defun er/add-yaml-mode-expansions () - "Add yaml-mode-specific expansions for buffers in yaml-mode." - (let ((try-expand-list-additions '(er/mark-symbol - er/mark-outside-quotes - er/mark-yaml-list-item - er/mark-yaml-key-value - er/mark-yaml-block - er/mark-yaml-outer-block - er/mark-yaml-inner-block))) - (set (make-local-variable 'expand-region-skip-whitespace) nil) - (set (make-local-variable 'er/try-expand-list) try-expand-list-additions))) - -(er/enable-mode-expansions 'yaml-mode #'er/add-yaml-mode-expansions) - -(provide 'yaml-mode-expansions) - -;;; yaml-mode-expansions.el ends here blob - a525e40fb7597aee11b4cd6efa48b198c4e7d40f (mode 644) blob + /dev/null --- elpa/expand-region-1.0.0.signed +++ /dev/null @@ -1,2 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2023-10-20T11:05:04+0200 using RSA -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2023-10-20T11:05:04+0200 using EDDSA \ No newline at end of file blob - da471f0c4d65226d5941632105cd6e9368f17063 (mode 644) blob + /dev/null --- elpa/f-0.20.0/f-autoloads.el +++ /dev/null @@ -1,28 +0,0 @@ -;;; f-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from f.el - -(register-definition-prefixes "f" '("f-")) - -;;; End of scraped data - -(provide 'f-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; f-autoloads.el ends here blob - 09e7a2f6dbe20a61a84719d9a7b0b010363c1e6d (mode 644) blob + /dev/null --- elpa/f-0.20.0/f-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; Generated package description from f.el -*- no-byte-compile: t -*- -(define-package "f" "0.20.0" "Modern API for working with files and directories" '((s "1.7.0") (dash "2.2.0")) :commit "de6d4d40ddc844eee643e92d47b9d6a63fbebb48" :authors '(("Johan Andersson" . "johan.rejeep@gmail.com")) :maintainers '(("Johan Andersson" . "johan.rejeep@gmail.com")) :maintainer '("Johan Andersson" . "johan.rejeep@gmail.com") :keywords '("files" "directories") :url "http://github.com/rejeep/f.el") blob - 0ab3898a2221b8395d7a50b1a2f64eb9096943b1 (mode 644) blob + /dev/null --- elpa/f-0.20.0/f.el +++ /dev/null @@ -1,625 +0,0 @@ -;;; f.el --- Modern API for working with files and directories -*- lexical-binding: t; -*- - -;; Copyright (C) 2013 Johan Andersson - -;; Author: Johan Andersson -;; Maintainer: Johan Andersson -;; Version: 0.20.0 -;; Package-Version: 0.20.0 -;; Package-Commit: de6d4d40ddc844eee643e92d47b9d6a63fbebb48 -;; Keywords: files, directories -;; URL: http://github.com/rejeep/f.el -;; Package-Requires: ((s "1.7.0") (dash "2.2.0")) - -;; This file is NOT part of GNU Emacs. - -;;; License: - -;; This program 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 program 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. - -;;; Code: - - - -(require 's) -(require 'dash) - -(put 'f-guard-error 'error-conditions '(error f-guard-error)) -(put 'f-guard-error 'error-message "Destructive operation outside sandbox") - -(defvar f--guard-paths nil - "List of allowed paths to modify when guarded. - -Do not modify this variable.") - -(defmacro f--destructive (path &rest body) - "If PATH is allowed to be modified, yield BODY. - -If PATH is not allowed to be modified, throw error." - (declare (indent 1)) - `(if f--guard-paths - (if (--any? (or (f-same? it ,path) - (f-ancestor-of? it ,path)) f--guard-paths) - (progn ,@body) - (signal 'f-guard-error (list ,path f--guard-paths))) - ,@body)) - - -;;;; Paths - -(defun f-join (&rest args) - "Join ARGS to a single path." - (let (path (relative (f-relative? (car args)))) - (-map - (lambda (arg) - (setq path (f-expand arg path))) - args) - (if relative (f-relative path) path))) - -(defun f-split (path) - "Split PATH and return list containing parts." - (let ((parts (s-split (f-path-separator) path 'omit-nulls))) - (if (f-absolute? path) - (push (f-path-separator) parts) - parts))) - -(defun f-expand (path &optional dir) - "Expand PATH relative to DIR (or `default-directory'). -PATH and DIR can be either a directory names or directory file -names. Return a directory name if PATH is a directory name, and -a directory file name otherwise. File name handlers are -ignored." - (let (file-name-handler-alist) - (expand-file-name path dir))) - -(defun f-filename (path) - "Return the name of PATH." - (file-name-nondirectory (directory-file-name path))) - -(defalias 'f-parent 'f-dirname) -(defun f-dirname (path) - "Return the parent directory to PATH." - (let ((parent (file-name-directory - (directory-file-name (f-expand path default-directory))))) - (unless (f-same? path parent) - (if (f-relative? path) - (f-relative parent) - (directory-file-name parent))))) - -(defun f-common-parent (paths) - "Return the deepest common parent directory of PATHS." - (cond - ((not paths) nil) - ((not (cdr paths)) (f-parent (car paths))) - (:otherwise - (let* ((paths (-map 'f-split paths)) - (common (caar paths)) - (re nil)) - (while (and (not (null (car paths))) (--all? (equal (car it) common) paths)) - (setq paths (-map 'cdr paths)) - (push common re) - (setq common (caar paths))) - (cond - ((null re) "") - ((and (= (length re) 1) (f-root? (car re))) - (f-root)) - (:otherwise - (concat (apply 'f-join (nreverse re)) "/"))))))) - -(defun f-ext (path) - "Return the file extension of PATH. - -The extension, in a file name, is the part that follows the last -'.', excluding version numbers and backup suffixes." - (file-name-extension path)) - -(defun f-no-ext (path) - "Return everything but the file extension of PATH." - (file-name-sans-extension path)) - -(defun f-swap-ext (path ext) - "Return PATH but with EXT as the new extension. -EXT must not be nil or empty." - (if (s-blank? ext) - (error "Extension cannot be empty or nil") - (concat (f-no-ext path) "." ext))) - -(defun f-base (path) - "Return the name of PATH, excluding the extension of file." - (f-no-ext (f-filename path))) - -(defun f-relative (path &optional dir) - "Return PATH relative to DIR." - (file-relative-name path dir)) - -(defalias 'f-abbrev 'f-short) -(defun f-short (path) - "Return abbrev of PATH. See `abbreviate-file-name'." - (abbreviate-file-name path)) - -(defun f-long (path) - "Return long version of PATH." - (f-expand path)) - -(defun f-canonical (path) - "Return the canonical name of PATH." - (file-truename path)) - -(defun f-slash (path) - "Append slash to PATH unless one already. - -Some functions, such as `call-process' requires there to be an -ending slash." - (if (f-dir? path) - (file-name-as-directory path) - path)) - -(defun f-full (path) - "Return absolute path to PATH, with ending slash." - (f-slash (f-long path))) - -(defun f--uniquify (paths) - "Helper for `f-uniquify' and `f-uniquify-alist'." - (let* ((files-length (length paths)) - (uniq-filenames (--map (cons it (f-filename it)) paths)) - (uniq-filenames-next (-group-by 'cdr uniq-filenames))) - (while (/= files-length (length uniq-filenames-next)) - (setq uniq-filenames-next - (-group-by 'cdr - (--mapcat - (let ((conf-files (cdr it))) - (if (> (length conf-files) 1) - (--map (cons (car it) (concat (f-filename (s-chop-suffix (cdr it) (car it))) (f-path-separator) (cdr it))) conf-files) - conf-files)) - uniq-filenames-next)))) - uniq-filenames-next)) - -(defun f-uniquify (files) - "Return unique suffixes of FILES. - -This function expects no duplicate paths." - (-map 'car (f--uniquify files))) - -(defun f-uniquify-alist (files) - "Return alist mapping FILES to unique suffixes of FILES. - -This function expects no duplicate paths." - (-map 'cadr (f--uniquify files))) - - -;;;; I/O - -(defun f-read-bytes (path) - "Read binary data from PATH. - -Return the binary data as unibyte string." - (with-temp-buffer - (set-buffer-multibyte nil) - (setq buffer-file-coding-system 'binary) - (insert-file-contents-literally path) - (buffer-substring-no-properties (point-min) (point-max)))) - -(defalias 'f-read 'f-read-text) -(defun f-read-text (path &optional coding) - "Read text with PATH, using CODING. - -CODING defaults to `utf-8'. - -Return the decoded text as multibyte string." - (decode-coding-string (f-read-bytes path) (or coding 'utf-8))) - -(defalias 'f-write 'f-write-text) -(defun f-write-text (text coding path) - "Write TEXT with CODING to PATH. - -TEXT is a multibyte string. CODING is a coding system to encode -TEXT with. PATH is a file name to write to." - (f-write-bytes (encode-coding-string text coding) path)) - -(defun f-unibyte-string-p (s) - "Determine whether S is a unibyte string." - (not (multibyte-string-p s))) - -(defun f-write-bytes (data path) - "Write binary DATA to PATH. - -DATA is a unibyte string. PATH is a file name to write to." - (f--destructive path - (unless (f-unibyte-string-p data) - (signal 'wrong-type-argument (list 'f-unibyte-string-p data))) - (let ((file-coding-system-alist nil) - (coding-system-for-write 'binary)) - (with-temp-file path - (setq buffer-file-coding-system 'binary) - (set-buffer-multibyte nil) - (insert data))))) - -(defalias 'f-append 'f-append-text) -(defun f-append-text (text coding path) - "Append TEXT with CODING to PATH. - -If PATH does not exist, it is created." - (f-append-bytes (encode-coding-string text coding) path)) - -(defun f-append-bytes (data path) - "Append binary DATA to PATH. - -If PATH does not exist, it is created." - (let ((content - (if (f-file? path) - (f-read-bytes path) - ""))) - (f-write-bytes (concat content data) path))) - - -;;;; Destructive - -(defun f-mkdir (&rest dirs) - "Create directories DIRS." - (let (path) - (-each - dirs - (lambda (dir) - (setq path (f-expand dir path)) - (unless (f-directory? path) - (f--destructive path (make-directory path))))))) - -(defun f-delete (path &optional force) - "Delete PATH, which can be file or directory. - -If FORCE is t, a directory will be deleted recursively." - (f--destructive path - (if (or (f-file? path) (f-symlink? path)) - (delete-file path) - (delete-directory path force)))) - -(defun f-symlink (source path) - "Create a symlink to SOURCE from PATH." - (f--destructive path (make-symbolic-link source path))) - -(defun f-move (from to) - "Move or rename FROM to TO. -If TO is a directory name, move FROM into TO." - (f--destructive to (rename-file from to t))) - -(defun f-copy (from to) - "Copy file or directory FROM to TO. -If FROM names a directory and TO is a directory name, copy FROM -into TO as a subdirectory." - (f--destructive to - (if (f-file? from) - (copy-file from to) - ;; The behavior of `copy-directory' differs between Emacs 23 and - ;; 24 in that in Emacs 23, the contents of `from' is copied to - ;; `to', while in Emacs 24 the directory `from' is copied to - ;; `to'. We want the Emacs 24 behavior. - (if (> emacs-major-version 23) - (copy-directory from to) - (if (f-dir? to) - (progn - (apply 'f-mkdir (f-split to)) - (let ((new-to (f-expand (f-filename from) to))) - (copy-directory from new-to))) - (copy-directory from to)))))) - -(defun f-copy-contents (from to) - "Copy contents in directory FROM, to directory TO." - (unless (f-exists? to) - (error "Cannot copy contents to non existing directory %s" to)) - (unless (f-dir? from) - (error "Cannot copy contents as %s is a file" from)) - (--each (f-entries from) - (f-copy it (file-name-as-directory to)))) - -(defun f-touch (path) - "Update PATH last modification date or create if it does not exist." - (f--destructive path - (if (f-file? path) - (set-file-times path) - (f-write-bytes "" path)))) - - -;;;; Predicates - -(defun f-exists? (path) - "Return t if PATH exists, false otherwise." - (file-exists-p path)) - -(defalias 'f-exists-p 'f-exists?) - -(defalias 'f-dir? 'f-directory?) -(defalias 'f-dir-p 'f-dir?) - -(defun f-directory? (path) - "Return t if PATH is directory, false otherwise." - (file-directory-p path)) - -(defalias 'f-directory-p 'f-directory?) - -(defun f-file? (path) - "Return t if PATH is file, false otherwise." - (file-regular-p path)) - -(defalias 'f-file-p 'f-file?) - -(defun f-symlink? (path) - "Return t if PATH is symlink, false otherwise." - (not (not (file-symlink-p path)))) - -(defalias 'f-symlink-p 'f-symlink?) - -(defun f-readable? (path) - "Return t if PATH is readable, false otherwise." - (file-readable-p path)) - -(defalias 'f-readable-p 'f-readable?) - -(defun f-writable? (path) - "Return t if PATH is writable, false otherwise." - (file-writable-p path)) - -(defalias 'f-writable-p 'f-writable?) - -(defun f-executable? (path) - "Return t if PATH is executable, false otherwise." - (file-executable-p path)) - -(defalias 'f-executable-p 'f-executable?) - -(defun f-absolute? (path) - "Return t if PATH is absolute, false otherwise." - (file-name-absolute-p path)) - -(defalias 'f-absolute-p 'f-absolute?) - -(defun f-relative? (path) - "Return t if PATH is relative, false otherwise." - (not (f-absolute? path))) - -(defalias 'f-relative-p 'f-relative?) - -(defun f-root? (path) - "Return t if PATH is root directory, false otherwise." - (not (f-parent path))) - -(defalias 'f-root-p 'f-root?) - -(defun f-ext? (path &optional ext) - "Return t if extension of PATH is EXT, false otherwise. - -If EXT is nil or omitted, return t if PATH has any extension, -false otherwise. - -The extension, in a file name, is the part that follows the last -'.', excluding version numbers and backup suffixes." - (if ext - (string= (f-ext path) ext) - (not (eq (f-ext path) nil)))) - -(defalias 'f-ext-p 'f-ext?) - -(defalias 'f-equal? 'f-same?) -(defalias 'f-equal-p 'f-equal?) - -(defun f-same? (path-a path-b) - "Return t if PATH-A and PATH-B are references to same file." - (when (and (f-exists? path-a) - (f-exists? path-b)) - (equal - (f-canonical (directory-file-name (f-expand path-a))) - (f-canonical (directory-file-name (f-expand path-b)))))) - -(defalias 'f-same-p 'f-same?) - -(defun f-parent-of? (path-a path-b) - "Return t if PATH-A is parent of PATH-B." - (--when-let (f-parent path-b) - (f-same? path-a it))) - -(defalias 'f-parent-of-p 'f-parent-of?) - -(defun f-child-of? (path-a path-b) - "Return t if PATH-A is child of PATH-B." - (--when-let (f-parent path-a) - (f-same? it path-b))) - -(defalias 'f-child-of-p 'f-child-of?) - -(defun f-ancestor-of? (path-a path-b) - "Return t if PATH-A is ancestor of PATH-B." - (unless (f-same? path-a path-b) - (s-starts-with? (f-full path-a) - (f-full path-b)))) - -(defalias 'f-ancestor-of-p 'f-ancestor-of?) - -(defun f-descendant-of? (path-a path-b) - "Return t if PATH-A is desendant of PATH-B." - (unless (f-same? path-a path-b) - (s-starts-with? (f-full path-b) - (f-full path-a)))) - -(defalias 'f-descendant-of-p 'f-descendant-of?) - -(defun f-hidden? (path) - "Return t if PATH is hidden, nil otherwise." - (unless (f-exists? path) - (error "Path does not exist: %s" path)) - (string= (substring path 0 1) ".")) - -(defalias 'f-hidden-p 'f-hidden?) - -(defun f-empty? (path) - "If PATH is a file, return t if the file in PATH is empty, nil otherwise. -If PATH is directory, return t if directory has no files, nil otherwise." - (if (f-directory? path) - (equal (f-files path nil t) nil) - (= (f-size path) 0))) - -(defalias 'f-empty-p 'f-empty?) - - -;;;; Stats - -(defun f-size (path) - "Return size of PATH. - -If PATH is a file, return size of that file. If PATH is -directory, return sum of all files in PATH." - (if (f-directory? path) - (-sum (-map 'f-size (f-files path nil t))) - (nth 7 (file-attributes path)))) - -(defun f-depth (path) - "Return the depth of PATH. - -At first, PATH is expanded with `f-expand'. Then the full path is used to -detect the depth. -'/' will be zero depth, '/usr' will be one depth. And so on." - (- (length (f-split (f-expand path))) 1)) - - -;;;; Misc - -(defun f-this-file () - "Return path to this file." - (cond - (load-in-progress load-file-name) - ((and (boundp 'byte-compile-current-file) byte-compile-current-file) - byte-compile-current-file) - (:else (buffer-file-name)))) - -(defvar f--path-separator nil - "A variable to cache result of `f-path-separator'.") - -(defun f-path-separator () - "Return path separator." - (or f--path-separator - (setq f--path-separator (substring (f-join "x" "y") 1 2)))) - -(defun f-glob (pattern &optional path) - "Find PATTERN in PATH." - (file-expand-wildcards - (f-join (or path default-directory) pattern))) - -(defun f--collect-entries (path recursive) - (let (result - (entries - (-reject - (lambda (file) - (or - (equal (f-filename file) ".") - (equal (f-filename file) ".."))) - (directory-files path t)))) - (cond (recursive - (-map - (lambda (entry) - (if (f-file? entry) - (setq result (cons entry result)) - (when (f-directory? entry) - (setq result (cons entry result)) - (setq result (append result (f--collect-entries entry recursive)))))) - entries)) - (t (setq result entries))) - result)) - -(defmacro f--entries (path body &optional recursive) - "Anaphoric version of `f-entries'." - `(f-entries - ,path - (lambda (path) - (let ((it path)) - ,body)) - ,recursive)) - -(defun f-entries (path &optional fn recursive) - "Find all files and directories in PATH. - -FN - called for each found file and directory. If FN returns a thruthy -value, file or directory will be included. -RECURSIVE - Search for files and directories recursive." - (let ((entries (f--collect-entries path recursive))) - (if fn (-select fn entries) entries))) - -(defmacro f--directories (path body &optional recursive) - "Anaphoric version of `f-directories'." - `(f-directories - ,path - (lambda (path) - (let ((it path)) - ,body)) - ,recursive)) - -(defun f-directories (path &optional fn recursive) - "Find all directories in PATH. See `f-entries'." - (let ((directories (-select 'f-directory? (f--collect-entries path recursive)))) - (if fn (-select fn directories) directories))) - -(defmacro f--files (path body &optional recursive) - "Anaphoric version of `f-files'." - `(f-files - ,path - (lambda (path) - (let ((it path)) - ,body)) - ,recursive)) - -(defun f-files (path &optional fn recursive) - "Find all files in PATH. See `f-entries'." - (let ((files (-select 'f-file? (f--collect-entries path recursive)))) - (if fn (-select fn files) files))) - -(defmacro f--traverse-upwards (body &optional path) - "Anaphoric version of `f-traverse-upwards'." - `(f-traverse-upwards - (lambda (dir) - (let ((it dir)) - ,body)) - ,path)) - -(defun f-traverse-upwards (fn &optional path) - "Traverse up as long as FN return nil, starting at PATH. - -If FN returns a non-nil value, the path sent as argument to FN is -returned. If no function callback return a non-nil value, nil is -returned." - (unless path - (setq path default-directory)) - (when (f-relative? path) - (setq path (f-expand path))) - (if (funcall fn path) - path - (unless (f-root? path) - (f-traverse-upwards fn (f-parent path))))) - -(defun f-root () - "Return absolute root." - (f-traverse-upwards 'f-root?)) - -(defmacro f-with-sandbox (path-or-paths &rest body) - "Only allow PATH-OR-PATHS and decendants to be modified in BODY." - (declare (indent 1)) - `(let ((paths (if (listp ,path-or-paths) - ,path-or-paths - (list ,path-or-paths)))) - (unwind-protect - (let ((f--guard-paths paths)) - ,@body) - (setq f--guard-paths nil)))) - -(provide 'f) - -;;; f.el ends here blob - f288702d2fa16d3cdf0035b15a9fcbc552cd88e7 (mode 644) blob + /dev/null --- elpa/git-commit-3.3.0/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program 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 of the License, or - (at your option) any later version. - - This program 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 this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. blob - 2c6a07e83d90b6b872ec8486c654833228909801 (mode 644) blob + /dev/null --- elpa/git-commit-3.3.0/git-commit-autoloads.el +++ /dev/null @@ -1,35 +0,0 @@ -;;; git-commit-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from git-commit.el - -(put 'git-commit-major-mode 'safe-local-variable - (lambda (val) - (memq val '(text-mode - markdown-mode - org-mode - fundamental-mode - git-commit-elisp-text-mode)))) -(register-definition-prefixes "git-commit" '("git-commit-" "global-git-commit-mode")) - -;;; End of scraped data - -(provide 'git-commit-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; git-commit-autoloads.el ends here blob - 1779dab7471394c7be36ed5aaaf0b414b674ee78 (mode 644) blob + /dev/null --- elpa/git-commit-3.3.0/git-commit-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from git-commit.el -*- no-byte-compile: t -*- -(define-package "git-commit" "3.3.0" "Edit Git commit messages" '((emacs "25.1") (dash "2.19.1") (transient "0.3.6") (with-editor "3.0.5")) :authors '(("Jonas Bernoulli" . "jonas@bernoul.li") ("Sebastian Wiesner" . "lunaryorn@gmail.com") ("Florian Ragwitz" . "rafl@debian.org") ("Marius Vollmer" . "marius.vollmer@gmail.com")) :maintainer '("Jonas Bernoulli" . "jonas@bernoul.li") :keywords '("git" "tools" "vc") :url "https://github.com/magit/magit") blob - 64813af8255698d776046d561c3ed5ee98a5f739 (mode 644) blob + /dev/null --- elpa/git-commit-3.3.0/git-commit.el +++ /dev/null @@ -1,1077 +0,0 @@ -;;; git-commit.el --- Edit Git commit messages -*- lexical-binding: t; -*- - -;; Copyright (C) 2010-2021 The Magit Project Contributors -;; -;; You should have received a copy of the AUTHORS.md file which -;; lists all contributors. If not, see http://magit.vc/authors. - -;; Author: Jonas Bernoulli -;; Sebastian Wiesner -;; Florian Ragwitz -;; Marius Vollmer -;; Maintainer: Jonas Bernoulli - -;; Keywords: git tools vc -;; Homepage: https://github.com/magit/magit -;; Package-Requires: ((emacs "25.1") (dash "2.19.1") (transient "0.3.6") (with-editor "3.0.5")) -;; Package-Version: 3.3.0 -;; SPDX-License-Identifier: GPL-3.0-or-later - -;; 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 this file. If not, see . - -;;; Commentary: - -;; This package assists the user in writing good Git commit messages. - -;; While Git allows for the message to be provided on the command -;; line, it is preferable to tell Git to create the commit without -;; actually passing it a message. Git then invokes the `$GIT_EDITOR' -;; (or if that is undefined `$EDITOR') asking the user to provide the -;; message by editing the file ".git/COMMIT_EDITMSG" (or another file -;; in that directory, e.g. ".git/MERGE_MSG" for merge commits). - -;; When `global-git-commit-mode' is enabled, which it is by default, -;; then opening such a file causes the features described below, to -;; be enabled in that buffer. Normally this would be done using a -;; major-mode but to allow the use of any major-mode, as the user sees -;; fit, it is done here by running a setup function, which among other -;; things turns on the preferred major-mode, by default `text-mode'. - -;; Git waits for the `$EDITOR' to finish and then either creates the -;; commit using the contents of the file as commit message, or, if the -;; editor process exited with a non-zero exit status, aborts without -;; creating a commit. Unfortunately Emacsclient (which is what Emacs -;; users should be using as `$EDITOR' or at least as `$GIT_EDITOR') -;; does not differentiate between "successfully" editing a file and -;; aborting; not out of the box that is. - -;; By making use of the `with-editor' package this package provides -;; both ways of finish an editing session. In either case the file -;; is saved, but Emacseditor's exit code differs. -;; -;; C-c C-c Finish the editing session successfully by returning -;; with exit code 0. Git then creates the commit using -;; the message it finds in the file. -;; -;; C-c C-k Aborts the edit editing session by returning with exit -;; code 1. Git then aborts the commit. - -;; Aborting the commit does not cause the message to be lost, but -;; relying solely on the file not being tampered with is risky. This -;; package additionally stores all aborted messages for the duration -;; of the current session (i.e. until you close Emacs). To get back -;; an aborted message use M-p and M-n while editing a message. -;; -;; M-p Replace the buffer contents with the previous message -;; from the message ring. Of course only after storing -;; the current content there too. -;; -;; M-n Replace the buffer contents with the next message from -;; the message ring, after storing the current content. - -;; Some support for pseudo headers as used in some projects is -;; provided by these commands: -;; -;; C-c C-s Insert a Signed-off-by header. -;; C-c C-a Insert a Acked-by header. -;; C-c C-m Insert a Modified-by header. -;; C-c C-t Insert a Tested-by header. -;; C-c C-r Insert a Reviewed-by header. -;; C-c C-o Insert a Cc header. -;; C-c C-p Insert a Reported-by header. -;; C-c C-i Insert a Suggested-by header. - -;; When Git requests a commit message from the user, it does so by -;; having her edit a file which initially contains some comments, -;; instructing her what to do, and providing useful information, such -;; as which files were modified. These comments, even when left -;; intact by the user, do not become part of the commit message. This -;; package ensures these comments are propertizes as such and further -;; prettifies them by using different faces for various parts, such as -;; files. - -;; Finally this package highlights style errors, like lines that are -;; too long, or when the second line is not empty. It may even nag -;; you when you attempt to finish the commit without having fixed -;; these issues. The style checks and many other settings can easily -;; be configured: -;; -;; M-x customize-group RET git-commit RET - -;;; Code: -;;;; Dependencies - -(require 'dash) -(require 'subr-x) - -(require 'magit-git nil t) -(require 'magit-mode nil t) -(require 'magit-utils nil t) - -(require 'log-edit) -(require 'ring) -(require 'rx) -(require 'server) -(require 'transient) -(require 'with-editor) - -(defvar recentf-exclude) - -;;;; Declarations - -(defvar diff-default-read-only) -(defvar flyspell-generic-check-word-predicate) -(defvar font-lock-beg) -(defvar font-lock-end) - -(declare-function magit-completing-read "magit-utils" - (prompt collection &optional predicate require-match - initial-input hist def fallback)) -(declare-function magit-expand-git-file-name "magit-git" (filename)) -(declare-function magit-git-lines "magit-git" (&rest args)) -(declare-function magit-list-local-branch-names "magit-git" ()) -(declare-function magit-list-remote-branch-names "magit-git" - (&optional remote relative)) - -;;; Options -;;;; Variables - -(defgroup git-commit nil - "Edit Git commit messages." - :prefix "git-commit-" - :link '(info-link "(magit)Editing Commit Messages") - :group 'tools) - -(define-minor-mode global-git-commit-mode - "Edit Git commit messages. - -This global mode arranges for `git-commit-setup' to be called -when a Git commit message file is opened. That usually happens -when Git uses the Emacsclient as $GIT_EDITOR to have the user -provide such a commit message. - -Loading the library `git-commit' by default enables this mode, -but the library is not automatically loaded because doing that -would pull in many dependencies and increase startup time too -much. You can either rely on `magit' loading this library or -you can load it explicitly. Autoloading is not an alternative -because in this case autoloading would immediately trigger -full loading." - :group 'git-commit - :type 'boolean - :global t - :init-value t - :initialize (lambda (symbol exp) - (custom-initialize-default symbol exp) - (when global-git-commit-mode - (add-hook 'find-file-hook 'git-commit-setup-check-buffer))) - (if global-git-commit-mode - (add-hook 'find-file-hook 'git-commit-setup-check-buffer) - (remove-hook 'find-file-hook 'git-commit-setup-check-buffer))) - -(defcustom git-commit-major-mode 'text-mode - "Major mode used to edit Git commit messages. -The major mode configured here is turned on by the minor mode -`git-commit-mode'." - :group 'git-commit - :type '(choice (function-item text-mode) - (function-item markdown-mode) - (function-item org-mode) - (function-item fundamental-mode) - (function-item git-commit-elisp-text-mode) - (function :tag "Another mode") - (const :tag "No major mode"))) -;;;###autoload(put 'git-commit-major-mode 'safe-local-variable -;;;###autoload (lambda (val) -;;;###autoload (memq val '(text-mode -;;;###autoload markdown-mode -;;;###autoload org-mode -;;;###autoload fundamental-mode -;;;###autoload git-commit-elisp-text-mode)))) - -(defcustom git-commit-setup-hook - '(git-commit-save-message - git-commit-setup-changelog-support - git-commit-turn-on-auto-fill - git-commit-propertize-diff - bug-reference-mode - with-editor-usage-message) - "Hook run at the end of `git-commit-setup'." - :group 'git-commit - :type 'hook - :get (and (featurep 'magit-utils) 'magit-hook-custom-get) - :options '(git-commit-save-message - git-commit-setup-changelog-support - magit-generate-changelog - git-commit-turn-on-auto-fill - git-commit-turn-on-flyspell - git-commit-propertize-diff - bug-reference-mode - with-editor-usage-message)) - -(defcustom git-commit-post-finish-hook nil - "Hook run after the user finished writing a commit message. - -\\\ -This hook is only run after pressing \\[with-editor-finish] in a buffer used -to edit a commit message. If a commit is created without the -user typing a message into a buffer, then this hook is not run. - -This hook is not run until the new commit has been created. If -doing so takes Git longer than one second, then this hook isn't -run at all. For certain commands such as `magit-rebase-continue' -this hook is never run because doing so would lead to a race -condition. - -This hook is only run if `magit' is available. - -Also see `magit-post-commit-hook'." - :group 'git-commit - :type 'hook - :get (and (featurep 'magit-utils) 'magit-hook-custom-get)) - -(defcustom git-commit-finish-query-functions - '(git-commit-check-style-conventions) - "List of functions called to query before performing commit. - -The commit message buffer is current while the functions are -called. If any of them returns nil, then the commit is not -performed and the buffer is not killed. The user should then -fix the issue and try again. - -The functions are called with one argument. If it is non-nil, -then that indicates that the user used a prefix argument to -force finishing the session despite issues. Functions should -usually honor this wish and return non-nil." - :options '(git-commit-check-style-conventions) - :type 'hook - :group 'git-commit) - -(defcustom git-commit-style-convention-checks '(non-empty-second-line) - "List of checks performed by `git-commit-check-style-conventions'. -Valid members are `non-empty-second-line' and `overlong-summary-line'. -That function is a member of `git-commit-finish-query-functions'." - :options '(non-empty-second-line overlong-summary-line) - :type '(list :convert-widget custom-hook-convert-widget) - :group 'git-commit) - -(defcustom git-commit-summary-max-length 68 - "Column beyond which characters in the summary lines are highlighted. - -The highlighting indicates that the summary is getting too long -by some standards. It does in no way imply that going over the -limit a few characters or in some cases even many characters is -anything that deserves shaming. It's just a friendly reminder -that if you can make the summary shorter, then you might want -to consider doing so." - :group 'git-commit - :safe 'numberp - :type 'number) - -(defcustom git-commit-fill-column nil - "Override `fill-column' in commit message buffers. - -If this is non-nil, then it should be an integer. If that is the -case and the buffer-local value of `fill-column' is not already -set by the time `git-commit-turn-on-auto-fill' is called as a -member of `git-commit-setup-hook', then that function sets the -buffer-local value of `fill-column' to the value of this option. - -This option exists mostly for historic reasons. If you are not -already using it, then you probably shouldn't start doing so." - :group 'git-commit - :safe 'numberp - :type '(choice (const :tag "use regular fill-column") - number)) - -(make-obsolete-variable 'git-commit-fill-column 'fill-column - "Magit 2.11.0" 'set) - -(defcustom git-commit-known-pseudo-headers - '("Signed-off-by" "Acked-by" "Modified-by" "Cc" - "Suggested-by" "Reported-by" "Tested-by" "Reviewed-by" - "Co-authored-by") - "A list of Git pseudo headers to be highlighted." - :group 'git-commit - :safe (lambda (val) (and (listp val) (-all-p 'stringp val))) - :type '(repeat string)) - -(defcustom git-commit-use-local-message-ring nil - "Whether to use a local message ring instead of the global one. -This can be set globally, in which case every repository gets its -own commit message ring, or locally for a single repository. If -Magit isn't available, then setting this to a non-nil value has -no effect." - :group 'git-commit - :safe 'booleanp - :type 'boolean) - -;;;; Faces - -(defgroup git-commit-faces nil - "Faces used for highlighting Git commit messages." - :prefix "git-commit-" - :group 'git-commit - :group 'faces) - -(defface git-commit-summary - '((t :inherit font-lock-type-face)) - "Face used for the summary in commit messages." - :group 'git-commit-faces) - -(defface git-commit-overlong-summary - '((t :inherit font-lock-warning-face)) - "Face used for the tail of overlong commit message summaries." - :group 'git-commit-faces) - -(defface git-commit-nonempty-second-line - '((t :inherit font-lock-warning-face)) - "Face used for non-whitespace on the second line of commit messages." - :group 'git-commit-faces) - -(defface git-commit-keyword - '((t :inherit font-lock-string-face)) - "Face used for keywords in commit messages. -In this context a \"keyword\" is text surrounded by brackets." - :group 'git-commit-faces) - -(define-obsolete-face-alias 'git-commit-note - 'git-commit-keyword "Git-Commit 3.0.0") - -(defface git-commit-pseudo-header - '((t :inherit font-lock-string-face)) - "Face used for pseudo headers in commit messages." - :group 'git-commit-faces) - -(defface git-commit-known-pseudo-header - '((t :inherit font-lock-keyword-face)) - "Face used for the keywords of known pseudo headers in commit messages." - :group 'git-commit-faces) - -(defface git-commit-comment-branch-local - (if (featurep 'magit) - '((t :inherit magit-branch-local)) - '((t :inherit font-lock-variable-name-face))) - "Face used for names of local branches in commit message comments." - :group 'git-commit-faces) - -(define-obsolete-face-alias 'git-commit-comment-branch - 'git-commit-comment-branch-local "Git-Commit 2.12.0") - -(defface git-commit-comment-branch-remote - (if (featurep 'magit) - '((t :inherit magit-branch-remote)) - '((t :inherit font-lock-variable-name-face))) - "Face used for names of remote branches in commit message comments. -This is only used if Magit is available." - :group 'git-commit-faces) - -(defface git-commit-comment-detached - '((t :inherit git-commit-comment-branch-local)) - "Face used for detached `HEAD' in commit message comments." - :group 'git-commit-faces) - -(defface git-commit-comment-heading - '((t :inherit git-commit-known-pseudo-header)) - "Face used for headings in commit message comments." - :group 'git-commit-faces) - -(defface git-commit-comment-file - '((t :inherit git-commit-pseudo-header)) - "Face used for file names in commit message comments." - :group 'git-commit-faces) - -(defface git-commit-comment-action - '((t :inherit bold)) - "Face used for actions in commit message comments." - :group 'git-commit-faces) - -;;; Keymap - -(defvar git-commit-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "M-p") 'git-commit-prev-message) - (define-key map (kbd "M-n") 'git-commit-next-message) - (define-key map (kbd "C-c C-i") 'git-commit-insert-pseudo-header) - (define-key map (kbd "C-c C-a") 'git-commit-ack) - (define-key map (kbd "C-c M-i") 'git-commit-suggested) - (define-key map (kbd "C-c C-m") 'git-commit-modified) - (define-key map (kbd "C-c C-o") 'git-commit-cc) - (define-key map (kbd "C-c C-p") 'git-commit-reported) - (define-key map (kbd "C-c C-r") 'git-commit-review) - (define-key map (kbd "C-c C-s") 'git-commit-signoff) - (define-key map (kbd "C-c C-t") 'git-commit-test) - (define-key map (kbd "C-c M-s") 'git-commit-save-message) - map) - "Key map used by `git-commit-mode'.") - -;;; Menu - -(require 'easymenu) -(easy-menu-define git-commit-mode-menu git-commit-mode-map - "Git Commit Mode Menu" - '("Commit" - ["Previous" git-commit-prev-message t] - ["Next" git-commit-next-message t] - "-" - ["Ack" git-commit-ack :active t - :help "Insert an 'Acked-by' header"] - ["Sign-Off" git-commit-signoff :active t - :help "Insert a 'Signed-off-by' header"] - ["Modified-by" git-commit-modified :active t - :help "Insert a 'Modified-by' header"] - ["Tested-by" git-commit-test :active t - :help "Insert a 'Tested-by' header"] - ["Reviewed-by" git-commit-review :active t - :help "Insert a 'Reviewed-by' header"] - ["CC" git-commit-cc t - :help "Insert a 'Cc' header"] - ["Reported" git-commit-reported :active t - :help "Insert a 'Reported-by' header"] - ["Suggested" git-commit-suggested t - :help "Insert a 'Suggested-by' header"] - ["Co-authored-by" git-commit-co-authored t - :help "Insert a 'Co-authored-by' header"] - "-" - ["Save" git-commit-save-message t] - ["Cancel" with-editor-cancel t] - ["Commit" with-editor-finish t])) - -;;; Hooks - -(defconst git-commit-filename-regexp "/\\(\ -\\(\\(COMMIT\\|NOTES\\|PULLREQ\\|MERGEREQ\\|TAG\\)_EDIT\\|MERGE_\\|\\)MSG\ -\\|\\(BRANCH\\|EDIT\\)_DESCRIPTION\\)\\'") - -(with-eval-after-load 'recentf - (add-to-list 'recentf-exclude git-commit-filename-regexp)) - -(add-to-list 'with-editor-file-name-history-exclude git-commit-filename-regexp) - -(defun git-commit-setup-font-lock-in-buffer () - (and buffer-file-name - (string-match-p git-commit-filename-regexp buffer-file-name) - (git-commit-setup-font-lock))) - -(add-hook 'after-change-major-mode-hook 'git-commit-setup-font-lock-in-buffer) - -(defun git-commit-setup-check-buffer () - (and buffer-file-name - (string-match-p git-commit-filename-regexp buffer-file-name) - (git-commit-setup))) - -(defvar git-commit-mode) - -(defun git-commit-file-not-found () - ;; cygwin git will pass a cygwin path (/cygdrive/c/foo/.git/...), - ;; try to handle this in window-nt Emacs. - (--when-let - (and (or (string-match-p git-commit-filename-regexp buffer-file-name) - (and (boundp 'git-rebase-filename-regexp) - (string-match-p git-rebase-filename-regexp - buffer-file-name))) - (not (file-accessible-directory-p - (file-name-directory buffer-file-name))) - (if (require 'magit-git nil t) - ;; Emacs prepends a "c:". - (magit-expand-git-file-name (substring buffer-file-name 2)) - ;; Fallback if we can't load `magit-git'. - (and (string-match "\\`[a-z]:/\\(cygdrive/\\)?\\([a-z]\\)/\\(.*\\)" - buffer-file-name) - (concat (match-string 2 buffer-file-name) ":/" - (match-string 3 buffer-file-name))))) - (when (file-accessible-directory-p (file-name-directory it)) - (let ((inhibit-read-only t)) - (insert-file-contents it t) - t)))) - -(when (eq system-type 'windows-nt) - (add-hook 'find-file-not-found-functions #'git-commit-file-not-found)) - -(defconst git-commit-usage-message "\ -Type \\[with-editor-finish] to finish, \ -\\[with-editor-cancel] to cancel, and \ -\\[git-commit-prev-message] and \\[git-commit-next-message] \ -to recover older messages") - -(defun git-commit-setup () - (when (fboundp 'magit-toplevel) - ;; `magit-toplevel' is autoloaded and defined in magit-git.el, - ;; That library declares this functions without loading - ;; magit-process.el, which defines it. - (require 'magit-process nil t)) - (when git-commit-major-mode - (let ((auto-mode-alist (list (cons (concat "\\`" - (regexp-quote buffer-file-name) - "\\'") - git-commit-major-mode))) - ;; The major-mode hook might want to consult these minor - ;; modes, while the minor-mode hooks might want to consider - ;; the major mode. - (git-commit-mode t) - (with-editor-mode t)) - (normal-mode t))) - ;; Pretend that git-commit-mode is a major-mode, - ;; so that directory-local settings can be used. - (let ((default-directory - (or (and (not (file-exists-p ".dir-locals.el")) - ;; When $GIT_DIR/.dir-locals.el doesn't exist, - ;; fallback to $GIT_WORK_TREE/.dir-locals.el, - ;; because the maintainer can use the latter - ;; to enforce conventions, while s/he has no - ;; control over the former. - (fboundp 'magit-toplevel) ; silence byte-compiler - (magit-toplevel)) - default-directory))) - (let ((buffer-file-name nil) ; trick hack-dir-local-variables - (major-mode 'git-commit-mode)) ; trick dir-locals-collect-variables - (hack-dir-local-variables) - (hack-local-variables-apply))) - ;; Show our own message using our hook. - (setq with-editor-show-usage nil) - (setq with-editor-usage-message git-commit-usage-message) - (unless with-editor-mode - ;; Maybe already enabled when using `shell-command' or an Emacs shell. - (with-editor-mode 1)) - (add-hook 'with-editor-finish-query-functions - 'git-commit-finish-query-functions nil t) - (add-hook 'with-editor-pre-finish-hook - 'git-commit-save-message nil t) - (add-hook 'with-editor-pre-cancel-hook - 'git-commit-save-message nil t) - (when (and (fboundp 'magit-rev-parse) - (not (memq last-command - '(magit-sequencer-continue - magit-sequencer-skip - magit-am-continue - magit-am-skip - magit-rebase-continue - magit-rebase-skip)))) - (add-hook 'with-editor-post-finish-hook - (apply-partially 'git-commit-run-post-finish-hook - (magit-rev-parse "HEAD")) - nil t) - (when (fboundp 'magit-wip-maybe-add-commit-hook) - (magit-wip-maybe-add-commit-hook))) - (setq with-editor-cancel-message - 'git-commit-cancel-message) - (git-commit-mode 1) - (git-commit-setup-font-lock) - (git-commit-prepare-message-ring) - (when (boundp 'save-place) - (setq save-place nil)) - (save-excursion - (goto-char (point-min)) - (when (looking-at "\\`\\(\\'\\|\n[^\n]\\)") - (open-line 1))) - (with-demoted-errors "Error running git-commit-setup-hook: %S" - (run-hooks 'git-commit-setup-hook)) - (set-buffer-modified-p nil)) - -(defun git-commit-run-post-finish-hook (previous) - (when (and git-commit-post-finish-hook - (require 'magit nil t) - (fboundp 'magit-rev-parse)) - (cl-block nil - (let ((break (time-add (current-time) - (seconds-to-time 1)))) - (while (equal (magit-rev-parse "HEAD") previous) - (if (time-less-p (current-time) break) - (sit-for 0.01) - (message "No commit created after 1 second. Not running %s." - 'git-commit-post-finish-hook) - (cl-return)))) - (run-hooks 'git-commit-post-finish-hook)))) - -(define-minor-mode git-commit-mode - "Auxiliary minor mode used when editing Git commit messages. -This mode is only responsible for setting up some key bindings. -Don't use it directly, instead enable `global-git-commit-mode'." - :lighter "") - -(put 'git-commit-mode 'permanent-local t) - -(defun git-commit-setup-changelog-support () - "Treat ChangeLog entries as unindented paragraphs." - (when (fboundp 'log-indent-fill-entry) ; New in Emacs 27. - (setq-local fill-paragraph-function #'log-indent-fill-entry)) - (setq-local fill-indent-according-to-mode t) - (setq-local paragraph-start (concat paragraph-start "\\|\\*\\|("))) - -(defun git-commit-turn-on-auto-fill () - "Unconditionally turn on Auto Fill mode. -If `git-commit-fill-column' is non-nil, and `fill-column' -doesn't already have a buffer-local value, then set that -to `git-commit-fill-column'." - (when (and (numberp git-commit-fill-column) - (not (local-variable-p 'fill-column))) - (setq fill-column git-commit-fill-column)) - (setq-local comment-auto-fill-only-comments nil) - (turn-on-auto-fill)) - -(defun git-commit-turn-on-flyspell () - "Unconditionally turn on Flyspell mode. -Also prevent comments from being checked and -finally check current non-comment text." - (require 'flyspell) - (turn-on-flyspell) - (setq flyspell-generic-check-word-predicate - 'git-commit-flyspell-verify) - (let ((end) - (comment-start-regex (format "^\\(%s\\|$\\)" comment-start))) - (save-excursion - (goto-char (point-max)) - (while (and (not (bobp)) (looking-at comment-start-regex)) - (forward-line -1)) - (unless (looking-at comment-start-regex) - (forward-line)) - (setq end (point))) - (flyspell-region (point-min) end))) - -(defun git-commit-flyspell-verify () - (not (= (char-after (line-beginning-position)) - (aref comment-start 0)))) - -(defun git-commit-finish-query-functions (force) - (run-hook-with-args-until-failure - 'git-commit-finish-query-functions force)) - -(defun git-commit-check-style-conventions (force) - "Check for violations of certain basic style conventions. - -For each violation ask the user if she wants to proceed anyway. -Option `git-commit-style-convention-checks' controls which -conventions are checked." - (or force - (save-excursion - (goto-char (point-min)) - (re-search-forward (git-commit-summary-regexp) nil t) - (if (equal (match-string 1) "") - t ; Just try; we don't know whether --allow-empty-message was used. - (and (or (not (memq 'overlong-summary-line - git-commit-style-convention-checks)) - (equal (match-string 2) "") - (y-or-n-p "Summary line is too long. Commit anyway? ")) - (or (not (memq 'non-empty-second-line - git-commit-style-convention-checks)) - (not (match-string 3)) - (y-or-n-p "Second line is not empty. Commit anyway? "))))))) - -(defun git-commit-cancel-message () - (message - (concat "Commit canceled" - (and (memq 'git-commit-save-message with-editor-pre-cancel-hook) - ". Message saved to `log-edit-comment-ring'")))) - -;;; History - -(defun git-commit-prev-message (arg) - "Cycle backward through message history, after saving current message. -With a numeric prefix ARG, go back ARG comments." - (interactive "*p") - (let ((len (ring-length log-edit-comment-ring))) - (if (<= len 0) - (progn (message "Empty comment ring") (ding)) - ;; Unlike `log-edit-previous-comment' we save the current - ;; non-empty and newly written comment, because otherwise - ;; it would be irreversibly lost. - (when-let ((message (git-commit-buffer-message))) - (unless (ring-member log-edit-comment-ring message) - (ring-insert log-edit-comment-ring message) - (cl-incf arg) - (setq len (ring-length log-edit-comment-ring)))) - ;; Delete the message but not the instructions at the end. - (save-restriction - (goto-char (point-min)) - (narrow-to-region - (point) - (if (re-search-forward (concat "^" comment-start) nil t) - (max 1 (- (point) 2)) - (point-max))) - (delete-region (point-min) (point))) - (setq log-edit-comment-ring-index (log-edit-new-comment-index arg len)) - (message "Comment %d" (1+ log-edit-comment-ring-index)) - (insert (ring-ref log-edit-comment-ring log-edit-comment-ring-index))))) - -(defun git-commit-next-message (arg) - "Cycle forward through message history, after saving current message. -With a numeric prefix ARG, go forward ARG comments." - (interactive "*p") - (git-commit-prev-message (- arg))) - -(defun git-commit-save-message () - "Save current message to `log-edit-comment-ring'." - (interactive) - (when-let ((message (git-commit-buffer-message))) - (when-let ((index (ring-member log-edit-comment-ring message))) - (ring-remove log-edit-comment-ring index)) - (ring-insert log-edit-comment-ring message) - (when (and git-commit-use-local-message-ring - (fboundp 'magit-repository-local-set)) - (magit-repository-local-set 'log-edit-comment-ring - log-edit-comment-ring)))) - -(defun git-commit-prepare-message-ring () - (make-local-variable 'log-edit-comment-ring-index) - (when (and git-commit-use-local-message-ring - (fboundp 'magit-repository-local-get)) - (setq-local log-edit-comment-ring - (magit-repository-local-get - 'log-edit-comment-ring - (make-ring log-edit-maximum-comment-ring-size))))) - -(defun git-commit-buffer-message () - (let ((flush (concat "^" comment-start)) - (str (buffer-substring-no-properties (point-min) (point-max)))) - (with-temp-buffer - (insert str) - (goto-char (point-min)) - (when (re-search-forward (concat flush " -+ >8 -+$") nil t) - (delete-region (point-at-bol) (point-max))) - (goto-char (point-min)) - (flush-lines flush) - (goto-char (point-max)) - (unless (eq (char-before) ?\n) - (insert ?\n)) - (setq str (buffer-string))) - (unless (string-match "\\`[ \t\n\r]*\\'" str) - (when (string-match "\\`\n\\{2,\\}" str) - (setq str (replace-match "\n" t t str))) - (when (string-match "\n\\{2,\\}\\'" str) - (setq str (replace-match "\n" t t str))) - str))) - -;;; Headers - -(transient-define-prefix git-commit-insert-pseudo-header () - "Insert a commit message pseudo header." - [["Insert ... by yourself" - ("a" "Ack" git-commit-ack) - ("m" "Modified" git-commit-modified) - ("r" "Reviewed" git-commit-review) - ("s" "Signed-off" git-commit-signoff) - ("t" "Tested" git-commit-test)] - ["Insert ... by someone" - ("C-c" "Cc" git-commit-cc) - ("C-r" "Reported" git-commit-reported) - ("C-i" "Suggested" git-commit-suggested) - ("C-a" "Co-authored" git-commit-co-authored)]]) - -(defun git-commit-ack (name mail) - "Insert a header acknowledging that you have looked at the commit." - (interactive (git-commit-self-ident)) - (git-commit-insert-header "Acked-by" name mail)) - -(defun git-commit-modified (name mail) - "Insert a header to signal that you have modified the commit." - (interactive (git-commit-self-ident)) - (git-commit-insert-header "Modified-by" name mail)) - -(defun git-commit-review (name mail) - "Insert a header acknowledging that you have reviewed the commit." - (interactive (git-commit-self-ident)) - (git-commit-insert-header "Reviewed-by" name mail)) - -(defun git-commit-signoff (name mail) - "Insert a header to sign off the commit." - (interactive (git-commit-self-ident)) - (git-commit-insert-header "Signed-off-by" name mail)) - -(defun git-commit-test (name mail) - "Insert a header acknowledging that you have tested the commit." - (interactive (git-commit-self-ident)) - (git-commit-insert-header "Tested-by" name mail)) - -(defun git-commit-cc (name mail) - "Insert a header mentioning someone who might be interested." - (interactive (git-commit-read-ident "Cc")) - (git-commit-insert-header "Cc" name mail)) - -(defun git-commit-reported (name mail) - "Insert a header mentioning the person who reported the issue." - (interactive (git-commit-read-ident "Reported-by")) - (git-commit-insert-header "Reported-by" name mail)) - -(defun git-commit-suggested (name mail) - "Insert a header mentioning the person who suggested the change." - (interactive (git-commit-read-ident "Suggested-by")) - (git-commit-insert-header "Suggested-by" name mail)) - -(defun git-commit-co-authored (name mail) - "Insert a header mentioning the person who co-authored the commit." - (interactive (git-commit-read-ident "Co-authored-by")) - (git-commit-insert-header "Co-authored-by" name mail)) - -(defun git-commit-self-ident () - (list (or (getenv "GIT_AUTHOR_NAME") - (getenv "GIT_COMMITTER_NAME") - (ignore-errors (car (process-lines "git" "config" "user.name"))) - user-full-name - (read-string "Name: ")) - (or (getenv "GIT_AUTHOR_EMAIL") - (getenv "GIT_COMMITTER_EMAIL") - (getenv "EMAIL") - (ignore-errors (car (process-lines "git" "config" "user.email"))) - (read-string "Email: ")))) - -(defvar git-commit-read-ident-history nil) - -(defun git-commit-read-ident (prompt) - (if (require 'magit-git nil t) - (let ((str (magit-completing-read - prompt - (sort (delete-dups - (magit-git-lines "log" "-n9999" "--format=%aN <%ae>")) - 'string<) - nil nil nil 'git-commit-read-ident-history))) - (save-match-data - (if (string-match "\\`\\([^<]+\\) *<\\([^>]+\\)>\\'" str) - (list (save-match-data (string-trim (match-string 1 str))) - (string-trim (match-string 2 str))) - (user-error "Invalid input")))) - (list (read-string "Name: ") - (read-string "Email: ")))) - -(defun git-commit-insert-header (header name email) - (setq header (format "%s: %s <%s>" header name email)) - (save-excursion - (goto-char (point-max)) - (cond ((re-search-backward "^[-a-zA-Z]+: [^<]+? <[^>]+>" nil t) - (end-of-line) - (insert ?\n header) - (unless (= (char-after) ?\n) - (insert ?\n))) - (t - (while (re-search-backward (concat "^" comment-start) nil t)) - (unless (looking-back "\n\n" nil) - (insert ?\n)) - (insert header ?\n))) - (unless (or (eobp) (= (char-after) ?\n)) - (insert ?\n)))) - -;;; Font-Lock - -(defvar-local git-commit-need-summary-line t - "Whether the text should have a heading that is separated from the body. - -For commit messages that is a convention that should not -be violated. For notes it is up to the user. If you do -not want to insist on an empty second line here, then use -something like: - - (add-hook \\='git-commit-setup-hook - (lambda () - (when (equal (file-name-nondirectory (buffer-file-name)) - \"NOTES_EDITMSG\") - (setq git-commit-need-summary-line nil))))") - -(defun git-commit-summary-regexp () - (if git-commit-need-summary-line - (concat - ;; Leading empty lines and comments - (format "\\`\\(?:^\\(?:\\s-*\\|%s.*\\)\n\\)*" comment-start) - ;; Summary line - (format "\\(.\\{0,%d\\}\\)\\(.*\\)" git-commit-summary-max-length) - ;; Non-empty non-comment second line - (format "\\(?:\n%s\\|\n\\(.+\\)\\)?" comment-start)) - "\\(EASTER\\) \\(EGG\\)")) - -(defun git-commit-extend-region-summary-line () - "Identify the multiline summary-regexp construct. -Added to `font-lock-extend-region-functions'." - (save-excursion - (save-match-data - (goto-char (point-min)) - (when (looking-at (git-commit-summary-regexp)) - (let ((summary-beg (match-beginning 0)) - (summary-end (match-end 0))) - (when (or (< summary-beg font-lock-beg summary-end) - (< summary-beg font-lock-end summary-end)) - (setq font-lock-beg (min font-lock-beg summary-beg)) - (setq font-lock-end (max font-lock-end summary-end)))))))) - -(defvar-local git-commit--branch-name-regexp nil) - -(defconst git-commit-comment-headings - '("Changes to be committed:" - "Untracked files:" - "Changed but not updated:" - "Changes not staged for commit:" - "Unmerged paths:" - "Author:" - "Date:") - "Also fontified outside of comments in `git-commit-font-lock-keywords-2'.") - -(defconst git-commit-font-lock-keywords-1 - '(;; Pseudo headers - (eval . `(,(format "^\\(%s:\\)\\( .*\\)" - (regexp-opt git-commit-known-pseudo-headers)) - (1 'git-commit-known-pseudo-header) - (2 'git-commit-pseudo-header))) - ;; Summary - (eval . `(,(git-commit-summary-regexp) - (1 'git-commit-summary))) - ;; - Keyword [aka "text in brackets"] (overrides summary) - ("\\[.+?\\]" - (0 'git-commit-keyword t)) - ;; - Non-empty second line (overrides summary and note) - (eval . `(,(git-commit-summary-regexp) - (2 'git-commit-overlong-summary t t) - (3 'git-commit-nonempty-second-line t t))))) - -(defconst git-commit-font-lock-keywords-2 - `(,@git-commit-font-lock-keywords-1 - ;; Comments - (eval . `(,(format "^%s.*" comment-start) - (0 'font-lock-comment-face append))) - (eval . `(,(format "^%s On branch \\(.*\\)" comment-start) - (1 'git-commit-comment-branch-local t))) - (eval . `(,(format "^%s \\(HEAD\\) detached at" comment-start) - (1 'git-commit-comment-detached t))) - (eval . `(,(format "^%s %s" comment-start - (regexp-opt git-commit-comment-headings t)) - (1 'git-commit-comment-heading t))) - (eval . `(,(format "^%s\t\\(?:\\([^:\n]+\\):\\s-+\\)?\\(.*\\)" comment-start) - (1 'git-commit-comment-action t t) - (2 'git-commit-comment-file t))) - ;; "commit HASH" - (eval . `(,(rx bol "commit " (1+ alnum) eol) - (0 'git-commit-pseudo-header))) - ;; `git-commit-comment-headings' (but not in commented lines) - (eval . `(,(rx-to-string `(seq bol (or ,@git-commit-comment-headings) (1+ blank) (1+ nonl) eol)) - (0 'git-commit-pseudo-header))))) - -(defconst git-commit-font-lock-keywords-3 - `(,@git-commit-font-lock-keywords-2 - ;; More comments - (eval - ;; Your branch is ahead of 'master' by 3 commits. - ;; Your branch is behind 'master' by 2 commits, and can be fast-forwarded. - . `(,(format - "^%s Your branch is \\(?:ahead\\|behind\\) of '%s' by \\([0-9]*\\)" - comment-start git-commit--branch-name-regexp) - (1 'git-commit-comment-branch-local t) - (2 'git-commit-comment-branch-remote t) - (3 'bold t))) - (eval - ;; Your branch is up to date with 'master'. - ;; Your branch and 'master' have diverged, - . `(,(format - "^%s Your branch \\(?:is up[- ]to[- ]date with\\|and\\) '%s'" - comment-start git-commit--branch-name-regexp) - (1 'git-commit-comment-branch-local t) - (2 'git-commit-comment-branch-remote t))) - (eval - ;; and have 1 and 2 different commits each, respectively. - . `(,(format - "^%s and have \\([0-9]*\\) and \\([0-9]*\\) commits each" - comment-start) - (1 'bold t) - (2 'bold t))))) - -(defvar git-commit-font-lock-keywords git-commit-font-lock-keywords-3 - "Font-Lock keywords for Git-Commit mode.") - -(defun git-commit-setup-font-lock () - (let ((table (make-syntax-table (syntax-table)))) - (when comment-start - (modify-syntax-entry (string-to-char comment-start) "." table)) - (modify-syntax-entry ?# "." table) - (modify-syntax-entry ?\" "." table) - (modify-syntax-entry ?\' "." table) - (modify-syntax-entry ?` "." table) - (set-syntax-table table)) - (setq-local comment-start - (or (with-temp-buffer - (call-process "git" nil (current-buffer) nil - "config" "core.commentchar") - (unless (bobp) - (goto-char (point-min)) - (buffer-substring (point) (line-end-position)))) - "#")) - (setq-local comment-start-skip (format "^%s+[\s\t]*" comment-start)) - (setq-local comment-end-skip "\n") - (setq-local comment-use-syntax nil) - (setq-local git-commit--branch-name-regexp - (if (and (featurep 'magit-git) - ;; When using cygwin git, we may end up in a - ;; non-existing directory, which would cause - ;; any git calls to signal an error. - (file-accessible-directory-p default-directory)) - (progn - ;; Make sure the below functions are available. - (require 'magit) - ;; Font-Lock wants every submatch to succeed, so - ;; also match the empty string. Avoid listing - ;; remote branches and using `regexp-quote', - ;; because in repositories have thousands of - ;; branches that would be very slow. See #4353. - (format "\\(\\(?:%s\\)\\|\\)\\([^']+\\)" - (mapconcat #'identity - (magit-list-local-branch-names) - "\\|"))) - "\\([^']*\\)")) - (setq-local font-lock-multiline t) - (add-hook 'font-lock-extend-region-functions - #'git-commit-extend-region-summary-line - t t) - (font-lock-add-keywords nil git-commit-font-lock-keywords)) - -(defun git-commit-propertize-diff () - (require 'diff-mode) - (save-excursion - (goto-char (point-min)) - (when (re-search-forward "^diff --git" nil t) - (beginning-of-line) - (let ((buffer (current-buffer))) - (insert - (with-temp-buffer - (insert - (with-current-buffer buffer - (prog1 (buffer-substring-no-properties (point) (point-max)) - (delete-region (point) (point-max))))) - (let ((diff-default-read-only nil)) - (diff-mode)) - (let (font-lock-verbose font-lock-support-mode) - (if (fboundp 'font-lock-ensure) - (font-lock-ensure) - (with-no-warnings - (font-lock-fontify-buffer)))) - (let (next (pos (point-min))) - (while (setq next (next-single-property-change pos 'face)) - (put-text-property pos next 'font-lock-face - (get-text-property pos 'face)) - (setq pos next)) - (put-text-property pos (point-max) 'font-lock-face - (get-text-property pos 'face))) - (buffer-string))))))) - -;;; Elisp Text Mode - -(define-derived-mode git-commit-elisp-text-mode text-mode "ElText" - "Major mode for editing commit messages of elisp projects. -This is intended for use as `git-commit-major-mode' for projects -that expect `symbols' to look like this. I.e. like they look in -Elisp doc-strings, including this one. Unlike in doc-strings, -\"strings\" also look different than the other text." - (setq font-lock-defaults '(git-commit-elisp-text-mode-keywords))) - -(defvar git-commit-elisp-text-mode-keywords - `((,(concat "[`‘]\\(" lisp-mode-symbol-regexp "\\)['’]") - (1 font-lock-constant-face prepend)) - ("\"[^\"]*\"" (0 font-lock-string-face prepend)))) - -;;; _ -(provide 'git-commit) -;;; git-commit.el ends here blob - 6706a134b1fd441b2ec40444d3d802a04ce95138 (mode 644) blob + /dev/null --- elpa/git-commit-3.3.0.signed +++ /dev/null @@ -1 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) (trust undefined) created at 2021-10-04T23:05:04+0200 using RSA \ No newline at end of file blob - 898e8549067249061c4dcdff81de6b0b16ae7d4d (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/.github/workflows/emacs.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Emacs CI -on: [push, pull_request] -jobs: - - test: - name: Test - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Install emacs - run: sudo apt-get update && sudo apt-get install -y emacs - - - name: Check out code into the Go module directory - uses: actions/checkout@v1 - - - name: Test - run: cd test; emacs --batch -q -l ert -l ../go-mode.el $(for t in *-test.el; do echo -n "-l $t "; done) -f ert-run-tests-batch-and-exit blob - 66cb811a82f330f34368b725de85430aa3f31530 (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/.mailmap +++ /dev/null @@ -1 +0,0 @@ -Dominik Honnef blob - df4d9886afbce268e2a16db51fb0b80500c21c2b (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/AUTHORS +++ /dev/null @@ -1,50 +0,0 @@ -Aaron France -Alan Donovan -Alan Donovan -Andrew Gerrand -Austin Clements -Ben Fried -Bobby Powers -Charles Lee -Daniel Morsing -Dominik Honnef -Dominik Honnef -Eric Eisner -Erin Keenan -Evan Martin -Felix Lange -Florian Weimer -Istvan Marko -Iwasaki Yudai -James Aguilar -Jan Newmarch -Jean-Marc Eurin -Jeff Hodges -Juergen Hoetzel -Kevin Ballard -Konstantin Shaposhnikov -Lowe Thiderman -Mark Petrovic -Mats Lidell -Matt Armstrong -Peter Kleiweg -Philipp Stephani -Quan Yong Zhai -Robert Zaremba -Rui Ueyama -Russ Cox -Ryan Barrett -Rüdiger Sonderfeld -Sameer Ajmani -Scott Lawrence -Steven Elliot Harris -Syohei YOSHIDA -Taiki Sugawara -Viacheslav Chimishuk -Will -Yasuyuki Oka -Yutian Li -Zac Bergquist -kostya-sh -nverno -nwidger blob - ecdcbb85738ed5199b64d75771f6c1afa06440d0 (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/AUTHORS.old +++ /dev/null @@ -1,31 +0,0 @@ -# This file contains all the authors that contributed to go-mode while -# it was still part of the Go distribution. Most of these authors are -# not tracked in this repository's history. For a complete list of -# authors, see the AUTHORS file instead. - -Aaron France -Alan Donovan -Austin Clements -Ben Fried -Bobby Powers -Charles Lee -Dominik Honnef -Eric Eisner -Evan Martin -Florian Weimer -Istvan Marko -James Aguilar -Jan Newmarch -Jean-Marc Eurin -Kevin Ballard -Mats Lidell -Peter Kleiweg -Quan Yong Zhai -Robert Zaremba -Rui Ueyama -Russ Cox -Ryan Barrett -Sameer Ajmani -Scott Lawrence -Steven Elliot Harris -Yasuyuki Oka blob - 97e8c61b6ff66b18e578ad90f4cfb3d7f5568e51 (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014 The go-mode Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of the copyright holder nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. blob - 675655b6296548ee4eb4c3bb6c84038d42d153eb (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/NEWS +++ /dev/null @@ -1,134 +0,0 @@ -go-mode-1.4.0 (2016/05/12) - - * Fix minor bugs in fontification. - - * Use unwind-protect in gofmt, ensuring that temporary files will be - removed in all cases. - - * Improve go-remove-unused-imports: don't fail to remove unused - imports because of compilation errors in other files. - - * Add new commands for jumping to various parts of function - declarations: - - - go-goto-arguments (C-c C-f a) - - go-goto-docstring (C-c C-f d) - - go-goto-function (C-c C-f f) - - go-goto-function-name (C-c C-f n) - - go-goto-return-values (C-c C-f r) - - go-goto-method-receiver (C-c C-f m) - - Thanks to Lowe Thiderman for contributing these commands. - - * Add new customizable variable go-packages-function, which allows - choosing between different ways of finding installed packages. - Currently, go-packages-native (the default) and go-packages-go-list - are provided. - - * Automatically detect if goimports is used instead of gofmt and pass - the -srcdir flag, enabling support for vendoring. - - * Add new customizable variable gofmt-args, a list of strings that - will be passed to gofmt as additional arguments. Primarily this - allows using the -s flag with gofmt. - - * Add detection of GOPATH, Godep, wgo and gb. A new non-interactive - function go-guess-gopath will try a list of functions (the - customizable variable go-guess-gopath-functions) to detect a - suitable value for GOPATH. A new interactive command go-set-project - uses the result of go-guess-gopath to actually set GOPATH. This - interactive function could be used from inside a - projectile-switch-project-hook, directory variables or some other - way of invoking per-project code. - - * Add new command go-reset-gopath, which resets GOPATH to the value - it had when Emacs started. - - * Add customizable variable godoc-command, which allows choosing - between using godoc and go doc, and generally specifying - alternative paths to those tools. - - It defaults to go doc, which differs from prior versions which always - used godoc. - - * Add customizable variable godoc-use-completing-read, which allows - turning on or off the completion of import paths in the godoc - command. This is only really useful when using godoc instead of go - doc, and thus defaults to off. - - * Fix parsing of test output in compilation-mode for new versions of - Emacs. It's probably broken in older versions now. - - * Add support for electric-indent-mode and only reindent for closing - parens, not colons, commas or equal signs. - - * Avoid warnings by the byte compiler -- Use cl-lib instead of cl, - and utilize a macro to conditionally compile fallback code. - - * Use null-device instead of /dev/null to work better on Windows. - - * Add new customizable variable godoc-at-point-function, which allows - choosing between strategies for finding an identifier's - documentation. - - * Add new function godoc-and-godef, which is the default function - used for godoc-at-point and matches the previous behaviour. - - * Add new customizable variable godoc-and-godef-command, which allows - specifying the path to the godoc binary. - - * Add the function godoc-gogetdoc, which provides godoc-at-point - functionality by using the third party tool gogetdoc. - - * The godoc command no longer defaults to the symbol under point. It - rarely defaulted to a useful value. If you're interested in - documentation for the identifier at point, use godoc-at-point - instead. - - * Set compilation-error-screen-columns to nil in go-mode buffers. Go - uses tabs for indentation, and go/parser-based tools emit column - numbers in terms of characters, where a tab is one character wide. - Emacs defaults to interpreting columns as screen columns, where a - tab is tab-width columns wide. This breaks jumping to the right - columns from compilation-mode - - * Use HTTPS when talking to the Go Playground. Not only is it safer, - it is required nowadays. - -go-mode-1.3.1 (2015-07-03) - - * The 1.3.0 release forgot to update the version in the package - metadata. This version fixes that. - -go-mode-1.3.0 (2015-05-03) - - * Add a new minor mode godoc-mode, used instead of view-mode when - displaying godoc output. - -go-mode-1.2.1 (2015-04-10) - - * After using godef-jump, don't temporarily mark old buffer as - active. - -go-mode-1.2.0 (2015/03/22): - - * Add a menu for go-mode. - - * Add a new variable go-play-browsw-function that controls the - behaviour of go-play-region and go-play-buffer. - - * Fix minor bug in fontification. - - * Fix minor bug in go-remove-unused-imports that sometimes prevented - it from removing unused imports. - -go-mode-1.1.0 (2015-02-25): - - * Add a new variable godef-command, allowing customization of the - godef command. - - * Automatically hide the gofmt error window after fixing errors. - -go-mode-1.0.0 (2014-11-28): - - * First release since go-mode was removed from the Go distribution. blob - 854450cce8b65d69abfc7e05eae6e6b2b0c0cd04 (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/README.md +++ /dev/null @@ -1,178 +0,0 @@ -This is go-mode, the Emacs mode for editing Go code. - -It is a complete rewrite of the go-mode that shipped with Go 1.0.3 and -before, and was part of Go 1.1 until Go 1.3. Beginning with Go 1.4, -editor integration will not be part of the Go distribution anymore, -making this repository the canonical place for go-mode. - - -# Features - -In addition to normal features, such as fontification and indentation, -and close integration with familiar Emacs functionality (for example -syntax-based navigation like `beginning-of-defun`), go-mode comes with -the following extra features to provide an improved experience: - -- Integration with `gofmt` by providing a command of the same name, - and `gofmt-before-save`, which can be used in a hook to format Go - buffers before saving them. - - Setting the `gofmt-command` variable also allows using - `goimports`. - - Setting the `gofmt-args` variable with a list of arguments allows - using e.g. `gofmt -s`. -- Integration with `godoc` via the functions `godoc` and - `godoc-at-point`. -- Integration with the Playground - - `go-play-buffer` and `go-play-region` to send code to the - Playground - - `go-download-play` to download a Playground entry into a new - buffer -- Managing imports - - A function for jumping to the file's imports (`go-goto-imports` - - `C-c C-f i`) - - A function for adding imports, including tab completion - (`go-import-add`, bound to `C-c C-a`) - - A function for removing or commenting unused imports - (`go-remove-unused-imports`) - - It is recommended that you use `goimports` or the - `organize-imports` feature of `gopls` to manage - adding/removing/organizing imports automatically. -- Integration with godef - - `godef-describe` (`C-c C-d`) to describe expressions - - `godef-jump` (`C-c C-j`) and `godef-jump-other-window` (`C-x 4 C-c - C-j`) to jump to declarations - - This requires you to install godef via `go get - github.com/rogpeppe/godef`. -- Basic support for imenu (functions and variables) -- Built-in support for displaying code coverage as calculated by `go - test` (`go-coverage`) -- Several functions for jumping to and manipulating the individual - parts of function signatures. These functions support anonymous - functions, but are smart enough to skip them when required (e.g. - when jumping to a method receiver or docstring.) - - Jump to the argument list (`go-goto-arguments` - `C-c C-f a`) - - Jump to the docstring, create it if it does not exist yet - (`go-goto-docstring` - `C-c C-f d`). - - Jump to the function keyword (`go-goto-function` - `C-c C-f f`) - - Jump to the function name (`go-goto-function-name` - `C-c C-f n`) - - Jump to the return values (`go-goto-return-values` - `C-c C-f r`) - - Jump to the method receiver, adding a pair of parentheses if no - method receiver exists (`go-goto-method-receiver` - `C-c C-f m`). - - All of these functions accept a prefix argument (`C-u`), causing - them to skip anonymous functions. -- GOPATH detection – the function `go-guess-gopath` will guess a - suitable value for GOPATH, based on gb or wgo projects, Godeps and - src folders for plain GOPATH workspaces. The command - `go-set-project` uses the return value of `go-guess-gopath` to set - the GOPATH environment variable. - - You can either call `go-set-project` manually, or integrate it with - Projectile's project switching hooks, or any other means of - switching projects you may employ. - -# Installation - -## MELPA - -The recommended way of installing go-mode is via -[ELPA](http://www.emacswiki.org/emacs/ELPA), the Emacs package -manager, and the -[MELPA Stable repository](http://emacsredux.com/blog/2014/05/16/melpa-stable/), which provides -an up-to-date version of go-mode. - -If you're not familiar with ELPA yet, consider reading -[this guide](http://ergoemacs.org/emacs/emacs_package_system.html). - -## Manual - - - -To install go-mode manually, check out the `go-mode.el` repository in -a directory of your choice, add it to your load path and configure -Emacs to automatically load it when opening a `.go` file: - - (add-to-list 'load-path "/place/where/you/put/it/") - (autoload 'go-mode "go-mode" nil t) - (add-to-list 'auto-mode-alist '("\\.go\\'" . go-mode)) - -Either evaluate the statements with `C-x C-e`, or restart Emacs. - -# Other extensions - -There are several third party extensions that can enhance the Go -experience in Emacs. - -## Gopls integration - -[Gopls](https://github.com/golang/tools/blob/master/gopls/README.md) -is the official language server protocol (lsp) implementation provided -by the Go team. It is intended to replace the existing third party -tools for code formatting (gofmt), automatic imports (goimports), code -navigation (godef/guru), type and function descriptions (godoc/godef), -error checking, auto completion (gocode), variable and type renaming -(rename), and more. Once gopls is stable the older tools will no -longer be supported. - -Gopls is a supported backend for -[lsp-mode](https://github.com/emacs-lsp/lsp-mode). It will be used -automatically by lsp-mode if `gopls` is found in your PATH. You can -install gopls via: `go get golang.org/x/tools/gopls@latest`. To enable -lsp-mode for go buffers: - - (add-hook 'go-mode-hook 'lsp-deferred) - - -## Syntax/error checking - -There are two ways of using flymake with Go: - -1. [goflymake](https://github.com/dougm/goflymake), which internally -uses `go build` to capture all errors that a regular compilation would -also produce -2. [flymake-go](http://marmalade-repo.org/packages/flymake-go) for a -more lightweight solution that only uses `gofmt` and as such is only -able to catch syntax errors. Unlike goflymake, however, it does not -require an additional executable. - -Additionally, there is -[flycheck](https://github.com/flycheck/flycheck), a modern replacement -for flymake, which comes with built-in support for Go. In addition to -using `go build` or `gofmt`, it also has support for `go vet`, -`golint` and `errcheck`. - -## Auto completion - -For auto completion, take a look at -[gocode](https://github.com/nsf/gocode). - -## eldoc - -https://github.com/syohex/emacs-go-eldoc provides eldoc functionality -for go-mode. - -## Snippets - -I maintain a set of YASnippet snippets for go-mode at -https://github.com/dominikh/yasnippet-go - -## Integration with errcheck - -https://github.com/dominikh/go-errcheck.el provides integration with -[errcheck](https://github.com/kisielk/errcheck). - -# Stability - -go-mode.el has regular, tagged releases and is part of the MELPA -Stable repository. These tagged releases are intended to provide a -stable experience. APIs added in tagged releases will usually not be -removed or changed in future releases. - -Changes made on the master branch, which is tracked by the normal -MELPA repository, however, are under active development. New APIs are -experimental and may be changed or removed before the next release. -Furthermore, there is a higher chance for bugs. - -If you want a stable experience, use MELPA Stable. If you want cutting -edge features, or "beta-test" future releases, use MELPA or the master -branch. blob - 42bb6d54a910d7677438ae42346430f741ebb4dd (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/go-guru.el +++ /dev/null @@ -1,561 +0,0 @@ -;;; go-guru.el --- Integration of the Go 'guru' analysis tool into Emacs. - -;; Copyright 2016 The Go Authors. All rights reserved. -;; Use of this source code is governed by a BSD-style -;; license that can be found in the LICENSE file. - -;; Version: 0.1 -;; Package-Requires: ((go-mode "1.3.1") (cl-lib "0.5")) -;; Keywords: tools - -;;; Commentary: - -;; To enable the Go guru in Emacs, use this command to download, -;; build, and install the tool in $GOROOT/bin: -;; -;; $ go get golang.org/x/tools/cmd/guru -;; -;; Verify that the tool is on your $PATH: -;; -;; $ guru -help -;; Go source code guru. -;; Usage: guru [flags] -;; ... -;; -;; Then copy this file to a directory on your `load-path', -;; and add this to your ~/.emacs: -;; -;; (require 'go-guru) -;; -;; Inside a buffer of Go source code, select an expression of -;; interest, and type `C-c C-o d' (for "describe") or run one of the -;; other go-guru-xxx commands. If you use `menu-bar-mode', these -;; commands are available from the Guru menu. -;; -;; To enable identifier highlighting mode in a Go source buffer, use: -;; -;; (go-guru-hl-identifier-mode) -;; -;; To enable it automatically in all Go source buffers, -;; add this to your ~/.emacs: -;; -;; (add-hook 'go-mode-hook #'go-guru-hl-identifier-mode) -;; -;; See http://golang.org/s/using-guru for more information about guru. - -;;; Code: - -(require 'compile) -(require 'easymenu) -(require 'go-mode) -(require 'json) -(require 'simple) -(require 'cl-lib) - -(defgroup go-guru nil - "Options specific to the Go guru." - :group 'go) - -(defcustom go-guru-command "guru" - "The Go guru command." - :type 'string - :group 'go-guru) - -(defcustom go-guru-scope "" - "The scope of the analysis. See `go-guru-set-scope'." - :type 'string - :group 'go-guru) - -(defvar go-guru--scope-history - nil - "History of values supplied to `go-guru-set-scope'.") - -(defcustom go-guru-build-tags '() - "Build tags passed to guru." - :type '(repeat string) - :group 'go-guru) - -(defface go-guru-hl-identifier-face - '((t (:inherit highlight))) - "Face used for highlighting identifiers in `go-guru-hl-identifier'." - :group 'go-guru) - -(defcustom go-guru-debug nil - "Print debug messages when running guru." - :type 'boolean - :group 'go-guru) - -(defcustom go-guru-hl-identifier-idle-time 0.5 - "How long to wait after user input before highlighting the current identifier." - :type 'float - :group 'go-guru) - -(defvar go-guru--current-hl-identifier-idle-time - 0 - "The current delay for hl-identifier-mode.") - -(defvar go-guru--hl-identifier-timer - nil - "The global timer used for highlighting identifiers.") - -(defvar go-guru--last-enclosing - nil - "The remaining enclosing regions of the previous go-expand-region invocation.") - -;; Extend go-mode-map. -(let ((m (define-prefix-command 'go-guru-map))) - (define-key m "d" #'go-guru-describe) - (define-key m "f" #'go-guru-freevars) - (define-key m "i" #'go-guru-implements) - (define-key m "c" #'go-guru-peers) ; c for channel - (define-key m "r" #'go-guru-referrers) - (define-key m "j" #'go-guru-definition) ; j for jump - (define-key m "p" #'go-guru-pointsto) - (define-key m "s" #'go-guru-callstack) ; s for stack - (define-key m "e" #'go-guru-whicherrs) ; e for error - (define-key m "<" #'go-guru-callers) - (define-key m ">" #'go-guru-callees) - (define-key m "x" #'go-guru-expand-region)) ;; x for expand - -(define-key go-mode-map (kbd "C-c C-o") 'go-guru-map) - -(easy-menu-define go-guru-mode-menu go-mode-map - "Menu for Go Guru." - '("Guru" - ["Jump to Definition" go-guru-definition t] - ["Show Referrers" go-guru-referrers t] - ["Show Free Names" go-guru-freevars t] - ["Describe Expression" go-guru-describe t] - ["Show Implements" go-guru-implements t] - "---" - ["Show Callers" go-guru-callers t] - ["Show Callees" go-guru-callees t] - ["Show Callstack" go-guru-callstack t] - "---" - ["Show Points-To" go-guru-pointsto t] - ["Show Which Errors" go-guru-whicherrs t] - ["Show Channel Peers" go-guru-peers t] - "---" - ["Set pointer analysis scope..." go-guru-set-scope t])) - -(defun go-guru--read-scope () - "Read go-guru-scope from the minibuffer." - (completing-read-multiple "guru-scope (comma-separated): " - (go-packages) nil nil nil 'go-guru--scope-history)) - -(eval-when-compile (require 'subr-x)) - -;;;###autoload -(defun go-guru-set-scope () - "Set the scope for the Go guru, prompting the user to edit the previous scope. - -The scope restricts analysis to the specified packages. -Its value is a comma-separated list of patterns of these forms: - golang.org/x/tools/cmd/guru # a single package - golang.org/x/tools/... # all packages beneath dir - ... # the entire workspace. - -A pattern preceded by '-' is negative, so the scope - encoding/...,-encoding/xml -matches all encoding packages except encoding/xml." - (interactive) - (let ((scope (go-guru--read-scope))) - (setq go-guru-scope (string-join scope ",")))) - -(defun go-guru--set-scope-if-empty () - (if (string-equal "" go-guru-scope) - (go-guru-set-scope))) - -(defun go-guru--json (mode) - "Execute the Go guru in the specified MODE, passing it the -selected region of the current buffer, requesting JSON output. -Parse and return the resulting JSON object." - ;; A "what" query works even in a buffer without a file name. - (let* ((filename (file-truename (or buffer-file-name "synthetic.go"))) - (cmd (go-guru--command mode filename '("-json"))) - (buf (current-buffer)) - ;; Use temporary buffers to avoid conflict with go-guru--start. - (json-buffer (generate-new-buffer "*go-guru-json-output*")) - (input-buffer (generate-new-buffer "*go-guru-json-input*"))) - (unwind-protect - ;; Run guru, feeding it the input buffer (modified files). - (with-current-buffer input-buffer - (go-guru--insert-modified-files) - (unless (buffer-file-name buf) - (go-guru--insert-modified-file filename buf)) - (let ((exitcode (apply #'call-process-region - (append (list (point-min) - (point-max) - (car cmd) ; guru - nil ; delete - json-buffer ; output - nil) ; display - (cdr cmd))))) ; args - (with-current-buffer json-buffer - (unless (zerop exitcode) - ;; Failed: use buffer contents (sans final \n) as an error. - (error "%s" (buffer-substring (point-min) (1- (point-max))))) - ;; Success: parse JSON. - (goto-char (point-min)) - (json-read)))) - ;; Clean up temporary buffers. - (kill-buffer json-buffer) - (kill-buffer input-buffer)))) - -(define-compilation-mode go-guru-output-mode "Go guru" - "Go guru output mode is a variant of `compilation-mode' for the -output of the Go guru tool." - (set (make-local-variable 'compilation-error-screen-columns) nil) - (set (make-local-variable 'compilation-filter-hook) #'go-guru--compilation-filter-hook) - (set (make-local-variable 'compilation-start-hook) #'go-guru--compilation-start-hook)) - -(defun go-guru--compilation-filter-hook () - "Post-process a blob of input to the go-guru-output buffer." - ;; For readability, truncate each "file:line:col:" prefix to a fixed width. - ;; If the prefix is longer than 20, show "…/last/19chars.go". - ;; This usually includes the last segment of the package name. - ;; Hide the line and column numbers. - (let ((start compilation-filter-start) - (end (point))) - (goto-char start) - (unless (bolp) - ;; TODO(adonovan): not quite right: the filter may be called - ;; with chunks of output containing incomplete lines. Moving to - ;; beginning-of-line may cause duplicate post-processing. - (beginning-of-line)) - (setq start (point)) - (while (< start end) - (let ((p (search-forward ": " end t))) - (if (null p) - (setq start end) ; break out of loop - (setq p (1- p)) ; exclude final space - (let* ((posn (buffer-substring-no-properties start p)) - (flen (cl-search ":" posn)) ; length of filename - (filename (if (< flen 19) - (substring posn 0 flen) - (concat "…" (substring posn (- flen 19) flen))))) - (put-text-property start p 'display filename) - (forward-line 1) - (setq start (point)))))))) - -(defun go-guru--compilation-start-hook (proc) - "Erase default output header inserted by `compilation-mode'." - (with-current-buffer (process-buffer proc) - (let ((inhibit-read-only t)) - (goto-char (point-min)) - (delete-region (point) (point-max))))) - -(defun go-guru--start (mode) - "Start an asynchronous Go guru process for the specified query -MODE, passing it the selected region of the current buffer, and -feeding its standard input with the contents of all modified Go -buffers. Its output is handled by `go-guru-output-mode', a -variant of `compilation-mode'." - (or buffer-file-name - (error "Cannot use guru on a buffer without a file name")) - (let* ((filename (file-truename buffer-file-name)) - (cmd (mapconcat #'shell-quote-argument (go-guru--command mode filename) " ")) - (process-connection-type nil) ; use pipe (not pty) so EOF closes stdin - (procbuf (compilation-start cmd 'go-guru-output-mode))) - (with-current-buffer procbuf - (setq truncate-lines t)) ; the output is neater without line wrapping - (with-current-buffer (get-buffer-create "*go-guru-input*") - (erase-buffer) - (go-guru--insert-modified-files) - (process-send-region procbuf (point-min) (point-max)) - (process-send-eof procbuf)) - procbuf)) - -(defun go-guru--command (mode filename &optional flags) - "Return a command and argument list for a Go guru query of MODE, passing it -the selected region of the current buffer. FILENAME is the -effective name of the current buffer." - (let* ((posn (if (use-region-p) - (format "%s:#%d,#%d" - filename - (1- (position-bytes (region-beginning))) - (1- (position-bytes (region-end)))) - (format "%s:#%d" - filename - (1- (position-bytes (point)))))) - (cmd (append (list go-guru-command - "-modified" - "-scope" go-guru-scope - (format "-tags=%s" (mapconcat 'identity go-guru-build-tags " "))) - flags - (list mode - posn)))) - ;; Log the command to *Messages*, for debugging. - (when go-guru-debug - (message "go-guru--command: %s" cmd) - (message nil)) ; clear/shrink minibuffer - cmd)) - -(defun go-guru--insert-modified-files () - "Insert the contents of each modified Go buffer into the -current buffer in the format specified by guru's -modified flag." - (mapc #'(lambda (b) - (and (buffer-modified-p b) - (buffer-file-name b) - (string= (file-name-extension (buffer-file-name b)) "go") - (go-guru--insert-modified-file (buffer-file-name b) b))) - (buffer-list))) - -(defun go-guru--insert-modified-file (name buffer) - (insert (format "%s\n%d\n" name (go-guru--buffer-size-bytes buffer))) - (insert-buffer-substring buffer)) - -(defun go-guru--buffer-size-bytes (&optional buffer) - "Return the number of bytes in the current buffer. -If BUFFER, return the number of characters in that buffer instead." - (with-current-buffer (or buffer (current-buffer)) - (string-bytes (buffer-substring (point-min) - (point-max))))) - -(defun go-guru--goto-byte (offset) - "Go to the OFFSETth byte in the buffer." - (goto-char (byte-to-position offset))) - -(defun go-guru--goto-byte-column (offset) - "Go to the OFFSETth byte in the current line." - (goto-char (byte-to-position (+ (position-bytes (point-at-bol)) (1- offset))))) - -(defun go-guru--goto-pos (posn other-window) - "Find the file containing the position POSN (of the form `file:line:col') -set the point to it, switching the current buffer." - (let ((file-line-pos (split-string posn ":"))) - (funcall (if other-window #'find-file-other-window #'find-file) (car file-line-pos)) - (goto-char (point-min)) - (forward-line (1- (string-to-number (cadr file-line-pos)))) - (go-guru--goto-byte-column (string-to-number (cl-caddr file-line-pos))))) - -(defun go-guru--goto-pos-no-file (posn) - "Given `file:line:col', go to the line and column. The file -component will be ignored." - (let ((file-line-pos (split-string posn ":"))) - (goto-char (point-min)) - (forward-line (1- (string-to-number (cadr file-line-pos)))) - (go-guru--goto-byte-column (string-to-number (cl-caddr file-line-pos))))) - -;;;###autoload -(defun go-guru-callees () - "Show possible callees of the function call at the current point." - (interactive) - (go-guru--set-scope-if-empty) - (go-guru--start "callees")) - -;;;###autoload -(defun go-guru-callers () - "Show the set of callers of the function containing the current point." - (interactive) - (go-guru--set-scope-if-empty) - (go-guru--start "callers")) - -;;;###autoload -(defun go-guru-callstack () - "Show an arbitrary path from a root of the call graph to the -function containing the current point." - (interactive) - (go-guru--set-scope-if-empty) - (go-guru--start "callstack")) - -;;;###autoload -(defun go-guru-definition (&optional other-window) - "Jump to the definition of the selected identifier." - (interactive) - (or buffer-file-name - (error "Cannot use guru on a buffer without a file name")) - (let* ((res (go-guru--json "definition")) - (desc (cdr (assoc 'desc res)))) - (push-mark) - (if (eval-when-compile (fboundp 'xref-push-marker-stack)) - ;; TODO: Integrate this facility with XRef. - (xref-push-marker-stack) - (ring-insert find-tag-marker-ring (point-marker))) - (go-guru--goto-pos (cdr (assoc 'objpos res)) other-window) - (message "%s" desc))) - -;;;###autoload -(defun go-guru-definition-other-window () - "Jump to the defintion of the selected identifier in another window" - (interactive) - (go-guru-definition t)) - -;;;###autoload -(defun go-guru-describe () - "Describe the selected syntax, its kind, type and methods." - (interactive) - (go-guru--start "describe")) - -;;;###autoload -(defun go-guru-pointsto () - "Show what the selected expression points to." - (interactive) - (go-guru--set-scope-if-empty) - (go-guru--start "pointsto")) - -;;;###autoload -(defun go-guru-implements () - "Describe the 'implements' relation for types in the package -containing the current point." - (interactive) - (go-guru--start "implements")) - -;;;###autoload -(defun go-guru-freevars () - "Enumerate the free variables of the current selection." - (interactive) - (go-guru--start "freevars")) - -;;;###autoload -(defun go-guru-peers () - "Enumerate the set of possible corresponding sends/receives for -this channel receive/send operation." - (interactive) - (go-guru--set-scope-if-empty) - (go-guru--start "peers")) - -;;;###autoload -(defun go-guru-referrers () - "Enumerate all references to the object denoted by the selected -identifier." - (interactive) - (go-guru--start "referrers")) - -;;;###autoload -(defun go-guru-whicherrs () - "Show globals, constants and types to which the selected -expression (of type 'error') may refer." - (interactive) - (go-guru--set-scope-if-empty) - (go-guru--start "whicherrs")) - -(defun go-guru-what () - "Run a 'what' query and return the parsed JSON response as an -association list." - (go-guru--json "what")) - -(defun go-guru--hl-symbols (posn face id) - "Highlight the symbols at the positions POSN by creating -overlays with face FACE. The attribute 'go-guru-overlay on the -overlays will be set to ID." - (save-excursion - (mapc (lambda (pos) - (go-guru--goto-pos-no-file pos) - (let ((x (make-overlay (point) (+ (point) (length (current-word)))))) - (overlay-put x 'go-guru-overlay id) - (overlay-put x 'face face))) - posn))) - -;;;###autoload -(defun go-guru-unhighlight-identifiers () - "Remove highlights from previously highlighted identifier." - (remove-overlays nil nil 'go-guru-overlay 'sameid)) - -;;;###autoload -(defun go-guru-hl-identifier () - "Highlight all instances of the identifier under point. Removes -highlights from previously highlighted identifier." - (interactive) - (go-guru-unhighlight-identifiers) - (go-guru--hl-identifier)) - -;;;###autoload -(define-minor-mode go-guru-hl-identifier-mode - "Highlight instances of the identifier at point after a short -timeout." - :group 'go-guru - (if go-guru-hl-identifier-mode - (progn - (go-guru--hl-set-timer) - ;; Unhighlight if point moves off identifier - (add-hook 'post-command-hook #'go-guru--hl-identifiers-post-command-hook nil t) - ;; Unhighlight any time the buffer changes - (add-hook 'before-change-functions #'go-guru--hl-identifiers-before-change-function nil t)) - (remove-hook 'post-command-hook #'go-guru--hl-identifiers-post-command-hook t) - (remove-hook 'before-change-functions #'go-guru--hl-identifiers-before-change-function t) - (go-guru-unhighlight-identifiers))) - -(defun go-guru--hl-identifier () - "Highlight all instances of the identifier under point." - (let ((posn (cdr (assoc 'sameids (go-guru-what))))) - (go-guru--hl-symbols posn 'go-guru-hl-identifier-face 'sameid))) - -(defun go-guru--hl-identifiers-function () - "Function run after an idle timeout, highlighting the -identifier at point, if necessary." - (when go-guru-hl-identifier-mode - (unless (go-guru--on-overlay-p 'sameid) - ;; Ignore guru errors. Otherwise, we might end up with an error - ;; every time the timer runs, e.g. because of a malformed - ;; buffer. - (condition-case nil - (go-guru-hl-identifier) - (error nil))) - (unless (eq go-guru--current-hl-identifier-idle-time go-guru-hl-identifier-idle-time) - (go-guru--hl-set-timer)))) - -(defun go-guru--hl-set-timer () - (if go-guru--hl-identifier-timer - (cancel-timer go-guru--hl-identifier-timer)) - (setq go-guru--current-hl-identifier-idle-time go-guru-hl-identifier-idle-time) - (setq go-guru--hl-identifier-timer (run-with-idle-timer - go-guru-hl-identifier-idle-time - t - #'go-guru--hl-identifiers-function))) - -(defun go-guru--on-overlay-p (id) - "Return whether point is on a guru overlay of type ID." - (cl-find-if (lambda (el) (eq (overlay-get el 'go-guru-overlay) id)) (overlays-at (point)))) - -(defun go-guru--hl-identifiers-post-command-hook () - (if (and go-guru-hl-identifier-mode - (not (go-guru--on-overlay-p 'sameid))) - (go-guru-unhighlight-identifiers))) - -(defun go-guru--hl-identifiers-before-change-function (_beg _end) - (go-guru-unhighlight-identifiers)) - -;; TODO(dominikh): a future feature may be to cycle through all uses -;; of an identifier. - -(defun go-guru--enclosing () - "Return a list of enclosing regions." - (cdr (assoc 'enclosing (go-guru-what)))) - -(defun go-guru--enclosing-unique () - "Return a list of enclosing regions, with duplicates removed. -Two regions are considered equal if they have the same start and -end point." - (let ((enclosing (go-guru--enclosing))) - (cl-remove-duplicates enclosing - :from-end t - :test (lambda (a b) - (and (= (cdr (assoc 'start a)) - (cdr (assoc 'start b))) - (= (cdr (assoc 'end a)) - (cdr (assoc 'end b)))))))) - -(defun go-guru-expand-region () - "Expand region to the next enclosing syntactic unit." - (interactive) - (let* ((enclosing (if (eq last-command #'go-guru-expand-region) - go-guru--last-enclosing - (go-guru--enclosing-unique))) - (block (if (> (length enclosing) 0) (elt enclosing 0)))) - (when block - (go-guru--goto-byte (1+ (cdr (assoc 'start block)))) - (set-mark (byte-to-position (1+ (cdr (assoc 'end block))))) - (setq go-guru--last-enclosing (cl-subseq enclosing 1)) - (message "Region: %s" (cdr (assoc 'desc block))) - (setq deactivate-mark nil)))) - - -(provide 'go-guru) - -;; Local Variables: -;; indent-tabs-mode: t -;; tab-width: 8 -;; End: - -;;; go-guru.el ends here blob - 6905dc862a1ff308da9400f48441c0f5f2e00d9c (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/go-mode-autoloads.el +++ /dev/null @@ -1,198 +0,0 @@ -;;; go-mode-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) - - - -;;; Generated autoloads from go-guru.el - -(autoload 'go-guru-set-scope "go-guru" "\ -Set the scope for the Go guru, prompting the user to edit the previous scope. - -The scope restricts analysis to the specified packages. -Its value is a comma-separated list of patterns of these forms: - golang.org/x/tools/cmd/guru # a single package - golang.org/x/tools/... # all packages beneath dir - ... # the entire workspace. - -A pattern preceded by '-' is negative, so the scope - encoding/...,-encoding/xml -matches all encoding packages except encoding/xml." t) -(autoload 'go-guru-callees "go-guru" "\ -Show possible callees of the function call at the current point." t) -(autoload 'go-guru-callers "go-guru" "\ -Show the set of callers of the function containing the current point." t) -(autoload 'go-guru-callstack "go-guru" "\ -Show an arbitrary path from a root of the call graph to the -function containing the current point." t) -(autoload 'go-guru-definition "go-guru" "\ -Jump to the definition of the selected identifier. - -(fn &optional OTHER-WINDOW)" t) -(autoload 'go-guru-definition-other-window "go-guru" "\ -Jump to the defintion of the selected identifier in another window" t) -(autoload 'go-guru-describe "go-guru" "\ -Describe the selected syntax, its kind, type and methods." t) -(autoload 'go-guru-pointsto "go-guru" "\ -Show what the selected expression points to." t) -(autoload 'go-guru-implements "go-guru" "\ -Describe the 'implements' relation for types in the package -containing the current point." t) -(autoload 'go-guru-freevars "go-guru" "\ -Enumerate the free variables of the current selection." t) -(autoload 'go-guru-peers "go-guru" "\ -Enumerate the set of possible corresponding sends/receives for -this channel receive/send operation." t) -(autoload 'go-guru-referrers "go-guru" "\ -Enumerate all references to the object denoted by the selected -identifier." t) -(autoload 'go-guru-whicherrs "go-guru" "\ -Show globals, constants and types to which the selected -expression (of type 'error') may refer." t) -(autoload 'go-guru-unhighlight-identifiers "go-guru" "\ -Remove highlights from previously highlighted identifier.") -(autoload 'go-guru-hl-identifier "go-guru" "\ -Highlight all instances of the identifier under point. Removes -highlights from previously highlighted identifier." t) -(autoload 'go-guru-hl-identifier-mode "go-guru" "\ -Highlight instances of the identifier at point after a short - -timeout. - -This is a minor mode. If called interactively, toggle the -`Go-Guru-Hl-Identifier mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `go-guru-hl-identifier-mode'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(register-definition-prefixes "go-guru" '("go-guru-")) - - -;;; Generated autoloads from go-mode.el - -(autoload 'go-mode "go-mode" "\ -Major mode for editing Go source text. - -This mode provides (not just) basic editing capabilities for -working with Go code. It offers almost complete syntax -highlighting, indentation that is almost identical to gofmt and -proper parsing of the buffer content to allow features such as -navigation by function, manipulation of comments or detection of -strings. - -In addition to these core features, it offers various features to -help with writing Go code. You can directly run buffer content -through gofmt, read godoc documentation from within Emacs, modify -and clean up the list of package imports or interact with the -Playground (uploading and downloading pastes). - -The following extra functions are defined: - -- `gofmt' -- `godoc' and `godoc-at-point' -- `go-import-add' -- `go-remove-unused-imports' -- `go-goto-arguments' -- `go-goto-docstring' -- `go-goto-function' -- `go-goto-function-name' -- `go-goto-imports' -- `go-goto-return-values' -- `go-goto-method-receiver' -- `go-play-buffer' and `go-play-region' -- `go-download-play' -- `godef-describe' and `godef-jump' -- `go-coverage' -- `go-set-project' -- `go-reset-gopath' - -If you want to automatically run `gofmt' before saving a file, -add the following hook to your emacs configuration: - -(add-hook 'before-save-hook #'gofmt-before-save) - -If you want to use `godef-jump' instead of etags (or similar), -consider binding godef-jump to `M-.', which is the default key -for `find-tag': - -(add-hook 'go-mode-hook (lambda () - (local-set-key (kbd \"M-.\") #'godef-jump))) - -Please note that godef is an external dependency. You can install -it with - -go get github.com/rogpeppe/godef - - -If you're looking for even more integration with Go, namely -on-the-fly syntax checking, auto-completion and snippets, it is -recommended that you look at flycheck -(see URL `https://github.com/flycheck/flycheck') or flymake in combination -with goflymake (see URL `https://github.com/dougm/goflymake'), gocode -(see URL `https://github.com/nsf/gocode'), go-eldoc -(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go -(see URL `https://github.com/dominikh/yasnippet-go') - -(fn)" t) -(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) -(autoload 'gofmt-before-save "go-mode" "\ -Add this to .emacs to run gofmt on the current buffer when saving: -(add-hook 'before-save-hook 'gofmt-before-save). - -Note that this will cause ‘go-mode’ to get loaded the first time -you save any file, kind of defeating the point of autoloading." t) -(autoload 'godoc "go-mode" "\ -Show Go documentation for QUERY, much like \\\\[man]. - -(fn QUERY)" t) -(autoload 'go-download-play "go-mode" "\ -Download a paste from the playground and insert it in a Go buffer. -Tries to look for a URL at point. - -(fn URL)" t) -(autoload 'go-dot-mod-mode "go-mode" "\ -A major mode for editing go.mod files. - -(fn)" t) -(add-to-list 'auto-mode-alist '("go\\.mod\\'" . go-dot-mod-mode)) -(register-definition-prefixes "go-mode" '("go-" "god" "gofmt")) - - -;;; Generated autoloads from go-rename.el - -(autoload 'go-rename "go-rename" "\ -Rename the entity denoted by the identifier at point, using -the `gorename' tool. With FORCE, call `gorename' with the -`-force' flag. - -(fn NEW-NAME &optional FORCE)" t) -(register-definition-prefixes "go-rename" '("go-")) - -;;; End of scraped data - -(provide 'go-mode-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; go-mode-autoloads.el ends here blob - 55da8d81201a37cced577a0361fb9e82436f1671 (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/go-mode-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;; Generated package description from go-mode.el -*- no-byte-compile: t -*- -(define-package "go-mode" "1.6.0" "Major mode for the Go programming language" '((emacs "26.1")) :commit "3273fcece5d9ab7edd4f15b2d6bce61f4e5a0666" :authors '(("The go-mode Authors")) :maintainer '("The go-mode Authors") :keywords '("languages" "go") :url "https://github.com/dominikh/go-mode.el") blob - cdebdc3caea9e963455aeb23a36da93198ba75b9 (mode 644) blob + /dev/null --- elpa/go-mode-1.6.0/go-mode.el +++ /dev/null @@ -1,3047 +0,0 @@ -;;; go-mode.el --- Major mode for the Go programming language - -;;; Commentary: - -;; Copyright 2013 The go-mode Authors. All rights reserved. -;; Use of this source code is governed by a BSD-style -;; license that can be found in the LICENSE file. - -;; Author: The go-mode Authors -;; Version: 1.6.0 -;; Keywords: languages go -;; Package-Requires: ((emacs "26.1")) -;; URL: https://github.com/dominikh/go-mode.el -;; -;; This file is not part of GNU Emacs. - -;;; Code: - -(require 'cl-lib) -(require 'compile) -(require 'etags) -(require 'ffap) -(require 'find-file) -(require 'ring) -(require 'url) -(require 'xref) - - -(eval-when-compile - (defmacro go--forward-word (&optional arg) - (if (fboundp 'forward-word-strictly) - `(forward-word-strictly ,arg) - `(forward-word ,arg)))) - -(defun go--delete-whole-line (&optional arg) - "Delete the current line without putting it in the `kill-ring'. -Derived from function `kill-whole-line'. ARG is defined as for that -function." - (setq arg (or arg 1)) - (if (and (> arg 0) - (eobp) - (save-excursion (forward-visible-line 0) (eobp))) - (signal 'end-of-buffer nil)) - (if (and (< arg 0) - (bobp) - (save-excursion (end-of-visible-line) (bobp))) - (signal 'beginning-of-buffer nil)) - (cond ((zerop arg) - (delete-region (progn (forward-visible-line 0) (point)) - (progn (end-of-visible-line) (point)))) - ((< arg 0) - (delete-region (progn (end-of-visible-line) (point)) - (progn (forward-visible-line (1+ arg)) - (unless (bobp) - (backward-char)) - (point)))) - (t - (delete-region (progn (forward-visible-line 0) (point)) - (progn (forward-visible-line arg) (point)))))) - -(defun go-goto-opening-parenthesis (&optional _legacy-unused) - "Move up one level of parentheses. - -Return non-nil if there was a paren to move up to." - ;; The old implementation of go-goto-opening-parenthesis had an - ;; optional argument to speed up the function. It didn't change the - ;; function's outcome. - - ;; Silently fail if there's no matching opening parenthesis. - (let ((open-char (nth 1 (syntax-ppss)))) - (when open-char - (goto-char open-char)))) - - -(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]") -(defconst go--max-dangling-operator-length 2 - "The maximum length of dangling operators. -This must be at least the length of the longest string matched by -‘go-dangling-operators-regexp’ and must be updated whenever that -constant is changed.") - -(defconst go-identifier-regexp "[[:word:][:multibyte:]]+") -(defconst go-type-name-no-prefix-regexp "\\(?:[[:word:][:multibyte:]]+\\.\\)?[[:word:][:multibyte:]]+") -(defconst go-qualified-identifier-regexp (concat go-identifier-regexp "\\." go-identifier-regexp)) -(defconst go-label-regexp go-identifier-regexp) -(defconst go-type-regexp "[[:word:][:multibyte:]*]+") -(defconst go-func-regexp (concat "\\_\\s *\\(" go-identifier-regexp "\\)")) -(defconst go-func-meth-regexp (concat - "\\_\\s *\\(?:(\\s *" - "\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp - "\\s *)\\s *\\)?\\(" - go-identifier-regexp - "\\)(")) - -(defconst go--comment-start-regexp "[[:space:]]*\\(?:/[/*]\\)") -(defconst go--case-regexp "\\([[:space:]]*case\\([[:space:]]\\|$\\)\\)") -(defconst go--case-or-default-regexp (concat "\\(" go--case-regexp "\\|" "[[:space:]]*default:\\)")) - -(defconst go-builtins - '("append" "cap" "close" "complex" "copy" - "delete" "imag" "len" "make" "new" - "panic" "print" "println" "real" "recover") - "All built-in functions in the Go language. Used for font locking.") - -(defconst go-mode-keywords - '("break" "default" "func" "interface" "select" - "case" "defer" "go" "map" "struct" - "chan" "else" "goto" "package" "switch" - "const" "fallthrough" "if" "range" "type" - "continue" "for" "import" "return" "var") - "All keywords in the Go language. Used for font locking.") - -(defconst go-constants '("nil" "true" "false" "iota")) -(defconst go-type-name-regexp (concat "\\**\\(\\(?:" go-identifier-regexp "\\.\\)?" go-identifier-regexp "\\)")) - -(defvar go-dangling-cache) -(defvar go-godoc-history nil) -(defvar go--coverage-current-file-name) - -(defgroup go nil - "Major mode for editing Go code." - :link '(url-link "https://github.com/dominikh/go-mode.el") - :group 'languages) - -(defgroup go-cover nil - "Options specific to `cover`." - :group 'go) - -(defgroup godoc nil - "Options specific to `godoc'." - :group 'go) - -(defcustom go-fontify-function-calls t - "Fontify function and method calls if this is non-nil." - :type 'boolean - :group 'go) - -(defcustom go-fontify-variables t - "Fontify variable declarations if this is non-nil." - :type 'boolean - :group 'go) - -(defcustom go-mode-hook nil - "Hook called by `go-mode'." - :type 'hook - :group 'go) - -(defcustom go-command "go" - "The 'go' command. -Some users have multiple Go development trees and invoke the 'go' -tool via a wrapper that sets GOROOT and GOPATH based on the -current directory. Such users should customize this variable to -point to the wrapper script." - :type 'string - :group 'go) - -(defcustom gofmt-command "gofmt" - "The 'gofmt' command. -Some users may replace this with 'goimports' -from https://golang.org/x/tools/cmd/goimports." - :type 'string - :group 'go) - -(defcustom gofmt-args nil - "Additional arguments to pass to gofmt." - :type '(repeat string) - :group 'go) - -(defcustom gofmt-show-errors 'buffer - "Where to display gofmt error output. -It can either be displayed in its own buffer, in the echo area, or not at all. - -Please note that Emacs outputs to the echo area when writing -files and will overwrite gofmt's echo output if used from inside -a `before-save-hook'." - :type '(choice - (const :tag "Own buffer" buffer) - (const :tag "Echo area" echo) - (const :tag "None" nil)) - :group 'go) - -(defcustom godef-command "godef" - "The 'godef' command." - :type 'string - :group 'go) - -(defcustom go-other-file-alist - '(("_test\\.go\\'" (".go")) - ("\\.go\\'" ("_test.go"))) - "See the documentation of `ff-other-file-alist' for details." - :type '(repeat (list regexp (choice (repeat string) function))) - :group 'go) - -(defcustom go-packages-function 'go-packages-go-list - "Function called by `go-packages' to determine the list of available packages. -This is used in e.g. tab completion in `go-import-add'. - -This package provides two functions: `go-packages-go-list' uses -'go list all' to determine all Go packages. `go-packages-native' uses -elisp to find all .a files in all /pkg/ directories. -`go-packages-native' is obsolete as it doesn't behave correctly with -the Go build cache or Go modules." - :type 'function - :package-version '(go-mode . 1.4.0) - :group 'go) - -(defcustom go-guess-gopath-functions (list #'go-plain-gopath) - "Functions to call in sequence to detect a project's GOPATH. - -The functions in this list will be called one after another, -until a function returns non-nil. The order of the functions in -this list is important, as some project layouts may superficially -look like others." - :type '(repeat function) - :group 'go) - -(defcustom go-confirm-playground-uploads t - "Ask before uploading code to the public Go Playground. - -Set this to nil to upload without prompting. -" - :type 'boolean - :group 'go) - -(defcustom godoc-command "go doc" - "Which executable to use for `godoc'. -This can be either an absolute path or an executable in PATH." - :type 'string - :group 'go) - -(defcustom godoc-and-godef-command "go doc" - "Which executable to use for `godoc-and-godef'. -This can be either an absolute path or an executable in PATH." - :type 'string - :group 'go) - -(defcustom godoc-use-completing-read nil - "Provide auto-completion for godoc. -Only really desirable when using `godoc' instead of `go doc'." - :type 'boolean - :group 'godoc) - -(defcustom godoc-reuse-buffer nil - "Reuse a single *godoc* buffer to display godoc-at-point calls. -The default behavior is to open a separate buffer for each call." - :type 'boolean - :group 'godoc) - -(defcustom godoc-at-point-function #'godoc-and-godef - "Function to call to display the documentation for an -identifier at a given position. - -This package provides two functions: `godoc-and-godef' uses a -combination of godef and godoc to find the documentation. This -approach has several caveats. See its documentation for more -information. The second function, `godoc-gogetdoc' uses an -additional tool that correctly determines the documentation for -any identifier. It provides better results than -`godoc-and-godef'." - :type 'function - :group 'godoc) - -(defun godoc-and-godef (point) - "Use a combination of godef and godoc to guess the documentation at POINT. - -Due to a limitation in godoc, it is not possible to differentiate -between functions and methods, which may cause `godoc-at-point' -to display more documentation than desired. Furthermore, it -doesn't work on package names or variables. - -Consider using ‘godoc-gogetdoc’ instead for more accurate results." - (condition-case nil - (let* ((output (godef--call point)) - (file (car output)) - (name-parts (split-string (cadr output) " ")) - (first (car name-parts))) - (if (not (godef--successful-p file)) - (message "%s" (godef--error file)) - (go--godoc (format "%s %s" - (file-name-directory file) - (if (or (string= first "type") (string= first "const")) - (cadr name-parts) - (car name-parts))) - godoc-and-godef-command))) - (file-error (message "Could not run godef binary")))) - -(defun godoc-gogetdoc (point) - "Use the gogetdoc tool to find the documentation for an identifier at POINT. - -You can install gogetdoc with 'go get -u github.com/zmb3/gogetdoc'." - (if (not (buffer-file-name (go--coverage-origin-buffer))) - ;; TODO: gogetdoc supports unsaved files, but not introducing - ;; new artificial files, so this limitation will stay for now. - (error "Cannot use gogetdoc on a buffer without a file name")) - (let ((posn (format "%s:#%d" (file-truename buffer-file-name) (1- (position-bytes point)))) - (out (godoc--get-buffer ""))) - (with-temp-buffer - (go--insert-modified-files) - (call-process-region (point-min) (point-max) "gogetdoc" nil out nil - "-modified" - (format "-pos=%s" posn))) - (with-current-buffer out - (goto-char (point-min)) - (godoc-mode) - (display-buffer (current-buffer) t)))) - -(defun go--kill-new-message (url) - "Make URL the latest kill and print a message." - (kill-new url) - (message "%s" url)) - -(defcustom go-play-browse-function 'go--kill-new-message - "Function to call with the Playground URL. -See `go-play-region' for more details." - :type '(choice - (const :tag "Nothing" nil) - (const :tag "Kill + Message" go--kill-new-message) - (const :tag "Browse URL" browse-url) - (function :tag "Call function")) - :group 'go) - -(defcustom go-coverage-display-buffer-func 'display-buffer-reuse-window - "How `go-coverage' should display the coverage buffer. -See `display-buffer' for a list of possible functions." - :type 'function - :group 'go-cover) - -(defface go-coverage-untracked - '((t (:foreground "#505050"))) - "Coverage color of untracked code." - :group 'go-cover) - -(defface go-coverage-0 - '((t (:foreground "#c00000"))) - "Coverage color for uncovered code." - :group 'go-cover) -(defface go-coverage-1 - '((t (:foreground "#808080"))) - "Coverage color for covered code with weight 1." - :group 'go-cover) -(defface go-coverage-2 - '((t (:foreground "#748c83"))) - "Coverage color for covered code with weight 2." - :group 'go-cover) -(defface go-coverage-3 - '((t (:foreground "#689886"))) - "Coverage color for covered code with weight 3." - :group 'go-cover) -(defface go-coverage-4 - '((t (:foreground "#5ca489"))) - "Coverage color for covered code with weight 4." - :group 'go-cover) -(defface go-coverage-5 - '((t (:foreground "#50b08c"))) - "Coverage color for covered code with weight 5." - :group 'go-cover) -(defface go-coverage-6 - '((t (:foreground "#44bc8f"))) - "Coverage color for covered code with weight 6." - :group 'go-cover) -(defface go-coverage-7 - '((t (:foreground "#38c892"))) - "Coverage color for covered code with weight 7." - :group 'go-cover) -(defface go-coverage-8 - '((t (:foreground "#2cd495"))) - "Coverage color for covered code with weight 8. -For mode=set, all covered lines will have this weight." - :group 'go-cover) -(defface go-coverage-9 - '((t (:foreground "#20e098"))) - "Coverage color for covered code with weight 9." - :group 'go-cover) -(defface go-coverage-10 - '((t (:foreground "#14ec9b"))) - "Coverage color for covered code with weight 10." - :group 'go-cover) -(defface go-coverage-covered - '((t (:foreground "#2cd495"))) - "Coverage color of covered code." - :group 'go-cover) - -(defvar go-mode-syntax-table - (let ((st (make-syntax-table))) - (modify-syntax-entry ?+ "." st) - (modify-syntax-entry ?- "." st) - (modify-syntax-entry ?% "." st) - (modify-syntax-entry ?& "." st) - (modify-syntax-entry ?| "." st) - (modify-syntax-entry ?^ "." st) - (modify-syntax-entry ?! "." st) - (modify-syntax-entry ?= "." st) - (modify-syntax-entry ?< "." st) - (modify-syntax-entry ?> "." st) - (modify-syntax-entry ?/ ". 124b" st) - (modify-syntax-entry ?* ". 23" st) - (modify-syntax-entry ?\n "> b" st) - (modify-syntax-entry ?\" "\"" st) - (modify-syntax-entry ?\' "\"" st) - (modify-syntax-entry ?` "\"" st) - (modify-syntax-entry ?\\ "\\" st) - ;; TODO make _ a symbol constituent now that xemacs is gone - (modify-syntax-entry ?_ "w" st) - - st) - "Syntax table for Go mode.") - -(defun go--fontify-type-switch-case-pre () - "Move point to line following the end of case statement. - -This is used as an anchored font lock keyword PRE-MATCH-FORM. We -expand the font lock region to include multiline type switch case -statements." - (save-excursion - (beginning-of-line) - (while (or (looking-at "[[:space:]]*\\($\\|//\\)") (go--line-suffix-p ",")) - (forward-line)) - (when (go--line-suffix-p ":") - (forward-line)) - (point))) - -(defun go--build-font-lock-keywords () - ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24 - ;; doesn't understand that - (append - `( - ;; Match param lists in func signatures. This uses the - ;; MATCH-ANCHORED format (see `font-lock-keywords' docs). - ;; - ;; Parent/anchor match. It matches the param list opening "(". - (go--match-param-start - ;; Sub-matcher that matches individual params in the param list. - (go--fontify-param - ;; Pre-match form that runs before the first sub-match. - (go--fontify-param-pre) - ;; Post-match form that runs after last sub-match. - (go--fontify-param-post) - ;; Subexp 1 is the param variable name, if any. - (1 font-lock-variable-name-face nil t) - ;; Subexp 2 is the param type name, if any. We set the LAXMATCH - ;; flag to allow optional regex groups. - (2 font-lock-type-face nil t))) - - ;; Special case to match non-parenthesized function results. For - ;; example, "func(i int) string". - (go--match-single-func-result 1 font-lock-type-face) - - ;; Match name+type pairs, such as "foo bar" in "var foo bar". - (go--match-ident-type-pair 2 font-lock-type-face) - - ;; An anchored matcher for type switch case clauses. - (go--match-type-switch-case - (go--fontify-type-switch-case - (go--fontify-type-switch-case-pre) - nil - (1 font-lock-type-face))) - - ;; Match variable names in var decls, constant names in const - ;; decls, and type names in type decls. - (go--match-decl - (1 font-lock-variable-name-face nil t) - (2 font-lock-constant-face nil t) - (3 font-lock-type-face nil t)) - - (,(concat "\\_<" (regexp-opt go-mode-keywords t) "\\_>") . font-lock-keyword-face) - (,(concat "\\(\\_<" (regexp-opt go-builtins t) "\\_>\\)[[:space:]]*(") 1 font-lock-builtin-face) - (,(concat "\\_<" (regexp-opt go-constants t) "\\_>") . font-lock-constant-face) - - ;; Function (not method) name - (,go-func-regexp 1 font-lock-function-name-face)) - - (if go-fontify-function-calls - ;; Function call/method name - `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face) - ;; Bracketed function call - (,(concat "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) - ;; Method name - `((,go-func-meth-regexp 2 font-lock-function-name-face))) - - `( - ;; Raw string literal, needed for font-lock-syntactic-keywords - ("\\(`[^`]*`\\)" 1 font-lock-multiline) - - ;; RHS of type alias. - (go--match-type-alias 2 font-lock-type-face) - - ;; Arrays/slices: [] | [123] | [some.Const] | [someConst] | [...] - (,(concat "\\(?:^\\|[^[:word:][:multibyte:]]\\)\\[\\(?:[[:digit:]]+\\|" go-qualified-identifier-regexp "\\|" go-identifier-regexp "\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 1 font-lock-type-face) - - ;; Unary "!" - ("\\(!\\)[^=]" 1 font-lock-negation-char-face) - - ;; Composite literal type - (,(concat go-type-name-regexp "{") 1 font-lock-type-face) - - ;; Map value type - (go--match-map-value 1 font-lock-type-face) - - ;; Map key type - (,(concat "\\_\\[" go-type-name-regexp) 1 font-lock-type-face) - - ;; Channel type - (,(concat "\\_[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face) - - ;; "new()"/"make()" type - (,(concat "\\_<\\(?:new\\|make\\)\\_>\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) - - ;; Type assertion - (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) - - ;; Composite literal field names and label definitions. - (go--match-ident-colon 1 font-lock-constant-face) - - ;; Labels in goto/break/continue - (,(concat "\\_<\\(?:goto\\|break\\|continue\\)\\_>[[:space:]]*\\(" go-label-regexp "\\)") 1 font-lock-constant-face)))) - -(let ((m (define-prefix-command 'go-goto-map))) - (define-key m "a" #'go-goto-arguments) - (define-key m "d" #'go-goto-docstring) - (define-key m "f" #'go-goto-function) - (define-key m "i" #'go-goto-imports) - (define-key m "m" #'go-goto-method-receiver) - (define-key m "n" #'go-goto-function-name) - (define-key m "r" #'go-goto-return-values)) - -(defvar go-mode-map - (let ((m (make-sparse-keymap))) - (unless (boundp 'electric-indent-chars) - (define-key m "}" #'go-mode-insert-and-indent) - (define-key m ")" #'go-mode-insert-and-indent)) - (define-key m (kbd "C-c C-a") #'go-import-add) - (define-key m (kbd "C-c C-j") #'godef-jump) - (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window) - (define-key m (kbd "C-c C-d") #'godef-describe) - (define-key m (kbd "C-c C-f") 'go-goto-map) - m) - "Keymap used by ‘go-mode’.") - -(easy-menu-define go-mode-menu go-mode-map - "Menu for Go mode." - '("Go" - ["Describe Expression" godef-describe t] - ["Jump to Definition" godef-jump t] - "---" - ["Add Import" go-import-add t] - ["Remove Unused Imports" go-remove-unused-imports t] - ["Go to Imports" go-goto-imports t] - "---" - ("Playground" - ["Send Buffer" go-play-buffer t] - ["Send Region" go-play-region t] - ["Download" go-download-play t]) - "---" - ["Coverage" go-coverage t] - ["Gofmt" gofmt t] - ["Godoc" godoc t] - "---" - ["Customize Mode" (customize-group 'go) t])) - -(defun go-mode-insert-and-indent (key) - "Invoke the global binding of KEY, then reindent the line." - - (interactive (list (this-command-keys))) - (call-interactively (lookup-key (current-global-map) key)) - (indent-according-to-mode)) - -(defmacro go-paren-level () - `(car (syntax-ppss))) - -(defmacro go-in-string-or-comment-p () - `(nth 8 (syntax-ppss))) - -(defmacro go-in-string-p () - `(nth 3 (syntax-ppss))) - -(defmacro go-in-comment-p () - `(nth 4 (syntax-ppss))) - -(defmacro go-goto-beginning-of-string-or-comment () - `(goto-char (nth 8 (syntax-ppss)))) - -(defun go--backward-irrelevant (&optional stop-at-string) - "Skip backwards over any characters that are irrelevant for -indentation and related tasks. - -It skips over whitespace, comments, cases and labels and, if -STOP-AT-STRING is not true, over strings." - - (let (pos (start-pos (point))) - (skip-chars-backward "\n\s\t") - (if (and (save-excursion (beginning-of-line) (go-in-string-p)) - (= (char-before) ?`) - (not stop-at-string)) - (backward-char)) - (if (and (go-in-string-p) - (not stop-at-string)) - (go-goto-beginning-of-string-or-comment)) - (if (looking-back "\\*/" (line-beginning-position)) - (backward-char)) - (if (go-in-comment-p) - (go-goto-beginning-of-string-or-comment)) - (setq pos (point)) - (beginning-of-line) - (if (or (looking-at (concat "^" go-label-regexp ":")) - (looking-at "^[[:space:]]*\\(case .+\\|default\\):")) - (end-of-line 0) - (goto-char pos)) - (if (/= start-pos (point)) - (go--backward-irrelevant stop-at-string)) - (/= start-pos (point)))) - -(defun go--buffer-narrowed-p () - "Return non-nil if the current buffer is narrowed." - (/= (buffer-size) - (- (point-max) - (point-min)))) - -(defun go-previous-line-has-dangling-op-p () - "Return non-nil if the current line is a continuation line. -The return value is cached based on the current `line-beginning-position'." - (let* ((line-begin (line-beginning-position)) - (val (gethash line-begin go-dangling-cache 'nope))) - (when (or (go--buffer-narrowed-p) (equal val 'nope)) - (save-excursion - (go--forward-line -1) - (if (go--current-line-has-dangling-op-p) - (setq val (line-end-position)) - (setq val nil)) - - (if (not (go--buffer-narrowed-p)) - (puthash line-begin val go-dangling-cache)))) - val)) - -(defun go--current-line-has-dangling-op-p () - "Return non-nil if current line ends in a dangling operator. -The return value is not cached." - (or - (and - (go--line-suffix-p go-dangling-operators-regexp) - - ;; "=" does not behave like a dangling operator in decl statements. - (not (go--line-suffix-p "\\(?:var\\|type\\|const\\)[[:space:]].*=")) - - ;; Don't mistake "1234." for a dangling operator. - (not (go--line-suffix-p "[[:space:]]-?[[:digit:]][_0-9]*\\."))) - - ;; treat comma as dangling operator in certain cases - (and - (go--line-suffix-p ",") - (save-excursion (end-of-line) (go--commas-indent-p))))) - - -(defun go--commas-indent-p () - "Return non-nil if in a context where dangling commas indent next line." - (not (or - (go--open-paren-position) - (go--in-composite-literal-p) - (go--in-case-clause-list-p) - (go--in-struct-definition-p)))) - -(defun go--in-case-clause-list-p () - "Return non-nil if inside a multi-line case cause list. - -This function is only concerned with list items on lines after the -case keyword. It returns nil for the case line itself." - (save-excursion - (beginning-of-line) - (when (not (looking-at go--case-or-default-regexp)) - (let (saw-colon) - ;; optionally skip line with the colon - (when (go--line-suffix-p ":") - (setq saw-colon t) - (forward-line -1)) - - ;; go backwards while at a comment or a line ending in comma - (while (and - (or - (go--boring-line-p) - (go--line-suffix-p ",")) - (not (looking-at go--case-regexp)) - (go--forward-line -1))) - - (and - (looking-at-p go--case-regexp) - ;; we weren't in case list if first line ended in colon - ;; and the "case" line ended in colon - (not (and saw-colon (looking-at ".*:[[:space:]]*$")))))))) - -(defun go--in-composite-literal-p () - "Return non-nil if point is in a composite literal." - (save-excursion - (save-match-data - (and - (go-goto-opening-parenthesis) - - ;; Opening paren-like character is a curly. - (eq (char-after) ?{) - - (or - ;; Curly is preceded by non space (e.g. "Foo{"), definitely - ;; composite literal. - (zerop (skip-syntax-backward " ")) - - ;; Curly preceded by comma or semicolon. This is a composite - ;; literal with implicit type name. - (looking-back "[,:]" (1- (point))) - - ;; If we made it to the beginning of line we are either a naked - ;; block or a composite literal with implicit type name. If we - ;; are the latter, we must be contained in another composite - ;; literal. - (and (bolp) (go--in-composite-literal-p))))))) - -(defun go--in-paren-with-prefix-p (paren prefix) - (save-excursion - (and - (go-goto-opening-parenthesis) - (eq (char-after) paren) - (skip-syntax-backward " ") - (> (point) (length prefix)) - (string= prefix (buffer-substring (- (point) (length prefix)) (point)))))) - -(defun go--in-struct-definition-p () - "Return non-nil if point is inside a struct definition." - (go--in-paren-with-prefix-p ?{ "struct")) - -(defun go--in-interface-p () - "Return non-nil if point is inside an interface definition." - (go--in-paren-with-prefix-p ?{ "interface")) - - -(defun go--in-type-switch-p () - "Return non-nil if point is inside a type switch statement." - (go--in-paren-with-prefix-p ?{ ".(type)")) - -(defun go--fill-prefix () - "Return fill prefix for following comment paragraph." - (save-excursion - (beginning-of-line) - - ;; Skip over empty lines and empty comment openers/closers. - (while (and - (or (go--empty-line-p) (go--boring-comment-p)) - (zerop (forward-line 1)))) - - ;; If we are in a block comment, set prefix based on first line - ;; with content. - (if (go-in-comment-p) - (progn - (looking-at "[[:space:]]*") - (match-string-no-properties 0)) - - ;; Else if we are looking at the start of an interesting comment, our - ;; prefix is the comment opener and any space following. - (if (looking-at (concat go--comment-start-regexp "[[:space:]]*")) - ;; Replace "/*" opener with spaces so following lines don't - ;; get "/*" prefix. - (replace-regexp-in-string "/\\*" " " - (match-string-no-properties 0)))))) - -(defun go--open-paren-position () - "Return non-nil if point is between '(' and ')'. - -The return value is the position of the opening paren." - (save-excursion - (let ((start-paren-level (go-paren-level))) - (and - (go-goto-opening-parenthesis) - - ;; opening paren-like character is actually a paren - (eq (char-after) ?\() - - ;; point is before the closing paren - (< (go-paren-level)