From bb67a210f1c12ddf159f54c5b33ba98ca8aa3ddf Mon Sep 17 00:00:00 2001 From: Jens Schmidt Date: Wed, 25 Mar 2026 16:32:32 +0100 Subject: [PATCH 1/4] ; Rename shortdoc.el to shortdoc-doc.el --- lisp/emacs-lisp/{shortdoc.el => shortdoc-doc.el} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lisp/emacs-lisp/{shortdoc.el => shortdoc-doc.el} (100%) diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc-doc.el similarity index 100% rename from lisp/emacs-lisp/shortdoc.el rename to lisp/emacs-lisp/shortdoc-doc.el From 630af2f2e4f1f2a04c477a20545fa47e386e0b67 Mon Sep 17 00:00:00 2001 From: Jens Schmidt Date: Wed, 25 Mar 2026 16:33:41 +0100 Subject: [PATCH 2/4] ; Restore shortdoc.el --- lisp/emacs-lisp/shortdoc.el | 1960 +++++++++++++++++++++++++++++++++++ 1 file changed, 1960 insertions(+) create mode 100644 lisp/emacs-lisp/shortdoc.el diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el new file mode 100644 index 00000000000..ea6910c60fc --- /dev/null +++ b/lisp/emacs-lisp/shortdoc.el @@ -0,0 +1,1960 @@ +;;; shortdoc.el --- Short function summaries -*- lexical-binding: t -*- + +;; Copyright (C) 2020-2026 Free Software Foundation, Inc. + +;; Keywords: lisp, help +;; Package: emacs + +;; 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 package lists functions based on various groupings. +;; +;; For instance, `string-trim' and `mapconcat' are `string' functions, +;; so `M-x shortdoc RET string RET' will give an overview of functions +;; that operate on strings. +;; +;; The documentation groups are created with the +;; `define-short-documentation-group' macro. + +;;; Code: + +(require 'seq) +(require 'text-property-search) +(eval-when-compile (require 'cl-lib)) + +(defgroup shortdoc nil + "Short documentation." + :group 'lisp) + +(defface shortdoc-heading + '((t :inherit variable-pitch :height 1.3 :weight bold)) + "Face used for a heading." + :version "28.1") + +(defface shortdoc-section + '((t :inherit variable-pitch)) + "Face used for a section.") + +;;;###autoload +(defun shortdoc--check (group functions) + (let ((keywords '( :no-manual :args :eval :no-eval :no-value :no-eval* + :result :result-string :eg-result :eg-result-string :doc))) + (dolist (f functions) + (when (consp f) + (dolist (x f) + (when (and (keywordp x) (not (memq x keywords))) + (error "Shortdoc %s function `%s': bad keyword `%s'" + group (car f) x))))))) + +;;;###autoload +(progn + (defvar shortdoc--groups nil) + + (defmacro define-short-documentation-group (group &rest functions) + "Add GROUP to the list of defined documentation groups. +FUNCTIONS is a list of elements on the form: + + (FUNC + :no-manual BOOL + :args ARGS + :eval EVAL + :no-eval EXAMPLE-FORM + :no-value EXAMPLE-FORM + :no-eval* EXAMPLE-FORM + :result RESULT-FORM + :result-string RESULT-STRING + :eg-result RESULT-FORM + :eg-result-string RESULT-STRING) + +FUNC is the function being documented. + +NO-MANUAL should be non-nil if FUNC isn't documented in the +manual. + +ARGS is optional list of function FUNC's arguments. FUNC's +signature is displayed automatically if ARGS is not present. +Specifying ARGS might be useful where you don't want to document +some of the uncommon arguments a function might have. + +While the `:no-manual' and `:args' property can be used for +any (FUNC ..) form, all of the other properties shown above +cannot be used simultaneously in such a form. + +Here are some common forms with examples of properties that go +together: + +1. Document a form or string, and its evaluated return value. + (FUNC + :eval EVAL) + +If EVAL is a string, it will be inserted as is, and then that +string will be `read' and evaluated. + +2. Document a form or string, but manually document its evaluation + result. The provided form will not be evaluated. + + (FUNC + :no-eval EXAMPLE-FORM + :result RESULT-FORM) ;Use `:result-string' if value is in string form + +Using `:no-value' is the same as using `:no-eval'. + +Use `:no-eval*' instead of `:no-eval' where the successful +execution of the documented form depends on some conditions. + +3. Document a form or string EXAMPLE-FORM. Also manually + document an example result. This result could be unrelated to + the documented form. + + (FUNC + :no-eval EXAMPLE-FORM + :eg-result RESULT-FORM) ;Use `:eg-result-string' if value is in string form + +A FUNC form can have any number of `:no-eval' (or `:no-value'), +`:no-eval*', `:result', `:result-string', `:eg-result' and +`:eg-result-string' properties." + (declare (indent defun)) + (shortdoc--check group functions) + `(progn + (setq shortdoc--groups (delq (assq ',group shortdoc--groups) + shortdoc--groups)) + (push (cons ',group ',functions) shortdoc--groups)))) + +(define-short-documentation-group alist + "Alist Basics" + (assoc + :eval (assoc 'foo '((foo . bar) (zot . baz)))) + (rassoc + :eval (rassoc 'bar '((foo . bar) (zot . baz)))) + (assq + :eval (assq 'foo '((foo . bar) (zot . baz)))) + (rassq + :eval (rassq 'bar '((foo . bar) (zot . baz)))) + (assoc-string + :eval (assoc-string "foo" '(("foo" . "bar") ("zot" "baz")))) + "Manipulating Alists" + (assoc-delete-all + :eval (assoc-delete-all "b" (list '("a" . a) '("b" . b) '("b" . c)))) + (assq-delete-all + :eval (assq-delete-all 2 (list '(1 . a) '(2 . b) '(2 . c)))) + (rassq-delete-all + :eval (rassq-delete-all 'b (list '(1 . a) '(2 . b) '(2 . c)))) + (alist-get + :eval (let ((foo '((bar . baz)))) + (setf (alist-get 'bar foo) 'zot) + foo)) + "Misc" + (assoc-default + :eval (assoc-default "foobar" '(("foo" . baz)) #'string-match)) + (copy-alist + :eval (let* ((old '((foo . bar))) + (new (copy-alist old))) + (eq old new))) + ;; FIXME: Outputs "\.rose" for the symbol `.rose'. It would be + ;; better if that could be cleaned up. + (let-alist + :eval (let ((colors '((rose . red) + (lily . white)))) + (let-alist colors + (if (eq .rose 'red) + .lily))))) + +(define-short-documentation-group map + "Map Basics" + (mapp + :eval (mapp (list 'bar 1 'foo 2 'baz 3)) + :eval (mapp (list '(bar . 1) '(foo . 2) '(baz . 3))) + :eval (mapp [bar foo baz]) + :eval (mapp "this is a string") + :eval (mapp #s(hash-table data (bar 1 foo 2 baz 3))) + :eval (mapp '()) + :eval (mapp nil) + :eval (mapp (make-char-table 'shortdoc-test))) + (map-empty-p + :args (map) + :eval (map-empty-p nil) + :eval (map-empty-p []) + :eval (map-empty-p '())) + (map-elt + :args (map key) + :eval (map-elt (list 'bar 1 'foo 2 'baz 3) 'foo) + :eval (map-elt (list '(bar . 1) '(foo . 2) '(baz . 3)) 'foo) + :eval (map-elt [bar foo baz] 1) + :eval (map-elt #s(hash-table data (bar 1 foo 2 baz 3)) 'foo)) + (map-contains-key + :args (map key) + :eval (map-contains-key (list 'bar 1 'foo 2 'baz 3) 'foo) + :eval (map-contains-key (list '(bar . 1) '(foo . 2) '(baz . 3)) 'foo) + :eval (map-contains-key [bar foo baz] 1) + :eval (map-contains-key #s(hash-table data (bar 1 foo 2 baz 3)) 'foo)) + (map-put! + (map key value) + :eval +"(let ((map (list 'bar 1 'baz 3))) + (map-put! map 'foo 2) + map)" +;; This signals map-not-inplace when used in shortdoc.el :-( +;; :eval +;; "(let ((map (list '(bar . 1) '(baz . 3)))) +;; (map-put! map 'foo 2) +;; map)" + :eval +"(let ((map [bar bot baz])) + (map-put! map 1 'foo) + map)" + :eval +"(let ((map #s(hash-table data (bar 1 baz 3)))) + (map-put! map 'foo 2) + map)") + (map-insert + :args (map key value) + :eval (map-insert (list 'bar 1 'baz 3 'foo 7) 'foo 2) + :eval (map-insert (list '(bar . 1) '(baz . 3) '(foo . 7)) 'foo 2) + :eval (map-insert [bar bot baz] 1 'foo) + :eval (map-insert #s(hash-table data (bar 1 baz 3 foo 7)) 'foo 2)) + (map-delete + :args (map key) + :eval (map-delete (list 'bar 1 'foo 2 'baz 3) 'foo) + :eval (map-delete (list '(bar . 1) '(foo . 2) '(baz . 3)) 'foo) + :eval (map-delete [bar foo baz] 1) + :eval (map-delete #s(hash-table data (bar 1 foo 2 baz 3)) 'foo)) + (map-keys + :eval (map-keys (list 'bar 1 'foo 2 'baz 3)) + :eval (map-keys (list '(bar . 1) '(foo . 2) '(baz . 3))) + :eval (map-keys [bar foo baz]) + :eval (map-keys #s(hash-table data (bar 1 foo 2 baz 3)))) + (map-values + :args (map) + :eval (map-values (list 'bar 1 'foo 2 'baz 3)) + :eval (map-values (list '(bar . 1) '(foo . 2) '(baz . 3))) + :eval (map-values [bar foo baz]) + :eval (map-values #s(hash-table data (bar 1 foo 2 baz 3)))) + (map-pairs + :eval (map-pairs (list 'bar 1 'foo 2 'baz 3)) + :eval (map-pairs (list '(bar . 1) '(foo . 2) '(baz . 3))) + :eval (map-pairs [bar foo baz]) + :eval (map-pairs #s(hash-table data (bar 1 foo 2 baz 3)))) + (map-length + :args (map) + :eval (map-length (list 'bar 1 'foo 2 'baz 3)) + :eval (map-length (list '(bar . 1) '(foo . 2) '(baz . 3))) + :eval (map-length [bar foo baz]) + :eval (map-length #s(hash-table data (bar 1 foo 2 baz 3)))) + (map-copy + :args (map) + :eval (map-copy (list 'bar 1 'foo 2 'baz 3)) + :eval (map-copy (list '(bar . 1) '(foo . 2) '(baz . 3))) + :eval (map-copy [bar foo baz]) + :eval (map-copy #s(hash-table data (bar 1 foo 2 baz 3)))) + "Doing things to maps and their contents" + (map-apply + :args (function map) + :eval (map-apply #'+ (list '(1 . 2) '(3 . 4)))) + (map-do + :args (function map) + :eval +"(let ((map (list '(1 . 1) '(2 . 3))) + acc) + (map-do (lambda (k v) (push (+ k v) acc)) map) + (nreverse acc))") + (map-keys-apply + :eval (map-keys-apply #'1+ (list '(1 . 2) '(3 . 4)))) + (map-values-apply + :args (function map) + :eval (map-values-apply #'1+ (list '(1 . 2) '(3 . 4)))) + (map-filter + :eval (map-filter (lambda (k _) (oddp k)) (list '(1 . 2) '(4 . 6))) + :eval (map-filter (lambda (k v) (evenp (+ k v))) (list '(1 . 2) '(4 . 6)))) + (map-remove + :eval (map-remove (lambda (k _) (oddp k)) (list '(1 . 2) '(4 . 6))) + :eval (map-remove (lambda (k v) (evenp (+ k v))) (list '(1 . 2) '(4 . 6)))) + (map-some + :eval (map-some (lambda (k _) (oddp k)) (list '(1 . 2) '(4 . 6))) + :eval (map-some (lambda (k v) (evenp (+ k v))) (list '(1 . 2) '(4 . 6)))) + (map-every-p + :eval (map-every-p (lambda (k _) (oddp k)) (list '(1 . 2) '(4 . 6))) + :eval (map-every-p (lambda (k v) (evenp (+ k v))) (list '(1 . 3) '(4 . 6)))) + "Combining and changing maps" + (map-merge + :eval (map-merge 'alist '(1 2 3 4) #s(hash-table data (5 6 7 8))) + :eval (map-merge 'list '(1 2 3 4) #s(hash-table data (5 6 7 8))) + :eval (map-merge 'plist '(1 2 3 4) #s(hash-table data (5 6 7 8))) + :eval (map-merge 'hash-table '(1 2 3 4) #s(hash-table data (5 6 7 8)))) + (map-merge-with + :eval (map-merge-with 'alist #'max '(1 2 3 4) #s(hash-table data (1 1 3 5))) + :eval (map-merge-with 'alist #'min '(1 2 3 4) #s(hash-table data (1 1 3 5))) + :eval (map-merge-with 'hash-table #'min '(1 2 3 4) #s(hash-table data (1 1 3 5)))) + (map-into + :args (map type) + :eval (map-into #s(hash-table data '(5 6 7 8)) 'list) + :eval (map-into '((5 . 6) (7 . 8)) 'plist) + :eval (map-into '((5 . 6) (7 . 8)) 'hash-table))) + +(define-short-documentation-group string + "Making Strings" + (make-string + :args (length init) + :eval "(make-string 5 ?x)") + (string + :eval "(string ?a ?b ?c)") + (concat + :eval (concat "foo" "bar" "zot")) + (string-join + :no-manual t + :eval (string-join '("foo" "bar" "zot") " ")) + (mapconcat + :eval (mapconcat (lambda (a) (concat "[" a "]")) + '("foo" "bar" "zot") " ")) + (string-pad + :eval (string-pad "foo" 5) + :eval (string-pad "foobar" 5) + :eval (string-pad "foo" 5 ?- t)) + (mapcar + :eval (mapcar #'identity "123")) + (format + :eval (format "This number is %d" 4)) + "Manipulating Strings" + (substring + :eval (substring "abcde" 1 3) + :eval (substring "abcde" 2) + :eval (substring "abcde" 1 -1) + :eval (substring "abcde" -4 4)) + (string-limit + :eval (string-limit "foobar" 3) + :eval (string-limit "foobar" 3 t) + :eval (string-limit "foobar" 10) + :eval (string-limit "foε₯½" 3 nil 'utf-8)) + (truncate-string-to-width + :eval (truncate-string-to-width "foobar" 3) + :eval (truncate-string-to-width "δ½ ε₯½bar" 5)) + (split-string + :eval (split-string "foo bar") + :eval (split-string "|foo|bar|" "|") + :eval (split-string "|foo|bar|" "|" t)) + (split-string-and-unquote + :eval (split-string-and-unquote "foo \"bar zot\"")) + (split-string-shell-command + :eval (split-string-shell-command "ls /tmp/'foo bar'")) + (string-lines + :eval (string-lines "foo\n\nbar") + :eval (string-lines "foo\n\nbar" t)) + (string-replace + :eval (string-replace "foo" "bar" "foozot")) + (replace-regexp-in-string + :eval (replace-regexp-in-string "[a-z]+" "_" "*foo*")) + (string-trim + :args (string) + :doc "Trim STRING of leading and trailing white space." + :eval (string-trim " foo ")) + (string-trim-left + :eval (string-trim-left "oofoo" "o+")) + (string-trim-right + :eval (string-trim-right "barkss" "s+")) + (string-truncate-left + :no-manual t + :eval (string-truncate-left "longstring" 8)) + (string-remove-suffix + :no-manual t + :eval (string-remove-suffix "bar" "foobar")) + (string-remove-prefix + :no-manual t + :eval (string-remove-prefix "foo" "foobar")) + (string-chop-newline + :eval (string-chop-newline "foo\n")) + (string-clean-whitespace + :eval (string-clean-whitespace " foo bar ")) + (string-fill + :eval (string-fill "Three short words" 12) + :eval (string-fill "Long-word" 3)) + (reverse + :eval (reverse "foo")) + (substring-no-properties + :eval (substring-no-properties (propertize "foobar" 'face 'bold) 0 3)) + (try-completion + :eval (try-completion "foo" '("foobar" "foozot" "gazonk"))) + "Unicode Strings" + (string-glyph-split + :eval (string-glyph-split "Hello, πŸ‘ΌπŸ»πŸ§‘πŸΌβ€πŸ€β€πŸ§‘πŸ»")) + (string-glyph-compose + :eval (string-glyph-compose "Å")) + (string-glyph-decompose + :eval (string-glyph-decompose "β„«")) + "Predicates for Strings" + (string-equal + :eval (string-equal "abc" "abc") + :eval (string-equal "abc" "ABC")) + (string-equal-ignore-case + :eval (string-equal-ignore-case "foo" "FOO")) + (equal + :eval (equal "foo" "foo")) + (cl-equalp + :eval (cl-equalp "Foo" "foo")) + (stringp + :eval (stringp "a") + :eval (stringp 'a) + :eval "(stringp ?a)") + (string-or-null-p + :eval (string-or-null-p "a") + :eval (string-or-null-p nil)) + (char-or-string-p + :eval "(char-or-string-p ?a)" + :eval (char-or-string-p "a")) + (string-empty-p + :no-manual t + :eval (string-empty-p "")) + (string-blank-p + :no-manual t + :eval (string-blank-p " \n")) + (string-lessp + :eval (string-lessp "abc" "def") + :eval (string-lessp "pic4.png" "pic32.png") + :eval (string-lessp "1.1" "1.2")) + (string-greaterp + :eval (string-greaterp "foo" "bar")) + (string-version-lessp + :eval (string-version-lessp "pic4.png" "pic32.png") + :eval (string-version-lessp "1.9.3" "1.10.2")) + (string-collate-lessp + :eval (string-collate-lessp "abc" "abd")) + (string-prefix-p + :eval (string-prefix-p "foo" "foobar")) + (string-suffix-p + :eval (string-suffix-p "bar" "foobar")) + "Case Manipulation" + (upcase + :eval (upcase "foo")) + (downcase + :eval (downcase "FOObar")) + (capitalize + :eval (capitalize "foo bar zot")) + (upcase-initials + :eval (upcase-initials "The CAT in the hAt")) + "Converting Strings" + (string-to-number + :eval (string-to-number "42") + :eval (string-to-number "deadbeef" 16) + :eval (string-to-number "2.5e+03")) + (number-to-string + :eval (number-to-string 42)) + (char-uppercase-p + :eval "(char-uppercase-p ?A)" + :eval "(char-uppercase-p ?a)") + "Data About Strings" + (length + :eval (length "foo") + :eval (length "avocado: πŸ₯‘")) + (string-width + :eval (string-width "foo") + :eval (string-width "avocado: πŸ₯‘")) + (string-pixel-width + :eval (string-pixel-width "foo") + :eval (string-pixel-width "avocado: πŸ₯‘")) + (string-search + :eval (string-search "bar" "foobarzot")) + (assoc-string + :eval (assoc-string "foo" '(("a" 1) (foo 2)))) + (seq-position + :eval "(seq-position \"foobarzot\" ?z)")) + +(define-short-documentation-group file-name + "File Name Manipulation" + (file-name-directory + :eval (file-name-directory "/tmp/foo") + :eval (file-name-directory "/tmp/foo/")) + (file-name-nondirectory + :eval (file-name-nondirectory "/tmp/foo") + :eval (file-name-nondirectory "/tmp/foo/")) + (file-name-sans-versions + :args (filename) + :eval (file-name-sans-versions "/tmp/foo~")) + (file-name-extension + :eval (file-name-extension "/tmp/foo.txt")) + (file-name-sans-extension + :eval (file-name-sans-extension "/tmp/foo.txt")) + (file-name-with-extension + :eval (file-name-with-extension "foo.txt" "bin") + :eval (file-name-with-extension "foo" "bin")) + (file-name-base + :eval (file-name-base "/tmp/foo.txt")) + (file-relative-name + :eval (file-relative-name "/tmp/foo" "/tmp")) + (file-name-split + :eval (file-name-split "/tmp/foo") + :eval (file-name-split "foo/bar")) + (make-temp-name + :eval (make-temp-name "/tmp/foo-")) + (file-name-concat + :eval (file-name-concat "/tmp/" "foo") + :eval (file-name-concat "/tmp" "foo") + :eval (file-name-concat "/tmp" "foo" "bar/" "zot") + :eval (file-name-concat "/tmp" "~")) + (expand-file-name + :eval (expand-file-name "foo" "/tmp/") + :eval (expand-file-name "foo" "/tmp///") + :eval (expand-file-name "foo" "/tmp/foo/.././") + :eval (expand-file-name "~" "/tmp/")) + (substitute-in-file-name + :eval (substitute-in-file-name "$HOME/foo")) + "Directory Functions" + (file-name-as-directory + :eval (file-name-as-directory "/tmp/foo")) + (directory-file-name + :eval (directory-file-name "/tmp/foo/")) + (abbreviate-file-name + :no-eval (abbreviate-file-name "/home/some-user") + :eg-result "~some-user") + (file-name-parent-directory + :eval (file-name-parent-directory "/foo/bar") + :eval (file-name-parent-directory "/foo/") + :eval (file-name-parent-directory "foo/bar") + :eval (file-name-parent-directory "foo")) + "Quoted File Names" + (file-name-quote + :args (name) + :eval (file-name-quote "/tmp/foo")) + (file-name-unquote + :args (name) + :eval (file-name-unquote "/:/tmp/foo")) + "Predicates" + (file-name-absolute-p + :eval (file-name-absolute-p "/tmp/foo") + :eval (file-name-absolute-p "foo")) + (directory-name-p + :eval (directory-name-p "/tmp/foo/")) + (file-name-quoted-p + :eval (file-name-quoted-p "/:/tmp/foo"))) + +(define-short-documentation-group file + "Inserting Contents" + (insert-file-contents + :no-eval (insert-file-contents "/tmp/foo") + :eg-result ("/tmp/foo" 6)) + (insert-file-contents-literally + :no-eval (insert-file-contents-literally "/tmp/foo") + :eg-result ("/tmp/foo" 6)) + (find-file + :no-eval (find-file "/tmp/foo") + :eg-result-string "#") + "Predicates" + (file-symlink-p + :no-eval (file-symlink-p "/tmp/foo") + :eg-result t) + (file-directory-p + :no-eval (file-directory-p "/tmp") + :eg-result t) + (file-regular-p + :no-eval (file-regular-p "/tmp/foo") + :eg-result t) + (file-exists-p + :no-eval (file-exists-p "/tmp/foo") + :eg-result t) + (file-readable-p + :no-eval (file-readable-p "/tmp/foo") + :eg-result t) + (file-writable-p + :no-eval (file-writable-p "/tmp/foo") + :eg-result t) + (file-accessible-directory-p + :no-eval (file-accessible-directory-p "/tmp") + :eg-result t) + (file-executable-p + :no-eval (file-executable-p "/bin/cat") + :eg-result t) + (file-newer-than-file-p + :no-eval (file-newer-than-file-p "/tmp/foo" "/tmp/bar") + :eg-result nil) + (file-has-changed-p + :no-eval (file-has-changed-p "/tmp/foo") + :eg-result t) + (file-equal-p + :no-eval (file-equal-p "/tmp/foo" "/tmp/bar") + :eg-result nil) + (file-in-directory-p + :no-eval (file-in-directory-p "/tmp/foo" "/tmp/") + :eg-result t) + (file-locked-p + :no-eval (file-locked-p "/tmp/foo") + :eg-result nil) + "Information" + (file-attributes + :no-eval* (file-attributes "/tmp")) + (file-truename + :no-eval (file-truename "/tmp/foo/bar") + :eg-result "/tmp/foo/zot") + (file-chase-links + :no-eval (file-chase-links "/tmp/foo/bar") + :eg-result "/tmp/foo/zot") + (vc-responsible-backend + :args (file &optional no-error) + :no-eval (vc-responsible-backend "/src/foo/bar.c") + :eg-result Git) + (file-acl + :no-eval (file-acl "/tmp/foo") + :eg-result "user::rw-\ngroup::r--\nother::r--\n") + (file-extended-attributes + :no-eval* (file-extended-attributes "/tmp/foo")) + (file-selinux-context + :no-eval* (file-selinux-context "/tmp/foo")) + (locate-file + :no-eval (locate-file "syslog" '("/var/log" "/usr/bin")) + :eg-result "/var/log/syslog") + (executable-find + :no-eval (executable-find "ls") + :eg-result "/usr/bin/ls") + "Creating" + (make-temp-file + :no-eval (make-temp-file "/tmp/foo-") + :eg-result "/tmp/foo-ZcXFMj") + (make-nearby-temp-file + :no-eval (make-nearby-temp-file "/tmp/foo-") + :eg-result "/tmp/foo-xe8iON") + (write-region + :no-value (write-region (point-min) (point-max) "/tmp/foo")) + "Directories" + (make-directory + :no-value (make-directory "/tmp/bar/zot/" t)) + (directory-files + :no-eval (directory-files "/tmp/") + :eg-result ("." ".." ".ICE-unix" ".Test-unix")) + (directory-files-recursively + :no-eval (directory-files-recursively "/tmp/" "\\.png\\'") + :eg-result ("/tmp/foo.png" "/tmp/zot.png" "/tmp/bar/foobar.png")) + (directory-files-and-attributes + :no-eval* (directory-files-and-attributes "/tmp/foo")) + (file-expand-wildcards + :no-eval (file-expand-wildcards "/tmp/*.png") + :eg-result ("/tmp/foo.png" "/tmp/zot.png") + :no-eval (file-expand-wildcards "/*/foo.png") + :eg-result ("/tmp/foo.png" "/var/foo.png")) + (locate-dominating-file + :no-eval (locate-dominating-file "foo.png" "/tmp/foo/bar/zot") + :eg-result "/tmp/foo.png") + (copy-directory + :no-value (copy-directory "/tmp/bar/" "/tmp/barcopy")) + (delete-directory + :no-value (delete-directory "/tmp/bar/")) + "File Operations" + (rename-file + :no-value (rename-file "/tmp/foo" "/tmp/newname")) + (copy-file + :no-value (copy-file "/tmp/foo" "/tmp/foocopy")) + (delete-file + :no-value (delete-file "/tmp/foo")) + (make-empty-file + :no-value (make-empty-file "/tmp/foo")) + (make-symbolic-link + :no-value (make-symbolic-link "/tmp/foo" "/tmp/foosymlink")) + (add-name-to-file + :no-value (add-name-to-file "/tmp/foo" "/tmp/bar")) + (set-file-modes + :no-value "(set-file-modes \"/tmp/foo\" #o644)") + (set-file-times + :no-value (set-file-times "/tmp/foo")) + "File Modes" + (set-default-file-modes + :no-value "(set-default-file-modes #o755)") + (default-file-modes + :no-eval (default-file-modes) + :eg-result-string "#o755") + (file-modes-symbolic-to-number + :no-eval (file-modes-symbolic-to-number "a+r") + :eg-result-string "#o444") + (file-modes-number-to-symbolic + :eval "(file-modes-number-to-symbolic #o444)") + (set-file-extended-attributes + :no-eval (set-file-extended-attributes + "/tmp/foo" '((acl . "group::rxx"))) + :eg-result t) + (set-file-selinux-context + :no-eval (set-file-selinux-context + "/tmp/foo" '(unconfined_u object_r user_home_t s0)) + :eg-result t) + (set-file-acl + :no-eval (set-file-acl "/tmp/foo" "group::rxx") + :eg-result t)) + +(define-short-documentation-group hash-table + "Hash Table Basics" + (make-hash-table + :no-eval (make-hash-table) + :result-string "#s(hash-table ...)") + (puthash + :no-eval (puthash 'key "value" table)) + (gethash + :no-eval (gethash 'key table) + :eg-result "value") + (remhash + :no-eval (remhash 'key table) + :result nil) + (clrhash + :no-eval (clrhash table) + :result-string "#s(hash-table ...)") + (maphash + :no-eval (maphash (lambda (key value) (message value)) table) + :result nil) + "Other Hash Table Functions" + (hash-table-p + :eval (hash-table-p 123)) + (hash-table-contains-p + :no-eval (hash-table-contains-p 'key table)) + (copy-hash-table + :no-eval (copy-hash-table table) + :result-string "#s(hash-table ...)") + (hash-table-count + :no-eval (hash-table-count table) + :eg-result 15)) + +(define-short-documentation-group list + "Making Lists" + (make-list + :eval (make-list 5 'a)) + (cons + :eval (cons 1 '(2 3 4))) + (list + :eval (list 1 2 3)) + (number-sequence + :eval (number-sequence 5 8)) + (ensure-list + :eval (ensure-list "foo") + :eval (ensure-list '(1 2 3)) + :eval (ensure-list '(1 . 2))) + (ensure-proper-list + :eval (ensure-proper-list "foo") + :eval (ensure-proper-list '(1 2 3)) + :eval (ensure-proper-list '(1 . 2))) + "Operations on Lists" + (append + :eval (append '("foo" "bar") '("zot"))) + (copy-tree + :eval (copy-tree '(1 (2 3) 4))) + (flatten-tree + :eval (flatten-tree '(1 (2 3) 4))) + (car + :eval (car '(one two three)) + :eval (car '(one . two)) + :eval (car nil)) + (cdr + :eval (cdr '(one two three)) + :eval (cdr '(one . two)) + :eval (cdr nil)) + (last + :eval (last '(one two three))) + (butlast + :eval (butlast '(one two three))) + (nbutlast + :eval (nbutlast (list 'one 'two 'three))) + (nth + :eval (nth 1 '(one two three))) + (nthcdr + :eval (nthcdr 1 '(one two three))) + (take + :eval (take 3 '(one two three four))) + (ntake + :eval (ntake 3 (list 'one 'two 'three 'four))) + (take-while + :eval (take-while #'numberp '(1 2 three 4 five))) + (drop-while + :eval (drop-while #'numberp '(1 2 three 4 five))) + (any + :eval (any #'symbolp '(1 2 three 4 five))) + (all + :eval (all #'symbolp '(one 2 three)) + :eval (all #'symbolp '(one two three))) + (elt + :eval (elt '(one two three) 1)) + (car-safe + :eval (car-safe '(one two three))) + (cdr-safe + :eval (cdr-safe '(one two three))) + (push + :no-eval* (push 'a list)) + (pop + :no-eval* (pop list)) + (setcar + :no-eval (setcar list 'c) + :result c) + (setcdr + :no-eval (setcdr list (list c)) + :result '(c)) + (nconc + :eval (nconc (list 1) (list 2 3 4))) + (delq + :eval (delq 'a (list 'a 'b 'c 'd))) + (delete + :eval (delete 2 (list 1 2 3 4)) + :eval (delete "a" (list "a" "b" "c" "d"))) + (remq + :eval (remq 'b '(a b c))) + (remove + :eval (remove 2 '(1 2 3 4)) + :eval (remove "a" '("a" "b" "c" "d"))) + (delete-dups + :eval (delete-dups (list 1 2 4 3 2 4))) + "Mapping Over Lists" + (mapcar + :eval (mapcar #'list '(1 2 3))) + (mapcan + :eval (mapcan #'list '(1 2 3))) + (mapc + :eval (mapc #'insert '("1" "2" "3"))) + (seq-reduce + :eval (seq-reduce #'+ '(1 2 3) 0)) + (mapconcat + :eval (mapconcat #'identity '("foo" "bar") "|")) + "Predicates" + (listp + :eval (listp '(1 2 3)) + :eval (listp nil) + :eval (listp '(1 . 2))) + (consp + :eval (consp '(1 2 3)) + :eval (consp nil)) + (proper-list-p + :eval (proper-list-p '(1 2 3)) + :eval (proper-list-p nil) + :eval (proper-list-p '(1 . 2))) + (null + :eval (null nil)) + (atom + :eval (atom 'a)) + (nlistp + :eval (nlistp '(1 2 3)) + :eval (nlistp t) + :eval (nlistp '(1 . 2))) + "Finding Elements" + (memq + :eval (memq 'b '(a b c))) + (memql + :eval (memql 2.0 '(1.0 2.0 3.0))) + (member + :eval (member 2 '(1 2 3)) + :eval (member "b" '("a" "b" "c"))) + (member-ignore-case + :eval (member-ignore-case "foo" '("bar" "Foo" "zot"))) + "Association Lists" + (assoc + :eval (assoc "b" '(("a" . 1) ("b" . 2)))) + (rassoc + :eval (rassoc "b" '((1 . "a") (2 . "b")))) + (assq + :eval (assq 'b '((a . 1) (b . 2)))) + (rassq + :eval (rassq 'b '((1 . a) (2 . b)))) + (assoc-string + :eval (assoc-string "foo" '(("a" 1) (foo 2)))) + (alist-get + :eval (alist-get 2 '((1 . a) (2 . b)))) + (assoc-default + :eval (assoc-default 2 '((1 . a) (2 . b) #'=))) + (copy-alist + :eval (copy-alist '((1 . a) (2 . b)))) + (assoc-delete-all + :eval (assoc-delete-all "b" (list '("a" . a) '("b" . b) '("b" . c)))) + (assq-delete-all + :eval (assq-delete-all 2 (list '(1 . a) '(2 . b) '(2 . c)))) + (rassq-delete-all + :eval (rassq-delete-all 'b (list '(1 . a) '(2 . b) '(2 . c)))) + "Property Lists" + (plist-get + :eval (plist-get '(a 1 b 2 c 3) 'b)) + (plist-put + :no-eval (setq plist (plist-put plist 'd 4)) + :eg-result (a 1 b 2 c 3 d 4)) + (plist-member + :eval (plist-member '(a 1 b 2 c 3) 'b)) + "Data About Lists" + (length + :eval (length '(a b c))) + (length< + :eval (length< '(a b c) 1)) + (length> + :eval (length> '(a b c) 1)) + (length= + :eval (length= '(a b c) 3)) + (safe-length + :eval (safe-length '(a b c)))) + +(define-short-documentation-group symbol + "Making symbols" + (intern + :eval (intern "abc")) + (intern-soft + :eval (intern-soft "list") + :eval (intern-soft "Phooey!")) + (make-symbol + :eval (make-symbol "abc")) + (gensym + :no-eval (gensym) + :eg-result g37) + "Comparing symbols" + (eq + :eval (eq 'abc 'abc) + :eval (eq 'abc 'abd)) + (eql + :eval (eql 'abc 'abc)) + (equal + :eval (equal 'abc 'abc)) + "Name" + (symbol-name + :eval (symbol-name 'abc)) + "Obarrays" + (obarray-make + :eval (obarray-make)) + (obarrayp + :eval (obarrayp (obarray-make)) + :eval (obarrayp nil)) + (unintern + :no-eval (unintern "abc" my-obarray) + :eg-result t) + (mapatoms + :no-eval (mapatoms (lambda (symbol) (print symbol)) my-obarray)) + (obarray-clear + :no-eval (obarray-clear my-obarray))) + +(define-short-documentation-group comparison + "General-purpose" + (eq + :eval (eq 'a 'a) + :eval "(eq ?A ?A)" + :eval (let ((x (list 'a "b" '(c) 4 5.0))) + (eq x x))) + (eql + :eval (eql 2 2) + :eval (eql 2.0 2.0) + :eval (eql 2.0 2)) + (equal + :eval (equal "abc" "abc") + :eval (equal 2.0 2.0) + :eval (equal 2.0 2) + :eval (equal '(a "b" (c) 4.0) '(a "b" (c) 4.0))) + (cl-equalp + :eval (cl-equalp 2 2.0) + :eval (cl-equalp "ABC" "abc")) + "Numeric" + (= + :args (number &rest numbers) + :eval (= 2 2) + :eval (= 2.0 2.0) + :eval (= 2.0 2) + :eval (= 4 4 4 4)) + (/= + :eval (/= 4 4)) + (< + :args (number &rest numbers) + :eval (< 4 4) + :eval (< 1 2 3)) + (<= + :args (number &rest numbers) + :eval (<= 4 4) + :eval (<= 1 2 2 3)) + (> + :args (number &rest numbers) + :eval (> 4 4) + :eval (> 3 2 1)) + (>= + :args (number &rest numbers) + :eval (>= 4 4) + :eval (>= 3 2 2 1)) + "String" + (string-equal + :eval (string-equal "abc" "abc") + :eval (string-equal "abc" "ABC")) + (string-equal-ignore-case + :eval (string-equal-ignore-case "abc" "ABC")) + (string-lessp + :eval (string-lessp "abc" "abd") + :eval (string-lessp "abc" "abc") + :eval (string-lessp "pic4.png" "pic32.png")) + (string-greaterp + :eval (string-greaterp "abd" "abc") + :eval (string-greaterp "abc" "abc")) + (string-version-lessp + :eval (string-version-lessp "pic4.png" "pic32.png") + :eval (string-version-lessp "1.9.3" "1.10.2")) + (string-collate-lessp + :eval (string-collate-lessp "abc" "abd"))) + +(define-short-documentation-group vector + "Making Vectors" + (make-vector + :eval (make-vector 5 "foo")) + (vector + :eval (vector 1 "b" 3)) + "Operations on Vectors" + (vectorp + :eval (vectorp [1]) + :eval (vectorp "1")) + (vconcat + :eval (vconcat '(1 2) [3 4])) + (append + :eval (append [1 2] nil)) + (length + :eval (length [1 2 3])) + (seq-reduce + :eval (seq-reduce #'+ [1 2 3] 0)) + (seq-subseq + :eval (seq-subseq [1 2 3 4 5] 1 3) + :eval (seq-subseq [1 2 3 4 5] 1)) + (copy-tree + :eval (copy-tree [1 (2 3) [4 5]] t)) + "Mapping Over Vectors" + (mapcar + :eval (mapcar #'identity [1 2 3])) + (mapc + :eval (mapc #'insert ["1" "2" "3"]))) + +(define-short-documentation-group regexp + "Matching Strings" + (replace-regexp-in-string + :eval (replace-regexp-in-string "[a-z]+" "_" "*foo*")) + (string-match-p + :eval (string-match-p "^[fo]+" "foobar")) + "Looking in Buffers" + (re-search-forward + :no-eval (re-search-forward "^foo$" nil t) + :eg-result 43) + (re-search-backward + :no-eval (re-search-backward "^foo$" nil t) + :eg-result 43) + (looking-at-p + :no-eval (looking-at-p "f[0-9]") + :eg-result t) + "Match Data" + (match-string + :eval (and (string-match "^\\([fo]+\\)b" "foobar") + (match-string 0 "foobar"))) + (match-beginning + :no-eval (match-beginning 1) + :eg-result 0) + (match-end + :no-eval (match-end 1) + :eg-result 3) + (save-match-data + :no-eval (save-match-data ...)) + "Replacing Match" + (replace-match + :no-eval (replace-match "new") + :eg-result nil) + (match-substitute-replacement + :no-eval (match-substitute-replacement "new") + :eg-result "new") + (replace-regexp-in-region + :no-value (replace-regexp-in-region "[0-9]+" "Num \\&")) + "Utilities" + (regexp-quote + :eval (regexp-quote "foo.*bar")) + (regexp-opt + :eval (regexp-opt '("foo" "bar"))) + (regexp-opt-depth + :eval (regexp-opt-depth "\\(a\\(b\\)\\)")) + (regexp-opt-charset + :eval (regexp-opt-charset '(?a ?b ?c ?d ?e))) + "The `rx' Structured Regexp Notation" + (rx + :eval (rx "IP=" (+ digit) (= 3 "." (+ digit)))) + (rx-to-string + :eval (rx-to-string '(| "foo" "bar"))) + (rx-define + :no-eval "(and (rx-define haskell-comment (seq \"--\" (zero-or-more nonl))) + (rx haskell-comment))" + :result "--.*") + (rx-let + :eval "(rx-let ((comma-separated (item) (seq item (0+ \",\" item))) + (number (1+ digit)) + (numbers (comma-separated number))) + (rx \"(\" numbers \")\"))" + :result "([[:digit:]]+\\(?:,[[:digit:]]+\\)*)") + (rx-let-eval + :eval "(rx-let-eval + '((ponder (x) (seq \"Where have all the \" x \" gone?\"))) + (rx-to-string + '(ponder (or \"flowers\" \"cars\" \"socks\"))))" + :result "\\(?:Where have all the \\(?:\\(?:car\\|flower\\|sock\\)s\\) gone\\?\\)")) + +(define-short-documentation-group sequence + "Sequence Predicates" + (seq-contains-p + :eval (seq-contains-p '(a b c) 'b) + :eval (seq-contains-p '(a b c) 'd)) + (seq-every-p + :eval (seq-every-p #'numberp '(1 2 3))) + (seq-empty-p + :eval (seq-empty-p [])) + (seq-set-equal-p + :eval (seq-set-equal-p '(1 2 3) '(3 1 2))) + (seq-some + :eval (seq-some #'floatp '(1 2.0 3))) + "Building Sequences" + (seq-concatenate + :eval (seq-concatenate 'vector '(1 2) '(c d))) + (seq-copy + :eval (seq-copy '(a 2))) + (seq-into + :eval (seq-into '(1 2 3) 'vector)) + "Utility Functions" + (seq-count + :eval (seq-count #'numberp '(1 b c 4))) + (seq-elt + :eval (seq-elt '(a b c) 1)) + (seq-random-elt + :no-eval (seq-random-elt '(a b c)) + :eg-result c) + (seq-find + :eval (seq-find #'numberp '(a b 3 4 f 6))) + (seq-position + :eval (seq-position '(a b c) 'c)) + (seq-positions + :eval (seq-positions '(a b c a d) 'a) + :eval (seq-positions '(a b c a d) 'z) + :eval (seq-positions '(11 5 7 12 9 15) 10 #'>=)) + (seq-length + :eval (seq-length "abcde")) + (seq-max + :eval (seq-max [1 2 3])) + (seq-min + :eval (seq-min [1 2 3])) + (seq-first + :eval (seq-first [a b c])) + (seq-rest + :eval (seq-rest '[1 2 3])) + (seq-reverse + :eval (seq-reverse '(1 2 3))) + (seq-sort + :eval (seq-sort #'> '(1 2 3))) + (seq-sort-by + :eval (seq-sort-by (lambda (a) (/ 1.0 a)) #'< '(1 2 3))) + "Mapping Over Sequences" + (seq-map + :eval (seq-map #'1+ '(1 2 3))) + (seq-map-indexed + :eval (seq-map-indexed (lambda (a i) (cons i a)) '(a b c))) + (seq-mapcat + :eval (seq-mapcat #'upcase '("a" "b" "c") 'string)) + (seq-doseq + :no-eval (seq-doseq (a '("foo" "bar")) (insert a)) + :eg-result ("foo" "bar")) + (seq-do + :no-eval (seq-do (lambda (a) (insert a)) '("foo" "bar")) + :eg-result ("foo" "bar")) + (seq-do-indexed + :no-eval (seq-do-indexed + (lambda (a index) (message "%s:%s" index a)) + '("foo" "bar")) + :eg-result nil) + (seq-reduce + :eval (seq-reduce #'* [1 2 3] 2)) + "Excerpting Sequences" + (seq-drop + :eval (seq-drop '(a b c) 2)) + (seq-drop-while + :eval (seq-drop-while #'numberp '(1 2 c d 5))) + (seq-filter + :eval (seq-filter #'numberp '(a b 3 4 f 6))) + (seq-keep + :eval (seq-keep #'car-safe '((1 2) 3 t (a . b)))) + (seq-remove + :eval (seq-remove #'numberp '(1 2 c d 5))) + (seq-remove-at-position + :eval (seq-remove-at-position '(a b c d e) 3) + :eval (seq-remove-at-position [a b c d e] 0)) + (seq-group-by + :eval (seq-group-by #'natnump '(-1 2 3 -4 -5 6))) + (seq-union + :eval (seq-union '(1 2 3) '(3 5))) + (seq-difference + :eval (seq-difference '(1 2 3) '(2 3 4))) + (seq-intersection + :eval (seq-intersection '(1 2 3) '(2 3 4))) + (seq-partition + :eval (seq-partition '(a b c d e f g h) 3)) + (seq-subseq + :eval (seq-subseq '(a b c d e) 2 4)) + (seq-take + :eval (seq-take '(a b c d e) 3)) + (seq-split + :eval (seq-split [0 1 2 3 5] 2)) + (seq-take-while + :eval (seq-take-while #'integerp [1 2 3.0 4])) + (seq-uniq + :eval (seq-uniq '(a b d b a c)))) + +(define-short-documentation-group buffer + "Buffer Basics" + (current-buffer + :no-eval (current-buffer) + :eg-result-string "#") + (bufferp + :eval (bufferp 23)) + (buffer-live-p + :no-eval (buffer-live-p some-buffer) + :eg-result t) + (buffer-modified-p + :eval (buffer-modified-p (current-buffer))) + (buffer-name + :eval (buffer-name)) + (window-buffer + :eval (window-buffer)) + "Selecting Buffers" + (get-buffer-create + :no-eval (get-buffer-create "*foo*") + :eg-result-string "#") + (pop-to-buffer + :no-eval (pop-to-buffer "*foo*") + :eg-result-string "#") + (with-current-buffer + :no-eval* (with-current-buffer buffer (buffer-size))) + "Points and Positions" + (point + :eval (point)) + (point-min + :eval (point-min)) + (point-max + :eval (point-max)) + (pos-bol + :eval (pos-bol)) + (pos-eol + :eval (pos-eol)) + (bolp + :eval (bolp)) + (eolp + :eval (eolp)) + (line-beginning-position + :eval (line-beginning-position)) + (line-end-position + :eval (line-end-position)) + (buffer-size + :eval (buffer-size)) + (bobp + :eval (bobp)) + (eobp + :eval (eobp)) + "Moving Around" + (goto-char + :no-eval (goto-char (point-max)) + :eg-result 342) + (search-forward + :no-eval (search-forward "some-string" nil t) + :eg-result 245) + (re-search-forward + :no-eval (re-search-forward "some-s.*g" nil t) + :eg-result 245) + (forward-line + :no-eval (forward-line 1) + :eg-result 0 + :no-eval (forward-line -2) + :eg-result 0) + "Strings from Buffers" + (buffer-string + :no-eval* (buffer-string)) + (buffer-substring + :eval (buffer-substring (point-min) (+ (point-min) 10))) + (buffer-substring-no-properties + :eval (buffer-substring-no-properties (point-min) (+ (point-min) 10))) + (following-char + :no-eval (following-char) + :eg-result 67) + (preceding-char + :no-eval (preceding-char) + :eg-result 38) + (char-after + :eval (char-after 45)) + (char-before + :eval (char-before 13)) + (get-byte + :no-eval (get-byte 45) + :eg-result-string "#xff") + "Altering Buffers" + (delete-region + :no-value (delete-region (point-min) (point-max))) + (erase-buffer + :no-value (erase-buffer)) + (delete-line + :no-value (delete-line)) + (insert + :no-value (insert "This string will be inserted in the buffer\n")) + (subst-char-in-region + :no-eval "(subst-char-in-region (point-min) (point-max) ?+ ?-)") + (replace-string-in-region + :no-value (replace-string-in-region "foo" "bar")) + "Locking" + (lock-buffer + :no-value (lock-buffer "/tmp/foo")) + (unlock-buffer + :no-value (unlock-buffer))) + +(define-short-documentation-group overlay + "Predicates" + (overlayp + :no-eval (overlayp some-overlay) + :eg-result t) + "Creation and Deletion" + (make-overlay + :args (beg end &optional buffer) + :no-eval (make-overlay 1 10) + :eg-result-string "#") + (delete-overlay + :no-eval (delete-overlay foo) + :eg-result t) + "Searching Overlays" + (overlays-at + :no-eval (overlays-at 15) + :eg-result-string "(#)") + (overlays-in + :no-eval (overlays-in 1 30) + :eg-result-string "(#)") + (next-overlay-change + :no-eval (next-overlay-change 1) + :eg-result 20) + (previous-overlay-change + :no-eval (previous-overlay-change 30) + :eg-result 20) + "Overlay Properties" + (overlay-start + :no-eval (overlay-start foo) + :eg-result 1) + (overlay-end + :no-eval (overlay-end foo) + :eg-result 10) + (overlay-put + :no-eval (overlay-put foo 'happy t) + :eg-result t) + (overlay-get + :no-eval (overlay-get foo 'happy) + :eg-result t) + (overlay-buffer + :no-eval (overlay-buffer foo)) + "Moving Overlays" + (move-overlay + :no-eval (move-overlay foo 5 20) + :eg-result-string "#")) + +(define-short-documentation-group process + (make-process + :no-eval (make-process :name "foo" :command '("cat" "/tmp/foo")) + :eg-result-string "#") + (processp + :eval (processp t)) + (process-status + :no-eval (process-status process) + :eg-result exit) + (delete-process + :no-value (delete-process process)) + (kill-process + :no-value (kill-process process)) + (set-process-sentinel + :no-value (set-process-sentinel process (lambda (proc string)))) + (process-buffer + :no-eval (process-buffer process) + :eg-result-string "#") + (get-buffer-process + :no-eval (get-buffer-process buffer) + :eg-result-string "#") + (process-live-p + :no-eval (process-live-p process) + :eg-result t)) + +(define-short-documentation-group number + "Arithmetic" + (+ + :args (&rest numbers) + :eval (+ 1 2) + :eval (+ 1 2 3 4)) + (- + :args (&rest numbers) + :eval (- 3 2) + :eval (- 6 3 2)) + (* + :args (&rest numbers) + :eval (* 3 4 5)) + (/ + :eval (/ 10 5) + :eval (/ 10 6) + :eval (/ 10.0 6) + :eval (/ 10.0 3 3)) + (% + :eval (% 10 5) + :eval (% 10 6)) + (mod + :eval (mod 10 5) + :eval (mod 10 6) + :eval (mod 10.5 6)) + (1+ + :eval (1+ 2) + :eval (let ((x 2)) (1+ x) x)) + (1- + :eval (1- 4) + :eval (let ((x 4)) (1- x) x)) + (incf + :eval (let ((x 2)) (incf x) x) + :eval (let ((x 2)) (incf x 2) x)) + (decf + :eval (let ((x 4)) (decf x) x) + :eval (let ((x 4)) (decf x 2)) x) + "Predicates" + (= + :args (number &rest numbers) + :eval (= 4 4) + :eval (= 4.0 4.0) + :eval (= 4 4.0) + :eval (= 4 4 4 4)) + (eql + :eval (eql 4 4) + :eval (eql 4.0 4.0)) + (/= + :eval (/= 4 4)) + (< + :args (number &rest numbers) + :eval (< 4 4) + :eval (< 1 2 3)) + (<= + :args (number &rest numbers) + :eval (<= 4 4) + :eval (<= 1 2 2 3)) + (> + :args (number &rest numbers) + :eval (> 4 4) + :eval (> 3 2 1)) + (>= + :args (number &rest numbers) + :eval (>= 4 4) + :eval (>= 3 2 2 1)) + (zerop + :eval (zerop 0)) + (natnump + :eval (natnump -1) + :eval (natnump 0) + :eval (natnump 23)) + (plusp + :eval (plusp 0) + :eval (plusp 1)) + (minusp + :eval (minusp 0) + :eval (minusp -1)) + (oddp + :eval (oddp 3)) + (evenp + :eval (evenp 6)) + (bignump + :eval (bignump 4) + :eval (bignump (expt 2 90))) + (fixnump + :eval (fixnump 4) + :eval (fixnump (expt 2 90))) + (floatp + :eval (floatp 5.4)) + (integerp + :eval (integerp 5.4)) + (numberp + :eval (numberp "5.4")) + (cl-digit-char-p + :eval (cl-digit-char-p ?5 10) + :eval (cl-digit-char-p ?f 16)) + "Operations" + (max + :args (number &rest numbers) + :eval (max 7 9 3)) + (min + :args (number &rest numbers) + :eval (min 7 9 3)) + (abs + :eval (abs -4)) + (float + :eval (float 2)) + (truncate + :eval (truncate 1.2) + :eval (truncate -1.2) + :eval (truncate 5.4 2)) + (floor + :eval (floor 1.2) + :eval (floor -1.2) + :eval (floor 5.4 2)) + (ceiling + :eval (ceiling 1.2) + :eval (ceiling -1.2) + :eval (ceiling 5.4 2)) + (round + :eval (round 1.2) + :eval (round -1.2) + :eval (round 5.4 2)) + (random + :eval (random 6)) + "Bit Operations" + (ash + :eval (ash 1 4) + :eval (ash 16 -1)) + (logand + :no-eval "(logand #b10 #b111)" + :result-string "#b10") + (logior + :eval (logior 4 16)) + (logxor + :eval (logxor 4 16)) + (lognot + :eval (lognot 5)) + (logcount + :eval (logcount 5)) + "Floating Point" + (isnan + :eval (isnan 5.0)) + (frexp + :eval (frexp 5.7)) + (ldexp + :eval (ldexp 0.7125 3)) + (logb + :eval (logb 10.5)) + (ffloor + :eval (ffloor 1.2)) + (fceiling + :eval (fceiling 1.2)) + (ftruncate + :eval (ftruncate 1.2)) + (fround + :eval (fround 1.2)) + "Standard Math Functions" + (sin + :eval (sin float-pi)) + (cos + :eval (cos float-pi)) + (tan + :eval (tan float-pi)) + (asin + :eval (asin float-pi)) + (acos + :eval (acos float-pi)) + (atan + :eval (atan float-pi)) + (exp + :eval (exp 4)) + (log + :eval (log 54.59)) + (expt + :eval (expt 2 16)) + (sqrt + :eval (sqrt -1))) + +(define-short-documentation-group text-properties + "Examining Text Properties" + (get-text-property + :eval (get-text-property 0 'foo (propertize "x" 'foo t))) + (get-char-property + :eval (get-char-property 0 'foo (propertize "x" 'foo t))) + (get-pos-property + :eval (get-pos-property 0 'foo (propertize "x" 'foo t))) + (get-char-property-and-overlay + :eval (get-char-property-and-overlay 0 'foo (propertize "x" 'foo t))) + (text-properties-at + :eval (text-properties-at (point))) + "Changing Text Properties" + (put-text-property + :eval (let ((s (copy-sequence "abc"))) (put-text-property 0 1 'foo t s) s) + :no-eval (put-text-property (point) (1+ (point)) 'face 'error)) + (add-text-properties + :no-eval (add-text-properties (point) (1+ (point)) '(face error))) + (remove-text-properties + :no-eval (remove-text-properties (point) (1+ (point)) '(face nil))) + (remove-list-of-text-properties + :no-eval (remove-list-of-text-properties (point) (1+ (point)) '(face font-lock-face))) + (set-text-properties + :no-eval (set-text-properties (point) (1+ (point)) '(face error))) + (add-face-text-property + :no-eval (add-face-text-property START END '(:foreground "green"))) + (propertize + :eval (propertize "foo" 'face 'italic 'mouse-face 'bold-italic)) + "Searching for Text Properties" + (next-property-change + :no-eval (next-property-change (point) (current-buffer))) + (previous-property-change + :no-eval (previous-property-change (point) (current-buffer))) + (next-single-property-change + :no-eval (next-single-property-change (point) 'face (current-buffer))) + (previous-single-property-change + :no-eval (previous-single-property-change (point) 'face (current-buffer))) + ;; TODO: There are some more that could be added here. + (text-property-search-forward + :no-eval (text-property-search-forward 'face nil t)) + (text-property-search-backward + :no-eval (text-property-search-backward 'face nil t))) + +(define-short-documentation-group keymaps + "Defining keymaps or adding bindings to existing keymaps" + (define-keymap + :no-eval (define-keymap "C-c C-c" #'quit-buffer) + :no-eval (define-keymap :keymap ctl-x-map + "C-r" #'recentf-open + "k" #'kill-current-buffer)) + (defvar-keymap + :no-eval (defvar-keymap my-keymap "C-c C-c" #'quit-buffer)) + "Setting keys" + (keymap-set + :no-eval (keymap-set map "C-c C-c" #'quit-buffer)) + (keymap-local-set + :no-eval (keymap-local-set "C-c C-c" #'quit-buffer)) + (keymap-global-set + :no-eval (keymap-global-set "C-c C-c" #'quit-buffer)) + (keymap-unset + :no-eval (keymap-unset map "C-c C-c")) + (keymap-local-unset + :no-eval (keymap-local-unset "C-c C-c")) + (keymap-global-unset + :no-eval (keymap-global-unset "C-c C-c")) + (keymap-substitute + :no-eval (keymap-substitute map "C-c C-c" "M-a")) + (keymap-set-after + :no-eval (keymap-set-after map "" menu-bar-separator)) + "Predicates" + (keymapp + :eval (keymapp (define-keymap))) + (key-valid-p + :eval (key-valid-p "C-c C-c") + :eval (key-valid-p "C-cC-c")) + "Lookup" + (keymap-lookup + :eval (keymap-lookup (current-global-map) "C-x x g"))) + +;;;###autoload +(defun shortdoc-display-group (group &optional function same-window) + "Pop to a buffer with short documentation summary for functions in GROUP. +Interactively, prompt for GROUP. +If FUNCTION is non-nil, place point on the entry for FUNCTION (if any). +If SAME-WINDOW, don't pop to a new window." + (interactive (list (completing-read + "Group of functions for which to show summary: " + (mapcar #'car shortdoc--groups)))) + (when (stringp group) + (setq group (intern group))) + (unless (assq group shortdoc--groups) + (error "No such documentation group %s" group)) + (let ((buf (get-buffer-create (format "*Shortdoc %s*" group)))) + (shortdoc--insert-group-in-buffer group buf) + (funcall (if same-window + #'pop-to-buffer-same-window + #'pop-to-buffer) + buf)) + (goto-char (point-min)) + (when function + (text-property-search-forward 'shortdoc-function function t) + (beginning-of-line))) + +(defun shortdoc--insert-group-in-buffer (group &optional buf) + "Insert a short documentation summary for functions in GROUP in buffer BUF. +BUF defaults to the current buffer if nil or omitted." + (with-current-buffer (or buf (current-buffer)) + (let ((inhibit-read-only t) + (prev nil)) + (erase-buffer) + (shortdoc-mode) + (button-mode) + (mapc + (lambda (data) + (cond + ((stringp data) + (setq prev nil) + (unless (bobp) + (insert "\n")) + (insert (propertize + (substitute-command-keys data) + 'face 'shortdoc-heading + 'shortdoc-section t + 'outline-level 1)) + (insert (propertize + "\n\n" + 'face 'shortdoc-heading + 'shortdoc-section t))) + ;; There may be functions not yet defined in the data. + ((fboundp (car data)) + (when prev + (insert (make-separator-line) + ;; This helps with hidden outlines (bug#53981) + (propertize "\n" 'face '(:height 0)))) + (setq prev t) + (shortdoc--display-function data)))) + (cdr (assq group shortdoc--groups)))))) + +;;;###autoload +(defalias 'shortdoc #'shortdoc-display-group) + +(defun shortdoc--display-function (data) + (let ((function (pop data)) + (start-section (point)) + arglist-start) + ;; Function calling convention. + (insert (propertize "(" 'shortdoc-function function 'outline-level 2)) + (if (plist-get data :no-manual) + (insert-text-button + (symbol-name function) + 'face 'button + 'action (lambda (_) + (describe-function function)) + 'follow-link t + 'help-echo "mouse-1, RET: describe function") + (insert-text-button + (symbol-name function) + 'face 'button + 'action (lambda (_) + (info-lookup-symbol function 'emacs-lisp-mode)) + 'follow-link t + 'help-echo "mouse-1, RET: show \ +function's documentation in the Info manual")) + (setq arglist-start (point)) + (insert ")\n") + ;; Doc string. + (insert " " + (or (plist-get data :doc) + (car (split-string (or (documentation function) + "Error: missing docstring.") + "\n")))) + (insert "\n") + (add-face-text-property start-section (point) 'shortdoc-section t) + (let ((print-escape-newlines t) + (double-arrow (if (char-displayable-p ?β‡’) + "β‡’" + "=>")) + (single-arrow (if (char-displayable-p ?β†’) + "β†’" + "->")) + (start-example (point))) + (cl-loop for (type value) on data by #'cddr + do + (cl-case type + (:eval + (insert " ") + (if (stringp value) + (insert value) + (prin1 value (current-buffer))) + (insert "\n " double-arrow " ") + (let ((expr (if (stringp value) + (car (read-from-string value)) + value))) + (prin1 (eval expr) (current-buffer))) + (insert "\n")) + (:no-eval* + (if (stringp value) + (insert " " value "\n") + (insert " ") + (prin1 value (current-buffer))) + (insert "\n " single-arrow " " + (propertize "[it depends]" + 'face 'shortdoc-section) + "\n")) + (:no-value + (if (stringp value) + (insert " " value) + (insert " ") + (prin1 value (current-buffer))) + (insert "\n")) + (:no-eval + (if (stringp value) + (insert " " value) + (insert " ") + (prin1 value (current-buffer))) + (insert "\n")) + (:result + (insert " " double-arrow " ") + (prin1 value (current-buffer)) + (insert "\n")) + (:result-string + (insert " " double-arrow " ") + (princ value (current-buffer)) + (insert "\n")) + (:eg-result + (insert " e.g. " double-arrow " ") + (prin1 value (current-buffer)) + (insert "\n")) + (:eg-result-string + (insert " e.g. " double-arrow " ") + (princ value (current-buffer)) + (insert "\n")))) + (add-text-properties start-example (point) `(shortdoc-example ,function))) + ;; Insert the arglist after doing the evals, in case that's pulled + ;; in the function definition. + (save-excursion + (goto-char arglist-start) + (dolist (param (or (plist-get data :args) + (help-function-arglist function t))) + (insert " " (symbol-name param))) + (add-face-text-property arglist-start (point) 'shortdoc-section t)))) + +(defun shortdoc-function-examples (function) + "Return all shortdoc examples for FUNCTION. +The result is an alist with items of the form (GROUP . EXAMPLES), +where GROUP is a shortdoc group where FUNCTION appears, and +EXAMPLES is a string with the usage examples of FUNCTION defined +in GROUP. Return nil if FUNCTION is not a function or if it +doesn't has any shortdoc information." + (let ((groups (and (symbolp function) + (shortdoc-function-groups function))) + (examples nil)) + (mapc + (lambda (group) + (with-temp-buffer + (shortdoc--insert-group-in-buffer group) + (goto-char (point-min)) + (let ((match (text-property-search-forward + 'shortdoc-example function t))) + (push `(,group . ,(string-trim + (buffer-substring-no-properties + (prop-match-beginning match) + (prop-match-end match)))) + examples)))) + groups) + examples)) + +(defun shortdoc-help-fns-examples-function (function) + "Insert Emacs Lisp examples for FUNCTION into the current buffer. +You can add this function to the `help-fns-describe-function-functions' +hook to show examples of using FUNCTION in *Help* buffers produced +by \\[describe-function]." + (let* ((examples (shortdoc-function-examples function)) + (num-examples (length examples)) + (times 0)) + (dolist (example examples) + (when (zerop times) + (if (> num-examples 1) + (insert "\n Examples:\n\n") + ;; Some functions have more than one example per group. + ;; Count the number of arrows to know if we need to + ;; pluralize "Example". + (let* ((text (cdr example)) + (count 0) + (pos 0) + (end (length text)) + (double-arrow (if (char-displayable-p ?β‡’) + " β‡’" + " =>")) + (double-arrow-example (if (char-displayable-p ?β‡’) + " e.g. β‡’" + " e.g. =>")) + (single-arrow (if (char-displayable-p ?β†’) + " β†’" + " ->"))) + (while (and (< pos end) + (or (string-match double-arrow text pos) + (string-match double-arrow-example text pos) + (string-match single-arrow text pos))) + (setq count (1+ count) + pos (match-end 0))) + (if (> count 1) + (insert "\n Examples:\n\n") + (insert "\n Example:\n\n"))))) + (setq times (1+ times)) + (insert " ") + (insert (cdr example)) + (insert "\n\n")))) + +(defun shortdoc-function-groups (function) + "Return all shortdoc groups FUNCTION appears in." + (cl-loop for group in shortdoc--groups + when (assq function (cdr group)) + collect (car group))) + +(defun shortdoc-add-function (group section elem) + "Add ELEM to shortdoc GROUP in SECTION. +If GROUP doesn't exist, it will be created. +If SECTION doesn't exist, it will be added. + +ELEM is a Lisp form. See `define-short-documentation-group' for +details. + +Example: + + (shortdoc-add-function + \\='file \"Predicates\" + \\='(file-locked-p :no-eval (file-locked-p \"/tmp\")))" + (let ((glist (assq group shortdoc--groups))) + (unless glist + (setq glist (list group)) + (push glist shortdoc--groups)) + (let ((slist (member section glist))) + (unless slist + (setq slist (list section)) + (nconc glist slist)) + (while (and (cdr slist) + (not (stringp (cadr slist)))) + (setq slist (cdr slist))) + (setcdr slist (cons elem (cdr slist)))))) + +(defvar-keymap shortdoc-mode-map + :doc "Keymap for `shortdoc-mode'." + "n" #'shortdoc-next + "p" #'shortdoc-previous + "N" #'shortdoc-next-section + "P" #'shortdoc-previous-section + "C-c C-n" #'shortdoc-next-section + "C-c C-p" #'shortdoc-previous-section + "w" #'shortdoc-copy-function-as-kill) + +(define-derived-mode shortdoc-mode special-mode "shortdoc" + "Mode for shortdoc." + :interactive nil + (setq-local outline-search-function #'outline-search-level + outline-level (lambda () + (get-text-property (point) 'outline-level)))) + +(defun shortdoc--goto-section (arg sym &optional reverse) + (unless (natnump arg) + (setq arg 1)) + (while (> arg 0) + (funcall + (if reverse 'text-property-search-backward + 'text-property-search-forward) + sym nil t) + (setq arg (1- arg)))) + +(defun shortdoc-next (&optional arg) + "Move point to the next function. +With prefix numeric argument ARG, do it that many times." + (interactive "p" shortdoc-mode) + (shortdoc--goto-section arg 'shortdoc-function)) + +(defun shortdoc-previous (&optional arg) + "Move point to the previous function. +With prefix numeric argument ARG, do it that many times." + (interactive "p" shortdoc-mode) + (shortdoc--goto-section arg 'shortdoc-function t) + (backward-char 1)) + +(defun shortdoc-next-section (&optional arg) + "Move point to the next section. +With prefix numeric argument ARG, do it that many times." + (interactive "p" shortdoc-mode) + (shortdoc--goto-section arg 'shortdoc-section)) + +(defun shortdoc-previous-section (&optional arg) + "Move point to the previous section. +With prefix numeric argument ARG, do it that many times." + (interactive "p" shortdoc-mode) + (shortdoc--goto-section arg 'shortdoc-section t) + (forward-line -2)) + +(defun shortdoc-copy-function-as-kill () + "Copy name of the function near point into the kill ring." + (interactive) + (save-excursion + (goto-char (pos-bol)) + (when-let* ((re (rx bol "(" (group (+ (not (in " )")))))) + (string + (and (or (looking-at re) + (re-search-backward re nil t)) + (match-string 1)))) + (set-text-properties 0 (length string) nil string) + (kill-new string) + (message string)))) + +(provide 'shortdoc) + +;;; shortdoc.el ends here From 82882db8edc00f8964e5d4561f9fce6a96b6c336 Mon Sep 17 00:00:00 2001 From: Jens Schmidt Date: Wed, 25 Mar 2026 18:05:10 +0100 Subject: [PATCH 3/4] Split up shortdoc functions and groups, fix their format Move shortdoc group definitions from shortdoc.el to a separate file shortdoc-doc.el. Document shortdoc group format in a future-proof way and guide package authors on how to use shortdoc groups across past Emacs versions. * lisp/emacs-lisp/shortdoc-doc.el: New file. * lisp/emacs-lisp/shortdoc.el: Document shortdoc group format in a future-proof way. Require 'shortdoc-doc'. (shortdoc--keyword-plist-p): New function. (shortdoc--check): Update to check the documented shortdoc group format. (shortdoc--groups, define-short-documentation-group): Pull out of autoloaded 'progn'. (define-short-documentation-group): Report errors in terms of byte compiler warnings. (alist, map, string, file-name, file, hash-table, list, symbol) (comparison, vector, regexp, sequence, buffer, overlay, process, number) (text-properties, keymaps): Move group to shortdoc-doc.el. (shortdoc): Move alias to after function. (shortdoc-add-function): Add argument checks. * doc/lispref/tips.texi (Documentation Group Tips): New section. * doc/lispref/elisp.texi (Top): * doc/lispref/tips.texi (Tips): Add references to it. * doc/lispref/help.texi (Documentation Groups): Ditto, and add some concept index entries. (bug#80297) --- doc/lispref/elisp.texi | 1 + doc/lispref/help.texi | 9 +- doc/lispref/tips.texi | 84 ++ lisp/emacs-lisp/shortdoc-doc.el | 450 +-------- lisp/emacs-lisp/shortdoc.el | 1644 +++---------------------------- 5 files changed, 229 insertions(+), 1959 deletions(-) diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index 656a422cf6e..9115b3a4691 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -1661,6 +1661,7 @@ Tips and Conventions * Compilation Tips:: Making compiled code run fast. * Warning Tips:: Turning off compiler warnings. * Documentation Tips:: Writing readable documentation strings. +* Documentation Group Tips:: Writing useful documentation groups. * Comment Tips:: Conventions for writing comments. * Library Headers:: Standard headers for library packages. diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi index 3261cf838f7..a9bc9221912 100644 --- a/doc/lispref/help.texi +++ b/doc/lispref/help.texi @@ -828,14 +828,16 @@ if the user types the help character again. @cindex documentation groups @cindex groups of functions @cindex function groups +@cindex shortdoc groups Emacs can list functions based on various groupings. For instance, @code{string-trim} and @code{mapconcat} are ``string'' functions, so -@kbd{M-x shortdoc RET string RET} will give an overview -of functions that operate on strings. +@kbd{M-x shortdoc RET string RET} will give an overview of these and +other functions that operate on strings. The documentation groups are created with the -@code{define-short-documentation-group} macro. +@code{define-short-documentation-group} macro. @xref{Documentation +Group Tips}, for how to write good documentation groups. @defmac define-short-documentation-group group &rest functions Define @var{group} as a group of functions, and provide short @@ -846,6 +848,7 @@ summaries of using those functions. The optional argument (@var{func} [@var{keyword} @var{val}]@dots{}) @end lisp +@cindex documentation group keywords The following keywords are recognized: @table @code diff --git a/doc/lispref/tips.texi b/doc/lispref/tips.texi index 7f22dc06ef2..2fbac9508d6 100644 --- a/doc/lispref/tips.texi +++ b/doc/lispref/tips.texi @@ -35,6 +35,7 @@ in batch mode, e.g., with a command run by @kbd{@w{M-x compile * Compilation Tips:: Making compiled code run fast. * Warning Tips:: Turning off compiler warnings. * Documentation Tips:: Writing readable documentation strings. +* Documentation Group Tips:: Writing useful documentation groups. * Comment Tips:: Conventions for writing comments. * Library Headers:: Standard headers for library packages. @end menu @@ -934,6 +935,89 @@ If you do not anticipate anyone editing your code with older Emacs versions, there is no need for this work-around. @end itemize +@node Documentation Group Tips +@section Tips for Documentation Groups +@cindex documentation groups, tips +@cindex tips for documentation groups + +@cindex documentation groups, compatibility + Documentation groups, available since Emacs 28, are useful to document +functions of Lisp packages based on various groupings +(@pxref{Documentation Groups}). This section gives some tips on how you +can define documentation groups in your Lisp package in a way such that +users of different Emacs versions can equally well use these groups. + +@itemize @bullet +@item +To define documentation groups for your own Lisp package across +different Emacs versions, you can use a boilerplate template along the +lines of the following to make your package compile and load without +errors: + +@smallexample +@group +;;; well-doc.el --- a well-documented package -*- lexical-binding: t; -*- + +@dots{} package header and contents @dots{} +@end group + +@group +;; Explicitly require shortdoc for Emacs 28, which does not have an +;; autoload for macro `define-short-documentation-group'. And for +;; Emacs 30, so that we can redefine `shortdoc--check' later. +(require 'shortdoc nil t) + +(eval-when-compile + + ;; Default macro `define-short-documentation-group' for Emacs 27 + ;; and older, which do not have the shortdoc feature at all. + (unless (fboundp 'define-short-documentation-group) + (defmacro define-short-documentation-group (&rest _))) + + ;; Disable too rigid shortdoc checks for Emacs 30, which let it + ;; error out on newer shortdoc keywords. + (when (eq emacs-major-version 30) + (fset 'shortdoc--check #'ignore))) +@end group + +@group +(define-short-documentation-group well-doc + @dots{}) + +;;; well-doc.el ends here +@end group +@end smallexample + +@findex define-short-documentation-group +If you do not intend to support some of the Emacs versions mentioned +above, you can safely omit the corresponding forms from the template. +If you intend to support only Emacs 31 and newer, you do not need any +of the above and can just use @code{define-short-documentation-group}. + +@item +@cindex documentation group keywords, compatibility +Newer Emacs versions might introduce newer documentation group features +and keywords. However, these features or keywords will never break the +display of a documentation group in older Emacs versions. Suppose you +use a hypothetical group keyword @code{:super-pretty-print}, available +in some future Emacs version, like this in your Lisp package +@file{well-doc.el}: + +@smallexample +@group +(define-short-documentation-group well-doc + (well-doc-foo + :eval (well-doc-foo) + :super-pretty-print t)) +@end group +@end smallexample + +That future Emacs version will then supposedly super-pretty-print the +example for function @code{well-doc-foo}. Older Emacs versions will +silently ignore keyword @code{:super-pretty-print} and show the example +according to their regular display rules. +@end itemize + @node Comment Tips @section Tips on Writing Comments @cindex comments, Lisp convention for diff --git a/lisp/emacs-lisp/shortdoc-doc.el b/lisp/emacs-lisp/shortdoc-doc.el index ea6910c60fc..40b98811bf0 100644 --- a/lisp/emacs-lisp/shortdoc-doc.el +++ b/lisp/emacs-lisp/shortdoc-doc.el @@ -1,4 +1,4 @@ -;;; shortdoc.el --- Short function summaries -*- lexical-binding: t -*- +;;; shortdoc-doc.el --- Builtin shortdoc groups -*- lexical-binding: t -*- ;; Copyright (C) 2020-2026 Free Software Foundation, Inc. @@ -22,119 +22,16 @@ ;;; Commentary: -;; This package lists functions based on various groupings. +;; This file defines builtin Emacs shortdoc groups. ;; -;; For instance, `string-trim' and `mapconcat' are `string' functions, -;; so `M-x shortdoc RET string RET' will give an overview of functions -;; that operate on strings. -;; -;; The documentation groups are created with the -;; `define-short-documentation-group' macro. +;; If a shortdoc group describes builtin functions, functions from +;; subr.el or simple.el or otherwise preloaded files, or functions from +;; different files, then you should probably define it in this file. +;; Otherwise, you might as well define the shortdoc group in the file +;; where the documented functions live, like treesit.el does it. ;;; Code: -(require 'seq) -(require 'text-property-search) -(eval-when-compile (require 'cl-lib)) - -(defgroup shortdoc nil - "Short documentation." - :group 'lisp) - -(defface shortdoc-heading - '((t :inherit variable-pitch :height 1.3 :weight bold)) - "Face used for a heading." - :version "28.1") - -(defface shortdoc-section - '((t :inherit variable-pitch)) - "Face used for a section.") - -;;;###autoload -(defun shortdoc--check (group functions) - (let ((keywords '( :no-manual :args :eval :no-eval :no-value :no-eval* - :result :result-string :eg-result :eg-result-string :doc))) - (dolist (f functions) - (when (consp f) - (dolist (x f) - (when (and (keywordp x) (not (memq x keywords))) - (error "Shortdoc %s function `%s': bad keyword `%s'" - group (car f) x))))))) - -;;;###autoload -(progn - (defvar shortdoc--groups nil) - - (defmacro define-short-documentation-group (group &rest functions) - "Add GROUP to the list of defined documentation groups. -FUNCTIONS is a list of elements on the form: - - (FUNC - :no-manual BOOL - :args ARGS - :eval EVAL - :no-eval EXAMPLE-FORM - :no-value EXAMPLE-FORM - :no-eval* EXAMPLE-FORM - :result RESULT-FORM - :result-string RESULT-STRING - :eg-result RESULT-FORM - :eg-result-string RESULT-STRING) - -FUNC is the function being documented. - -NO-MANUAL should be non-nil if FUNC isn't documented in the -manual. - -ARGS is optional list of function FUNC's arguments. FUNC's -signature is displayed automatically if ARGS is not present. -Specifying ARGS might be useful where you don't want to document -some of the uncommon arguments a function might have. - -While the `:no-manual' and `:args' property can be used for -any (FUNC ..) form, all of the other properties shown above -cannot be used simultaneously in such a form. - -Here are some common forms with examples of properties that go -together: - -1. Document a form or string, and its evaluated return value. - (FUNC - :eval EVAL) - -If EVAL is a string, it will be inserted as is, and then that -string will be `read' and evaluated. - -2. Document a form or string, but manually document its evaluation - result. The provided form will not be evaluated. - - (FUNC - :no-eval EXAMPLE-FORM - :result RESULT-FORM) ;Use `:result-string' if value is in string form - -Using `:no-value' is the same as using `:no-eval'. - -Use `:no-eval*' instead of `:no-eval' where the successful -execution of the documented form depends on some conditions. - -3. Document a form or string EXAMPLE-FORM. Also manually - document an example result. This result could be unrelated to - the documented form. - - (FUNC - :no-eval EXAMPLE-FORM - :eg-result RESULT-FORM) ;Use `:eg-result-string' if value is in string form - -A FUNC form can have any number of `:no-eval' (or `:no-value'), -`:no-eval*', `:result', `:result-string', `:eg-result' and -`:eg-result-string' properties." - (declare (indent defun)) - (shortdoc--check group functions) - `(progn - (setq shortdoc--groups (delq (assq ',group shortdoc--groups) - shortdoc--groups)) - (push (cons ',group ',functions) shortdoc--groups)))) - (define-short-documentation-group alist "Alist Basics" (assoc @@ -1626,335 +1523,6 @@ A FUNC form can have any number of `:no-eval' (or `:no-value'), (keymap-lookup :eval (keymap-lookup (current-global-map) "C-x x g"))) -;;;###autoload -(defun shortdoc-display-group (group &optional function same-window) - "Pop to a buffer with short documentation summary for functions in GROUP. -Interactively, prompt for GROUP. -If FUNCTION is non-nil, place point on the entry for FUNCTION (if any). -If SAME-WINDOW, don't pop to a new window." - (interactive (list (completing-read - "Group of functions for which to show summary: " - (mapcar #'car shortdoc--groups)))) - (when (stringp group) - (setq group (intern group))) - (unless (assq group shortdoc--groups) - (error "No such documentation group %s" group)) - (let ((buf (get-buffer-create (format "*Shortdoc %s*" group)))) - (shortdoc--insert-group-in-buffer group buf) - (funcall (if same-window - #'pop-to-buffer-same-window - #'pop-to-buffer) - buf)) - (goto-char (point-min)) - (when function - (text-property-search-forward 'shortdoc-function function t) - (beginning-of-line))) +(provide 'shortdoc-doc) -(defun shortdoc--insert-group-in-buffer (group &optional buf) - "Insert a short documentation summary for functions in GROUP in buffer BUF. -BUF defaults to the current buffer if nil or omitted." - (with-current-buffer (or buf (current-buffer)) - (let ((inhibit-read-only t) - (prev nil)) - (erase-buffer) - (shortdoc-mode) - (button-mode) - (mapc - (lambda (data) - (cond - ((stringp data) - (setq prev nil) - (unless (bobp) - (insert "\n")) - (insert (propertize - (substitute-command-keys data) - 'face 'shortdoc-heading - 'shortdoc-section t - 'outline-level 1)) - (insert (propertize - "\n\n" - 'face 'shortdoc-heading - 'shortdoc-section t))) - ;; There may be functions not yet defined in the data. - ((fboundp (car data)) - (when prev - (insert (make-separator-line) - ;; This helps with hidden outlines (bug#53981) - (propertize "\n" 'face '(:height 0)))) - (setq prev t) - (shortdoc--display-function data)))) - (cdr (assq group shortdoc--groups)))))) - -;;;###autoload -(defalias 'shortdoc #'shortdoc-display-group) - -(defun shortdoc--display-function (data) - (let ((function (pop data)) - (start-section (point)) - arglist-start) - ;; Function calling convention. - (insert (propertize "(" 'shortdoc-function function 'outline-level 2)) - (if (plist-get data :no-manual) - (insert-text-button - (symbol-name function) - 'face 'button - 'action (lambda (_) - (describe-function function)) - 'follow-link t - 'help-echo "mouse-1, RET: describe function") - (insert-text-button - (symbol-name function) - 'face 'button - 'action (lambda (_) - (info-lookup-symbol function 'emacs-lisp-mode)) - 'follow-link t - 'help-echo "mouse-1, RET: show \ -function's documentation in the Info manual")) - (setq arglist-start (point)) - (insert ")\n") - ;; Doc string. - (insert " " - (or (plist-get data :doc) - (car (split-string (or (documentation function) - "Error: missing docstring.") - "\n")))) - (insert "\n") - (add-face-text-property start-section (point) 'shortdoc-section t) - (let ((print-escape-newlines t) - (double-arrow (if (char-displayable-p ?β‡’) - "β‡’" - "=>")) - (single-arrow (if (char-displayable-p ?β†’) - "β†’" - "->")) - (start-example (point))) - (cl-loop for (type value) on data by #'cddr - do - (cl-case type - (:eval - (insert " ") - (if (stringp value) - (insert value) - (prin1 value (current-buffer))) - (insert "\n " double-arrow " ") - (let ((expr (if (stringp value) - (car (read-from-string value)) - value))) - (prin1 (eval expr) (current-buffer))) - (insert "\n")) - (:no-eval* - (if (stringp value) - (insert " " value "\n") - (insert " ") - (prin1 value (current-buffer))) - (insert "\n " single-arrow " " - (propertize "[it depends]" - 'face 'shortdoc-section) - "\n")) - (:no-value - (if (stringp value) - (insert " " value) - (insert " ") - (prin1 value (current-buffer))) - (insert "\n")) - (:no-eval - (if (stringp value) - (insert " " value) - (insert " ") - (prin1 value (current-buffer))) - (insert "\n")) - (:result - (insert " " double-arrow " ") - (prin1 value (current-buffer)) - (insert "\n")) - (:result-string - (insert " " double-arrow " ") - (princ value (current-buffer)) - (insert "\n")) - (:eg-result - (insert " e.g. " double-arrow " ") - (prin1 value (current-buffer)) - (insert "\n")) - (:eg-result-string - (insert " e.g. " double-arrow " ") - (princ value (current-buffer)) - (insert "\n")))) - (add-text-properties start-example (point) `(shortdoc-example ,function))) - ;; Insert the arglist after doing the evals, in case that's pulled - ;; in the function definition. - (save-excursion - (goto-char arglist-start) - (dolist (param (or (plist-get data :args) - (help-function-arglist function t))) - (insert " " (symbol-name param))) - (add-face-text-property arglist-start (point) 'shortdoc-section t)))) - -(defun shortdoc-function-examples (function) - "Return all shortdoc examples for FUNCTION. -The result is an alist with items of the form (GROUP . EXAMPLES), -where GROUP is a shortdoc group where FUNCTION appears, and -EXAMPLES is a string with the usage examples of FUNCTION defined -in GROUP. Return nil if FUNCTION is not a function or if it -doesn't has any shortdoc information." - (let ((groups (and (symbolp function) - (shortdoc-function-groups function))) - (examples nil)) - (mapc - (lambda (group) - (with-temp-buffer - (shortdoc--insert-group-in-buffer group) - (goto-char (point-min)) - (let ((match (text-property-search-forward - 'shortdoc-example function t))) - (push `(,group . ,(string-trim - (buffer-substring-no-properties - (prop-match-beginning match) - (prop-match-end match)))) - examples)))) - groups) - examples)) - -(defun shortdoc-help-fns-examples-function (function) - "Insert Emacs Lisp examples for FUNCTION into the current buffer. -You can add this function to the `help-fns-describe-function-functions' -hook to show examples of using FUNCTION in *Help* buffers produced -by \\[describe-function]." - (let* ((examples (shortdoc-function-examples function)) - (num-examples (length examples)) - (times 0)) - (dolist (example examples) - (when (zerop times) - (if (> num-examples 1) - (insert "\n Examples:\n\n") - ;; Some functions have more than one example per group. - ;; Count the number of arrows to know if we need to - ;; pluralize "Example". - (let* ((text (cdr example)) - (count 0) - (pos 0) - (end (length text)) - (double-arrow (if (char-displayable-p ?β‡’) - " β‡’" - " =>")) - (double-arrow-example (if (char-displayable-p ?β‡’) - " e.g. β‡’" - " e.g. =>")) - (single-arrow (if (char-displayable-p ?β†’) - " β†’" - " ->"))) - (while (and (< pos end) - (or (string-match double-arrow text pos) - (string-match double-arrow-example text pos) - (string-match single-arrow text pos))) - (setq count (1+ count) - pos (match-end 0))) - (if (> count 1) - (insert "\n Examples:\n\n") - (insert "\n Example:\n\n"))))) - (setq times (1+ times)) - (insert " ") - (insert (cdr example)) - (insert "\n\n")))) - -(defun shortdoc-function-groups (function) - "Return all shortdoc groups FUNCTION appears in." - (cl-loop for group in shortdoc--groups - when (assq function (cdr group)) - collect (car group))) - -(defun shortdoc-add-function (group section elem) - "Add ELEM to shortdoc GROUP in SECTION. -If GROUP doesn't exist, it will be created. -If SECTION doesn't exist, it will be added. - -ELEM is a Lisp form. See `define-short-documentation-group' for -details. - -Example: - - (shortdoc-add-function - \\='file \"Predicates\" - \\='(file-locked-p :no-eval (file-locked-p \"/tmp\")))" - (let ((glist (assq group shortdoc--groups))) - (unless glist - (setq glist (list group)) - (push glist shortdoc--groups)) - (let ((slist (member section glist))) - (unless slist - (setq slist (list section)) - (nconc glist slist)) - (while (and (cdr slist) - (not (stringp (cadr slist)))) - (setq slist (cdr slist))) - (setcdr slist (cons elem (cdr slist)))))) - -(defvar-keymap shortdoc-mode-map - :doc "Keymap for `shortdoc-mode'." - "n" #'shortdoc-next - "p" #'shortdoc-previous - "N" #'shortdoc-next-section - "P" #'shortdoc-previous-section - "C-c C-n" #'shortdoc-next-section - "C-c C-p" #'shortdoc-previous-section - "w" #'shortdoc-copy-function-as-kill) - -(define-derived-mode shortdoc-mode special-mode "shortdoc" - "Mode for shortdoc." - :interactive nil - (setq-local outline-search-function #'outline-search-level - outline-level (lambda () - (get-text-property (point) 'outline-level)))) - -(defun shortdoc--goto-section (arg sym &optional reverse) - (unless (natnump arg) - (setq arg 1)) - (while (> arg 0) - (funcall - (if reverse 'text-property-search-backward - 'text-property-search-forward) - sym nil t) - (setq arg (1- arg)))) - -(defun shortdoc-next (&optional arg) - "Move point to the next function. -With prefix numeric argument ARG, do it that many times." - (interactive "p" shortdoc-mode) - (shortdoc--goto-section arg 'shortdoc-function)) - -(defun shortdoc-previous (&optional arg) - "Move point to the previous function. -With prefix numeric argument ARG, do it that many times." - (interactive "p" shortdoc-mode) - (shortdoc--goto-section arg 'shortdoc-function t) - (backward-char 1)) - -(defun shortdoc-next-section (&optional arg) - "Move point to the next section. -With prefix numeric argument ARG, do it that many times." - (interactive "p" shortdoc-mode) - (shortdoc--goto-section arg 'shortdoc-section)) - -(defun shortdoc-previous-section (&optional arg) - "Move point to the previous section. -With prefix numeric argument ARG, do it that many times." - (interactive "p" shortdoc-mode) - (shortdoc--goto-section arg 'shortdoc-section t) - (forward-line -2)) - -(defun shortdoc-copy-function-as-kill () - "Copy name of the function near point into the kill ring." - (interactive) - (save-excursion - (goto-char (pos-bol)) - (when-let* ((re (rx bol "(" (group (+ (not (in " )")))))) - (string - (and (or (looking-at re) - (re-search-backward re nil t)) - (match-string 1)))) - (set-text-properties 0 (length string) nil string) - (kill-new string) - (message string)))) - -(provide 'shortdoc) - -;;; shortdoc.el ends here +;;; shortdoc-doc.el ends here diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el index ea6910c60fc..e8ba6ededc0 100644 --- a/lisp/emacs-lisp/shortdoc.el +++ b/lisp/emacs-lisp/shortdoc.el @@ -25,8 +25,8 @@ ;; This package lists functions based on various groupings. ;; ;; For instance, `string-trim' and `mapconcat' are `string' functions, -;; so `M-x shortdoc RET string RET' will give an overview of functions -;; that operate on strings. +;; so `M-x shortdoc RET string RET' will give an overview of these and +;; other functions that operate on strings. ;; ;; The documentation groups are created with the ;; `define-short-documentation-group' macro. @@ -50,23 +50,109 @@ '((t :inherit variable-pitch)) "Face used for a section.") -;;;###autoload -(defun shortdoc--check (group functions) - (let ((keywords '( :no-manual :args :eval :no-eval :no-value :no-eval* - :result :result-string :eg-result :eg-result-string :doc))) - (dolist (f functions) - (when (consp f) - (dolist (x f) - (when (and (keywordp x) (not (memq x keywords))) - (error "Shortdoc %s function `%s': bad keyword `%s'" - group (car f) x))))))) + +;; Almost all past Emacs versions (but see note on Emacs 30 below) +;; understand the following shortdoc group structure: +;; +;; (SYMBOL ;; shortdoc group name +;; (:group [:KEYWORD VALUE ...]) ;; group properties +;; STRING ;; shortdoc section title +;; (:section [:KEYWORD VALUE ...]) ;; section properties +;; +;; (SYMBOL ;; shortdoc item +;; [:KEYWORD VALUE ...]) ;; item properties +;; ([:item] FORM ;; generalized shortdoc item +;; [:KEYWORD VALUE ...])) ;; item properties +;; +;; Where: +;; - a group definition must contain at least one section title or item; +;; - group and section properties must occur at most once after the +;; group name and a section title, respectively; +;; - the leading `:item' keyword of a generalized shortdoc item may be +;; omitted if the shortdoc group is not intended to be used on Emacs +;; versions older than Emacs 32; +;; - the group, secion, or item properties may be empty. +;; +;; That does not mean that any such shortdoc group is meaningful. And +;; that does not mean that past Emacs version actually use all the bits +;; available in such a definition. But they will not error out when +;; processing a definition with the format layed out above, they will +;; simply silently ignore those bits unknown to them (specifically +;; unknown keywords) and attempt to make the best out of the rest. +;; +;; Why is this important? Because it gives package authors a guarantee +;; that they can use shortdoc features of newer Emacs versions without +;; older Emacs versions breaking on them. +;; +;; So Emacs developers, please +;; +;; - stick to above structure when extending shortdoc.el (so that past +;; Emacs versions can grok your extensions without breaking); and +;; +;; - do not impose any additional restrictions on the format described +;; above and on the allowed keywords (so that you do not limit the +;; options of future Emacs versions). +;; +;; Emacs 30, for example, had introduced some restrictions on item +;; property keywords. As a result, we need that hack mentioned in the +;; "boilerplate template for Emacs package authors" above. + +(defun shortdoc--keyword-plist-p (object) + "Return non-nil if OBJECT is a plist with keywords as property names." + (let ((ok (proper-list-p object))) + (while (and ok object) + (setq ok (and (keywordp (car object)) (cdr object)) + object (cddr object))) + ok)) + +(defun shortdoc--check (group definition) + "Ensure that (GROUP DEFINITION) is a valid shortdoc group definition. +Signal an error if that is not the case." + (unless (symbolp group) + (signal 'wrong-type-argument (list 'symbolp group))) + (unless (proper-list-p definition) + (signal 'wrong-type-argument (list 'proper-list-p definition))) + (let ((has-content nil) + entry keyword type + (prev-type 'group-name)) + (while definition + (setq entry (car definition) + keyword (car-safe entry) + type (cond + ((and (eq keyword :group) + (shortdoc--keyword-plist-p (cdr entry))) + 'group-properties) + ((stringp entry) 'section-title) + ((and (eq keyword :section) + (shortdoc--keyword-plist-p (cdr entry))) + 'section-properties) + ((and (eq keyword :item) + (shortdoc--keyword-plist-p entry)) + 'item-definition) + ((and (consp entry) + (shortdoc--keyword-plist-p (cdr entry))) + 'item-definition) + (t 'invalid))) + (cond ((memq type '(section-title item-definition)) + (setq has-content t)) + ((and (eq type 'group-properties) + (eq prev-type 'group-name))) + ((and (eq type 'section-properties) + (eq prev-type 'section-title))) + (t + (error "Shortdoc group %s with invalid entry %S" + group entry))) + (setq prev-type type + definition (cdr definition))) + (unless has-content + (error "Shortdoc group %s without content" group)))) ;;;###autoload -(progn - (defvar shortdoc--groups nil) +(defvar shortdoc--groups nil) - (defmacro define-short-documentation-group (group &rest functions) - "Add GROUP to the list of defined documentation groups. +;;;###autoload +(defmacro define-short-documentation-group (group &rest functions) + "Add GROUP to the list of defined documentation groups. FUNCTIONS is a list of elements on the form: (FUNC @@ -128,1504 +214,28 @@ execution of the documented form depends on some conditions. A FUNC form can have any number of `:no-eval' (or `:no-value'), `:no-eval*', `:result', `:result-string', `:eg-result' and `:eg-result-string' properties." - (declare (indent defun)) - (shortdoc--check group functions) - `(progn - (setq shortdoc--groups (delq (assq ',group shortdoc--groups) - shortdoc--groups)) - (push (cons ',group ',functions) shortdoc--groups)))) + (declare (indent defun)) + (let ((err + (condition-case err + (progn (shortdoc--check group functions) nil) + (error err))) + (exp + `(progn + (setq shortdoc--groups (delq (assq ',group shortdoc--groups) + shortdoc--groups)) + (push (cons ',group ',functions) shortdoc--groups)))) + (if (null err) + exp + (macroexp-warn-and-return + (error-message-string err) exp nil t)))) -(define-short-documentation-group alist - "Alist Basics" - (assoc - :eval (assoc 'foo '((foo . bar) (zot . baz)))) - (rassoc - :eval (rassoc 'bar '((foo . bar) (zot . baz)))) - (assq - :eval (assq 'foo '((foo . bar) (zot . baz)))) - (rassq - :eval (rassq 'bar '((foo . bar) (zot . baz)))) - (assoc-string - :eval (assoc-string "foo" '(("foo" . "bar") ("zot" "baz")))) - "Manipulating Alists" - (assoc-delete-all - :eval (assoc-delete-all "b" (list '("a" . a) '("b" . b) '("b" . c)))) - (assq-delete-all - :eval (assq-delete-all 2 (list '(1 . a) '(2 . b) '(2 . c)))) - (rassq-delete-all - :eval (rassq-delete-all 'b (list '(1 . a) '(2 . b) '(2 . c)))) - (alist-get - :eval (let ((foo '((bar . baz)))) - (setf (alist-get 'bar foo) 'zot) - foo)) - "Misc" - (assoc-default - :eval (assoc-default "foobar" '(("foo" . baz)) #'string-match)) - (copy-alist - :eval (let* ((old '((foo . bar))) - (new (copy-alist old))) - (eq old new))) - ;; FIXME: Outputs "\.rose" for the symbol `.rose'. It would be - ;; better if that could be cleaned up. - (let-alist - :eval (let ((colors '((rose . red) - (lily . white)))) - (let-alist colors - (if (eq .rose 'red) - .lily))))) - -(define-short-documentation-group map - "Map Basics" - (mapp - :eval (mapp (list 'bar 1 'foo 2 'baz 3)) - :eval (mapp (list '(bar . 1) '(foo . 2) '(baz . 3))) - :eval (mapp [bar foo baz]) - :eval (mapp "this is a string") - :eval (mapp #s(hash-table data (bar 1 foo 2 baz 3))) - :eval (mapp '()) - :eval (mapp nil) - :eval (mapp (make-char-table 'shortdoc-test))) - (map-empty-p - :args (map) - :eval (map-empty-p nil) - :eval (map-empty-p []) - :eval (map-empty-p '())) - (map-elt - :args (map key) - :eval (map-elt (list 'bar 1 'foo 2 'baz 3) 'foo) - :eval (map-elt (list '(bar . 1) '(foo . 2) '(baz . 3)) 'foo) - :eval (map-elt [bar foo baz] 1) - :eval (map-elt #s(hash-table data (bar 1 foo 2 baz 3)) 'foo)) - (map-contains-key - :args (map key) - :eval (map-contains-key (list 'bar 1 'foo 2 'baz 3) 'foo) - :eval (map-contains-key (list '(bar . 1) '(foo . 2) '(baz . 3)) 'foo) - :eval (map-contains-key [bar foo baz] 1) - :eval (map-contains-key #s(hash-table data (bar 1 foo 2 baz 3)) 'foo)) - (map-put! - (map key value) - :eval -"(let ((map (list 'bar 1 'baz 3))) - (map-put! map 'foo 2) - map)" -;; This signals map-not-inplace when used in shortdoc.el :-( -;; :eval -;; "(let ((map (list '(bar . 1) '(baz . 3)))) -;; (map-put! map 'foo 2) -;; map)" - :eval -"(let ((map [bar bot baz])) - (map-put! map 1 'foo) - map)" - :eval -"(let ((map #s(hash-table data (bar 1 baz 3)))) - (map-put! map 'foo 2) - map)") - (map-insert - :args (map key value) - :eval (map-insert (list 'bar 1 'baz 3 'foo 7) 'foo 2) - :eval (map-insert (list '(bar . 1) '(baz . 3) '(foo . 7)) 'foo 2) - :eval (map-insert [bar bot baz] 1 'foo) - :eval (map-insert #s(hash-table data (bar 1 baz 3 foo 7)) 'foo 2)) - (map-delete - :args (map key) - :eval (map-delete (list 'bar 1 'foo 2 'baz 3) 'foo) - :eval (map-delete (list '(bar . 1) '(foo . 2) '(baz . 3)) 'foo) - :eval (map-delete [bar foo baz] 1) - :eval (map-delete #s(hash-table data (bar 1 foo 2 baz 3)) 'foo)) - (map-keys - :eval (map-keys (list 'bar 1 'foo 2 'baz 3)) - :eval (map-keys (list '(bar . 1) '(foo . 2) '(baz . 3))) - :eval (map-keys [bar foo baz]) - :eval (map-keys #s(hash-table data (bar 1 foo 2 baz 3)))) - (map-values - :args (map) - :eval (map-values (list 'bar 1 'foo 2 'baz 3)) - :eval (map-values (list '(bar . 1) '(foo . 2) '(baz . 3))) - :eval (map-values [bar foo baz]) - :eval (map-values #s(hash-table data (bar 1 foo 2 baz 3)))) - (map-pairs - :eval (map-pairs (list 'bar 1 'foo 2 'baz 3)) - :eval (map-pairs (list '(bar . 1) '(foo . 2) '(baz . 3))) - :eval (map-pairs [bar foo baz]) - :eval (map-pairs #s(hash-table data (bar 1 foo 2 baz 3)))) - (map-length - :args (map) - :eval (map-length (list 'bar 1 'foo 2 'baz 3)) - :eval (map-length (list '(bar . 1) '(foo . 2) '(baz . 3))) - :eval (map-length [bar foo baz]) - :eval (map-length #s(hash-table data (bar 1 foo 2 baz 3)))) - (map-copy - :args (map) - :eval (map-copy (list 'bar 1 'foo 2 'baz 3)) - :eval (map-copy (list '(bar . 1) '(foo . 2) '(baz . 3))) - :eval (map-copy [bar foo baz]) - :eval (map-copy #s(hash-table data (bar 1 foo 2 baz 3)))) - "Doing things to maps and their contents" - (map-apply - :args (function map) - :eval (map-apply #'+ (list '(1 . 2) '(3 . 4)))) - (map-do - :args (function map) - :eval -"(let ((map (list '(1 . 1) '(2 . 3))) - acc) - (map-do (lambda (k v) (push (+ k v) acc)) map) - (nreverse acc))") - (map-keys-apply - :eval (map-keys-apply #'1+ (list '(1 . 2) '(3 . 4)))) - (map-values-apply - :args (function map) - :eval (map-values-apply #'1+ (list '(1 . 2) '(3 . 4)))) - (map-filter - :eval (map-filter (lambda (k _) (oddp k)) (list '(1 . 2) '(4 . 6))) - :eval (map-filter (lambda (k v) (evenp (+ k v))) (list '(1 . 2) '(4 . 6)))) - (map-remove - :eval (map-remove (lambda (k _) (oddp k)) (list '(1 . 2) '(4 . 6))) - :eval (map-remove (lambda (k v) (evenp (+ k v))) (list '(1 . 2) '(4 . 6)))) - (map-some - :eval (map-some (lambda (k _) (oddp k)) (list '(1 . 2) '(4 . 6))) - :eval (map-some (lambda (k v) (evenp (+ k v))) (list '(1 . 2) '(4 . 6)))) - (map-every-p - :eval (map-every-p (lambda (k _) (oddp k)) (list '(1 . 2) '(4 . 6))) - :eval (map-every-p (lambda (k v) (evenp (+ k v))) (list '(1 . 3) '(4 . 6)))) - "Combining and changing maps" - (map-merge - :eval (map-merge 'alist '(1 2 3 4) #s(hash-table data (5 6 7 8))) - :eval (map-merge 'list '(1 2 3 4) #s(hash-table data (5 6 7 8))) - :eval (map-merge 'plist '(1 2 3 4) #s(hash-table data (5 6 7 8))) - :eval (map-merge 'hash-table '(1 2 3 4) #s(hash-table data (5 6 7 8)))) - (map-merge-with - :eval (map-merge-with 'alist #'max '(1 2 3 4) #s(hash-table data (1 1 3 5))) - :eval (map-merge-with 'alist #'min '(1 2 3 4) #s(hash-table data (1 1 3 5))) - :eval (map-merge-with 'hash-table #'min '(1 2 3 4) #s(hash-table data (1 1 3 5)))) - (map-into - :args (map type) - :eval (map-into #s(hash-table data '(5 6 7 8)) 'list) - :eval (map-into '((5 . 6) (7 . 8)) 'plist) - :eval (map-into '((5 . 6) (7 . 8)) 'hash-table))) - -(define-short-documentation-group string - "Making Strings" - (make-string - :args (length init) - :eval "(make-string 5 ?x)") - (string - :eval "(string ?a ?b ?c)") - (concat - :eval (concat "foo" "bar" "zot")) - (string-join - :no-manual t - :eval (string-join '("foo" "bar" "zot") " ")) - (mapconcat - :eval (mapconcat (lambda (a) (concat "[" a "]")) - '("foo" "bar" "zot") " ")) - (string-pad - :eval (string-pad "foo" 5) - :eval (string-pad "foobar" 5) - :eval (string-pad "foo" 5 ?- t)) - (mapcar - :eval (mapcar #'identity "123")) - (format - :eval (format "This number is %d" 4)) - "Manipulating Strings" - (substring - :eval (substring "abcde" 1 3) - :eval (substring "abcde" 2) - :eval (substring "abcde" 1 -1) - :eval (substring "abcde" -4 4)) - (string-limit - :eval (string-limit "foobar" 3) - :eval (string-limit "foobar" 3 t) - :eval (string-limit "foobar" 10) - :eval (string-limit "foε₯½" 3 nil 'utf-8)) - (truncate-string-to-width - :eval (truncate-string-to-width "foobar" 3) - :eval (truncate-string-to-width "δ½ ε₯½bar" 5)) - (split-string - :eval (split-string "foo bar") - :eval (split-string "|foo|bar|" "|") - :eval (split-string "|foo|bar|" "|" t)) - (split-string-and-unquote - :eval (split-string-and-unquote "foo \"bar zot\"")) - (split-string-shell-command - :eval (split-string-shell-command "ls /tmp/'foo bar'")) - (string-lines - :eval (string-lines "foo\n\nbar") - :eval (string-lines "foo\n\nbar" t)) - (string-replace - :eval (string-replace "foo" "bar" "foozot")) - (replace-regexp-in-string - :eval (replace-regexp-in-string "[a-z]+" "_" "*foo*")) - (string-trim - :args (string) - :doc "Trim STRING of leading and trailing white space." - :eval (string-trim " foo ")) - (string-trim-left - :eval (string-trim-left "oofoo" "o+")) - (string-trim-right - :eval (string-trim-right "barkss" "s+")) - (string-truncate-left - :no-manual t - :eval (string-truncate-left "longstring" 8)) - (string-remove-suffix - :no-manual t - :eval (string-remove-suffix "bar" "foobar")) - (string-remove-prefix - :no-manual t - :eval (string-remove-prefix "foo" "foobar")) - (string-chop-newline - :eval (string-chop-newline "foo\n")) - (string-clean-whitespace - :eval (string-clean-whitespace " foo bar ")) - (string-fill - :eval (string-fill "Three short words" 12) - :eval (string-fill "Long-word" 3)) - (reverse - :eval (reverse "foo")) - (substring-no-properties - :eval (substring-no-properties (propertize "foobar" 'face 'bold) 0 3)) - (try-completion - :eval (try-completion "foo" '("foobar" "foozot" "gazonk"))) - "Unicode Strings" - (string-glyph-split - :eval (string-glyph-split "Hello, πŸ‘ΌπŸ»πŸ§‘πŸΌβ€πŸ€β€πŸ§‘πŸ»")) - (string-glyph-compose - :eval (string-glyph-compose "Å")) - (string-glyph-decompose - :eval (string-glyph-decompose "β„«")) - "Predicates for Strings" - (string-equal - :eval (string-equal "abc" "abc") - :eval (string-equal "abc" "ABC")) - (string-equal-ignore-case - :eval (string-equal-ignore-case "foo" "FOO")) - (equal - :eval (equal "foo" "foo")) - (cl-equalp - :eval (cl-equalp "Foo" "foo")) - (stringp - :eval (stringp "a") - :eval (stringp 'a) - :eval "(stringp ?a)") - (string-or-null-p - :eval (string-or-null-p "a") - :eval (string-or-null-p nil)) - (char-or-string-p - :eval "(char-or-string-p ?a)" - :eval (char-or-string-p "a")) - (string-empty-p - :no-manual t - :eval (string-empty-p "")) - (string-blank-p - :no-manual t - :eval (string-blank-p " \n")) - (string-lessp - :eval (string-lessp "abc" "def") - :eval (string-lessp "pic4.png" "pic32.png") - :eval (string-lessp "1.1" "1.2")) - (string-greaterp - :eval (string-greaterp "foo" "bar")) - (string-version-lessp - :eval (string-version-lessp "pic4.png" "pic32.png") - :eval (string-version-lessp "1.9.3" "1.10.2")) - (string-collate-lessp - :eval (string-collate-lessp "abc" "abd")) - (string-prefix-p - :eval (string-prefix-p "foo" "foobar")) - (string-suffix-p - :eval (string-suffix-p "bar" "foobar")) - "Case Manipulation" - (upcase - :eval (upcase "foo")) - (downcase - :eval (downcase "FOObar")) - (capitalize - :eval (capitalize "foo bar zot")) - (upcase-initials - :eval (upcase-initials "The CAT in the hAt")) - "Converting Strings" - (string-to-number - :eval (string-to-number "42") - :eval (string-to-number "deadbeef" 16) - :eval (string-to-number "2.5e+03")) - (number-to-string - :eval (number-to-string 42)) - (char-uppercase-p - :eval "(char-uppercase-p ?A)" - :eval "(char-uppercase-p ?a)") - "Data About Strings" - (length - :eval (length "foo") - :eval (length "avocado: πŸ₯‘")) - (string-width - :eval (string-width "foo") - :eval (string-width "avocado: πŸ₯‘")) - (string-pixel-width - :eval (string-pixel-width "foo") - :eval (string-pixel-width "avocado: πŸ₯‘")) - (string-search - :eval (string-search "bar" "foobarzot")) - (assoc-string - :eval (assoc-string "foo" '(("a" 1) (foo 2)))) - (seq-position - :eval "(seq-position \"foobarzot\" ?z)")) - -(define-short-documentation-group file-name - "File Name Manipulation" - (file-name-directory - :eval (file-name-directory "/tmp/foo") - :eval (file-name-directory "/tmp/foo/")) - (file-name-nondirectory - :eval (file-name-nondirectory "/tmp/foo") - :eval (file-name-nondirectory "/tmp/foo/")) - (file-name-sans-versions - :args (filename) - :eval (file-name-sans-versions "/tmp/foo~")) - (file-name-extension - :eval (file-name-extension "/tmp/foo.txt")) - (file-name-sans-extension - :eval (file-name-sans-extension "/tmp/foo.txt")) - (file-name-with-extension - :eval (file-name-with-extension "foo.txt" "bin") - :eval (file-name-with-extension "foo" "bin")) - (file-name-base - :eval (file-name-base "/tmp/foo.txt")) - (file-relative-name - :eval (file-relative-name "/tmp/foo" "/tmp")) - (file-name-split - :eval (file-name-split "/tmp/foo") - :eval (file-name-split "foo/bar")) - (make-temp-name - :eval (make-temp-name "/tmp/foo-")) - (file-name-concat - :eval (file-name-concat "/tmp/" "foo") - :eval (file-name-concat "/tmp" "foo") - :eval (file-name-concat "/tmp" "foo" "bar/" "zot") - :eval (file-name-concat "/tmp" "~")) - (expand-file-name - :eval (expand-file-name "foo" "/tmp/") - :eval (expand-file-name "foo" "/tmp///") - :eval (expand-file-name "foo" "/tmp/foo/.././") - :eval (expand-file-name "~" "/tmp/")) - (substitute-in-file-name - :eval (substitute-in-file-name "$HOME/foo")) - "Directory Functions" - (file-name-as-directory - :eval (file-name-as-directory "/tmp/foo")) - (directory-file-name - :eval (directory-file-name "/tmp/foo/")) - (abbreviate-file-name - :no-eval (abbreviate-file-name "/home/some-user") - :eg-result "~some-user") - (file-name-parent-directory - :eval (file-name-parent-directory "/foo/bar") - :eval (file-name-parent-directory "/foo/") - :eval (file-name-parent-directory "foo/bar") - :eval (file-name-parent-directory "foo")) - "Quoted File Names" - (file-name-quote - :args (name) - :eval (file-name-quote "/tmp/foo")) - (file-name-unquote - :args (name) - :eval (file-name-unquote "/:/tmp/foo")) - "Predicates" - (file-name-absolute-p - :eval (file-name-absolute-p "/tmp/foo") - :eval (file-name-absolute-p "foo")) - (directory-name-p - :eval (directory-name-p "/tmp/foo/")) - (file-name-quoted-p - :eval (file-name-quoted-p "/:/tmp/foo"))) - -(define-short-documentation-group file - "Inserting Contents" - (insert-file-contents - :no-eval (insert-file-contents "/tmp/foo") - :eg-result ("/tmp/foo" 6)) - (insert-file-contents-literally - :no-eval (insert-file-contents-literally "/tmp/foo") - :eg-result ("/tmp/foo" 6)) - (find-file - :no-eval (find-file "/tmp/foo") - :eg-result-string "#") - "Predicates" - (file-symlink-p - :no-eval (file-symlink-p "/tmp/foo") - :eg-result t) - (file-directory-p - :no-eval (file-directory-p "/tmp") - :eg-result t) - (file-regular-p - :no-eval (file-regular-p "/tmp/foo") - :eg-result t) - (file-exists-p - :no-eval (file-exists-p "/tmp/foo") - :eg-result t) - (file-readable-p - :no-eval (file-readable-p "/tmp/foo") - :eg-result t) - (file-writable-p - :no-eval (file-writable-p "/tmp/foo") - :eg-result t) - (file-accessible-directory-p - :no-eval (file-accessible-directory-p "/tmp") - :eg-result t) - (file-executable-p - :no-eval (file-executable-p "/bin/cat") - :eg-result t) - (file-newer-than-file-p - :no-eval (file-newer-than-file-p "/tmp/foo" "/tmp/bar") - :eg-result nil) - (file-has-changed-p - :no-eval (file-has-changed-p "/tmp/foo") - :eg-result t) - (file-equal-p - :no-eval (file-equal-p "/tmp/foo" "/tmp/bar") - :eg-result nil) - (file-in-directory-p - :no-eval (file-in-directory-p "/tmp/foo" "/tmp/") - :eg-result t) - (file-locked-p - :no-eval (file-locked-p "/tmp/foo") - :eg-result nil) - "Information" - (file-attributes - :no-eval* (file-attributes "/tmp")) - (file-truename - :no-eval (file-truename "/tmp/foo/bar") - :eg-result "/tmp/foo/zot") - (file-chase-links - :no-eval (file-chase-links "/tmp/foo/bar") - :eg-result "/tmp/foo/zot") - (vc-responsible-backend - :args (file &optional no-error) - :no-eval (vc-responsible-backend "/src/foo/bar.c") - :eg-result Git) - (file-acl - :no-eval (file-acl "/tmp/foo") - :eg-result "user::rw-\ngroup::r--\nother::r--\n") - (file-extended-attributes - :no-eval* (file-extended-attributes "/tmp/foo")) - (file-selinux-context - :no-eval* (file-selinux-context "/tmp/foo")) - (locate-file - :no-eval (locate-file "syslog" '("/var/log" "/usr/bin")) - :eg-result "/var/log/syslog") - (executable-find - :no-eval (executable-find "ls") - :eg-result "/usr/bin/ls") - "Creating" - (make-temp-file - :no-eval (make-temp-file "/tmp/foo-") - :eg-result "/tmp/foo-ZcXFMj") - (make-nearby-temp-file - :no-eval (make-nearby-temp-file "/tmp/foo-") - :eg-result "/tmp/foo-xe8iON") - (write-region - :no-value (write-region (point-min) (point-max) "/tmp/foo")) - "Directories" - (make-directory - :no-value (make-directory "/tmp/bar/zot/" t)) - (directory-files - :no-eval (directory-files "/tmp/") - :eg-result ("." ".." ".ICE-unix" ".Test-unix")) - (directory-files-recursively - :no-eval (directory-files-recursively "/tmp/" "\\.png\\'") - :eg-result ("/tmp/foo.png" "/tmp/zot.png" "/tmp/bar/foobar.png")) - (directory-files-and-attributes - :no-eval* (directory-files-and-attributes "/tmp/foo")) - (file-expand-wildcards - :no-eval (file-expand-wildcards "/tmp/*.png") - :eg-result ("/tmp/foo.png" "/tmp/zot.png") - :no-eval (file-expand-wildcards "/*/foo.png") - :eg-result ("/tmp/foo.png" "/var/foo.png")) - (locate-dominating-file - :no-eval (locate-dominating-file "foo.png" "/tmp/foo/bar/zot") - :eg-result "/tmp/foo.png") - (copy-directory - :no-value (copy-directory "/tmp/bar/" "/tmp/barcopy")) - (delete-directory - :no-value (delete-directory "/tmp/bar/")) - "File Operations" - (rename-file - :no-value (rename-file "/tmp/foo" "/tmp/newname")) - (copy-file - :no-value (copy-file "/tmp/foo" "/tmp/foocopy")) - (delete-file - :no-value (delete-file "/tmp/foo")) - (make-empty-file - :no-value (make-empty-file "/tmp/foo")) - (make-symbolic-link - :no-value (make-symbolic-link "/tmp/foo" "/tmp/foosymlink")) - (add-name-to-file - :no-value (add-name-to-file "/tmp/foo" "/tmp/bar")) - (set-file-modes - :no-value "(set-file-modes \"/tmp/foo\" #o644)") - (set-file-times - :no-value (set-file-times "/tmp/foo")) - "File Modes" - (set-default-file-modes - :no-value "(set-default-file-modes #o755)") - (default-file-modes - :no-eval (default-file-modes) - :eg-result-string "#o755") - (file-modes-symbolic-to-number - :no-eval (file-modes-symbolic-to-number "a+r") - :eg-result-string "#o444") - (file-modes-number-to-symbolic - :eval "(file-modes-number-to-symbolic #o444)") - (set-file-extended-attributes - :no-eval (set-file-extended-attributes - "/tmp/foo" '((acl . "group::rxx"))) - :eg-result t) - (set-file-selinux-context - :no-eval (set-file-selinux-context - "/tmp/foo" '(unconfined_u object_r user_home_t s0)) - :eg-result t) - (set-file-acl - :no-eval (set-file-acl "/tmp/foo" "group::rxx") - :eg-result t)) - -(define-short-documentation-group hash-table - "Hash Table Basics" - (make-hash-table - :no-eval (make-hash-table) - :result-string "#s(hash-table ...)") - (puthash - :no-eval (puthash 'key "value" table)) - (gethash - :no-eval (gethash 'key table) - :eg-result "value") - (remhash - :no-eval (remhash 'key table) - :result nil) - (clrhash - :no-eval (clrhash table) - :result-string "#s(hash-table ...)") - (maphash - :no-eval (maphash (lambda (key value) (message value)) table) - :result nil) - "Other Hash Table Functions" - (hash-table-p - :eval (hash-table-p 123)) - (hash-table-contains-p - :no-eval (hash-table-contains-p 'key table)) - (copy-hash-table - :no-eval (copy-hash-table table) - :result-string "#s(hash-table ...)") - (hash-table-count - :no-eval (hash-table-count table) - :eg-result 15)) - -(define-short-documentation-group list - "Making Lists" - (make-list - :eval (make-list 5 'a)) - (cons - :eval (cons 1 '(2 3 4))) - (list - :eval (list 1 2 3)) - (number-sequence - :eval (number-sequence 5 8)) - (ensure-list - :eval (ensure-list "foo") - :eval (ensure-list '(1 2 3)) - :eval (ensure-list '(1 . 2))) - (ensure-proper-list - :eval (ensure-proper-list "foo") - :eval (ensure-proper-list '(1 2 3)) - :eval (ensure-proper-list '(1 . 2))) - "Operations on Lists" - (append - :eval (append '("foo" "bar") '("zot"))) - (copy-tree - :eval (copy-tree '(1 (2 3) 4))) - (flatten-tree - :eval (flatten-tree '(1 (2 3) 4))) - (car - :eval (car '(one two three)) - :eval (car '(one . two)) - :eval (car nil)) - (cdr - :eval (cdr '(one two three)) - :eval (cdr '(one . two)) - :eval (cdr nil)) - (last - :eval (last '(one two three))) - (butlast - :eval (butlast '(one two three))) - (nbutlast - :eval (nbutlast (list 'one 'two 'three))) - (nth - :eval (nth 1 '(one two three))) - (nthcdr - :eval (nthcdr 1 '(one two three))) - (take - :eval (take 3 '(one two three four))) - (ntake - :eval (ntake 3 (list 'one 'two 'three 'four))) - (take-while - :eval (take-while #'numberp '(1 2 three 4 five))) - (drop-while - :eval (drop-while #'numberp '(1 2 three 4 five))) - (any - :eval (any #'symbolp '(1 2 three 4 five))) - (all - :eval (all #'symbolp '(one 2 three)) - :eval (all #'symbolp '(one two three))) - (elt - :eval (elt '(one two three) 1)) - (car-safe - :eval (car-safe '(one two three))) - (cdr-safe - :eval (cdr-safe '(one two three))) - (push - :no-eval* (push 'a list)) - (pop - :no-eval* (pop list)) - (setcar - :no-eval (setcar list 'c) - :result c) - (setcdr - :no-eval (setcdr list (list c)) - :result '(c)) - (nconc - :eval (nconc (list 1) (list 2 3 4))) - (delq - :eval (delq 'a (list 'a 'b 'c 'd))) - (delete - :eval (delete 2 (list 1 2 3 4)) - :eval (delete "a" (list "a" "b" "c" "d"))) - (remq - :eval (remq 'b '(a b c))) - (remove - :eval (remove 2 '(1 2 3 4)) - :eval (remove "a" '("a" "b" "c" "d"))) - (delete-dups - :eval (delete-dups (list 1 2 4 3 2 4))) - "Mapping Over Lists" - (mapcar - :eval (mapcar #'list '(1 2 3))) - (mapcan - :eval (mapcan #'list '(1 2 3))) - (mapc - :eval (mapc #'insert '("1" "2" "3"))) - (seq-reduce - :eval (seq-reduce #'+ '(1 2 3) 0)) - (mapconcat - :eval (mapconcat #'identity '("foo" "bar") "|")) - "Predicates" - (listp - :eval (listp '(1 2 3)) - :eval (listp nil) - :eval (listp '(1 . 2))) - (consp - :eval (consp '(1 2 3)) - :eval (consp nil)) - (proper-list-p - :eval (proper-list-p '(1 2 3)) - :eval (proper-list-p nil) - :eval (proper-list-p '(1 . 2))) - (null - :eval (null nil)) - (atom - :eval (atom 'a)) - (nlistp - :eval (nlistp '(1 2 3)) - :eval (nlistp t) - :eval (nlistp '(1 . 2))) - "Finding Elements" - (memq - :eval (memq 'b '(a b c))) - (memql - :eval (memql 2.0 '(1.0 2.0 3.0))) - (member - :eval (member 2 '(1 2 3)) - :eval (member "b" '("a" "b" "c"))) - (member-ignore-case - :eval (member-ignore-case "foo" '("bar" "Foo" "zot"))) - "Association Lists" - (assoc - :eval (assoc "b" '(("a" . 1) ("b" . 2)))) - (rassoc - :eval (rassoc "b" '((1 . "a") (2 . "b")))) - (assq - :eval (assq 'b '((a . 1) (b . 2)))) - (rassq - :eval (rassq 'b '((1 . a) (2 . b)))) - (assoc-string - :eval (assoc-string "foo" '(("a" 1) (foo 2)))) - (alist-get - :eval (alist-get 2 '((1 . a) (2 . b)))) - (assoc-default - :eval (assoc-default 2 '((1 . a) (2 . b) #'=))) - (copy-alist - :eval (copy-alist '((1 . a) (2 . b)))) - (assoc-delete-all - :eval (assoc-delete-all "b" (list '("a" . a) '("b" . b) '("b" . c)))) - (assq-delete-all - :eval (assq-delete-all 2 (list '(1 . a) '(2 . b) '(2 . c)))) - (rassq-delete-all - :eval (rassq-delete-all 'b (list '(1 . a) '(2 . b) '(2 . c)))) - "Property Lists" - (plist-get - :eval (plist-get '(a 1 b 2 c 3) 'b)) - (plist-put - :no-eval (setq plist (plist-put plist 'd 4)) - :eg-result (a 1 b 2 c 3 d 4)) - (plist-member - :eval (plist-member '(a 1 b 2 c 3) 'b)) - "Data About Lists" - (length - :eval (length '(a b c))) - (length< - :eval (length< '(a b c) 1)) - (length> - :eval (length> '(a b c) 1)) - (length= - :eval (length= '(a b c) 3)) - (safe-length - :eval (safe-length '(a b c)))) - -(define-short-documentation-group symbol - "Making symbols" - (intern - :eval (intern "abc")) - (intern-soft - :eval (intern-soft "list") - :eval (intern-soft "Phooey!")) - (make-symbol - :eval (make-symbol "abc")) - (gensym - :no-eval (gensym) - :eg-result g37) - "Comparing symbols" - (eq - :eval (eq 'abc 'abc) - :eval (eq 'abc 'abd)) - (eql - :eval (eql 'abc 'abc)) - (equal - :eval (equal 'abc 'abc)) - "Name" - (symbol-name - :eval (symbol-name 'abc)) - "Obarrays" - (obarray-make - :eval (obarray-make)) - (obarrayp - :eval (obarrayp (obarray-make)) - :eval (obarrayp nil)) - (unintern - :no-eval (unintern "abc" my-obarray) - :eg-result t) - (mapatoms - :no-eval (mapatoms (lambda (symbol) (print symbol)) my-obarray)) - (obarray-clear - :no-eval (obarray-clear my-obarray))) - -(define-short-documentation-group comparison - "General-purpose" - (eq - :eval (eq 'a 'a) - :eval "(eq ?A ?A)" - :eval (let ((x (list 'a "b" '(c) 4 5.0))) - (eq x x))) - (eql - :eval (eql 2 2) - :eval (eql 2.0 2.0) - :eval (eql 2.0 2)) - (equal - :eval (equal "abc" "abc") - :eval (equal 2.0 2.0) - :eval (equal 2.0 2) - :eval (equal '(a "b" (c) 4.0) '(a "b" (c) 4.0))) - (cl-equalp - :eval (cl-equalp 2 2.0) - :eval (cl-equalp "ABC" "abc")) - "Numeric" - (= - :args (number &rest numbers) - :eval (= 2 2) - :eval (= 2.0 2.0) - :eval (= 2.0 2) - :eval (= 4 4 4 4)) - (/= - :eval (/= 4 4)) - (< - :args (number &rest numbers) - :eval (< 4 4) - :eval (< 1 2 3)) - (<= - :args (number &rest numbers) - :eval (<= 4 4) - :eval (<= 1 2 2 3)) - (> - :args (number &rest numbers) - :eval (> 4 4) - :eval (> 3 2 1)) - (>= - :args (number &rest numbers) - :eval (>= 4 4) - :eval (>= 3 2 2 1)) - "String" - (string-equal - :eval (string-equal "abc" "abc") - :eval (string-equal "abc" "ABC")) - (string-equal-ignore-case - :eval (string-equal-ignore-case "abc" "ABC")) - (string-lessp - :eval (string-lessp "abc" "abd") - :eval (string-lessp "abc" "abc") - :eval (string-lessp "pic4.png" "pic32.png")) - (string-greaterp - :eval (string-greaterp "abd" "abc") - :eval (string-greaterp "abc" "abc")) - (string-version-lessp - :eval (string-version-lessp "pic4.png" "pic32.png") - :eval (string-version-lessp "1.9.3" "1.10.2")) - (string-collate-lessp - :eval (string-collate-lessp "abc" "abd"))) - -(define-short-documentation-group vector - "Making Vectors" - (make-vector - :eval (make-vector 5 "foo")) - (vector - :eval (vector 1 "b" 3)) - "Operations on Vectors" - (vectorp - :eval (vectorp [1]) - :eval (vectorp "1")) - (vconcat - :eval (vconcat '(1 2) [3 4])) - (append - :eval (append [1 2] nil)) - (length - :eval (length [1 2 3])) - (seq-reduce - :eval (seq-reduce #'+ [1 2 3] 0)) - (seq-subseq - :eval (seq-subseq [1 2 3 4 5] 1 3) - :eval (seq-subseq [1 2 3 4 5] 1)) - (copy-tree - :eval (copy-tree [1 (2 3) [4 5]] t)) - "Mapping Over Vectors" - (mapcar - :eval (mapcar #'identity [1 2 3])) - (mapc - :eval (mapc #'insert ["1" "2" "3"]))) - -(define-short-documentation-group regexp - "Matching Strings" - (replace-regexp-in-string - :eval (replace-regexp-in-string "[a-z]+" "_" "*foo*")) - (string-match-p - :eval (string-match-p "^[fo]+" "foobar")) - "Looking in Buffers" - (re-search-forward - :no-eval (re-search-forward "^foo$" nil t) - :eg-result 43) - (re-search-backward - :no-eval (re-search-backward "^foo$" nil t) - :eg-result 43) - (looking-at-p - :no-eval (looking-at-p "f[0-9]") - :eg-result t) - "Match Data" - (match-string - :eval (and (string-match "^\\([fo]+\\)b" "foobar") - (match-string 0 "foobar"))) - (match-beginning - :no-eval (match-beginning 1) - :eg-result 0) - (match-end - :no-eval (match-end 1) - :eg-result 3) - (save-match-data - :no-eval (save-match-data ...)) - "Replacing Match" - (replace-match - :no-eval (replace-match "new") - :eg-result nil) - (match-substitute-replacement - :no-eval (match-substitute-replacement "new") - :eg-result "new") - (replace-regexp-in-region - :no-value (replace-regexp-in-region "[0-9]+" "Num \\&")) - "Utilities" - (regexp-quote - :eval (regexp-quote "foo.*bar")) - (regexp-opt - :eval (regexp-opt '("foo" "bar"))) - (regexp-opt-depth - :eval (regexp-opt-depth "\\(a\\(b\\)\\)")) - (regexp-opt-charset - :eval (regexp-opt-charset '(?a ?b ?c ?d ?e))) - "The `rx' Structured Regexp Notation" - (rx - :eval (rx "IP=" (+ digit) (= 3 "." (+ digit)))) - (rx-to-string - :eval (rx-to-string '(| "foo" "bar"))) - (rx-define - :no-eval "(and (rx-define haskell-comment (seq \"--\" (zero-or-more nonl))) - (rx haskell-comment))" - :result "--.*") - (rx-let - :eval "(rx-let ((comma-separated (item) (seq item (0+ \",\" item))) - (number (1+ digit)) - (numbers (comma-separated number))) - (rx \"(\" numbers \")\"))" - :result "([[:digit:]]+\\(?:,[[:digit:]]+\\)*)") - (rx-let-eval - :eval "(rx-let-eval - '((ponder (x) (seq \"Where have all the \" x \" gone?\"))) - (rx-to-string - '(ponder (or \"flowers\" \"cars\" \"socks\"))))" - :result "\\(?:Where have all the \\(?:\\(?:car\\|flower\\|sock\\)s\\) gone\\?\\)")) - -(define-short-documentation-group sequence - "Sequence Predicates" - (seq-contains-p - :eval (seq-contains-p '(a b c) 'b) - :eval (seq-contains-p '(a b c) 'd)) - (seq-every-p - :eval (seq-every-p #'numberp '(1 2 3))) - (seq-empty-p - :eval (seq-empty-p [])) - (seq-set-equal-p - :eval (seq-set-equal-p '(1 2 3) '(3 1 2))) - (seq-some - :eval (seq-some #'floatp '(1 2.0 3))) - "Building Sequences" - (seq-concatenate - :eval (seq-concatenate 'vector '(1 2) '(c d))) - (seq-copy - :eval (seq-copy '(a 2))) - (seq-into - :eval (seq-into '(1 2 3) 'vector)) - "Utility Functions" - (seq-count - :eval (seq-count #'numberp '(1 b c 4))) - (seq-elt - :eval (seq-elt '(a b c) 1)) - (seq-random-elt - :no-eval (seq-random-elt '(a b c)) - :eg-result c) - (seq-find - :eval (seq-find #'numberp '(a b 3 4 f 6))) - (seq-position - :eval (seq-position '(a b c) 'c)) - (seq-positions - :eval (seq-positions '(a b c a d) 'a) - :eval (seq-positions '(a b c a d) 'z) - :eval (seq-positions '(11 5 7 12 9 15) 10 #'>=)) - (seq-length - :eval (seq-length "abcde")) - (seq-max - :eval (seq-max [1 2 3])) - (seq-min - :eval (seq-min [1 2 3])) - (seq-first - :eval (seq-first [a b c])) - (seq-rest - :eval (seq-rest '[1 2 3])) - (seq-reverse - :eval (seq-reverse '(1 2 3))) - (seq-sort - :eval (seq-sort #'> '(1 2 3))) - (seq-sort-by - :eval (seq-sort-by (lambda (a) (/ 1.0 a)) #'< '(1 2 3))) - "Mapping Over Sequences" - (seq-map - :eval (seq-map #'1+ '(1 2 3))) - (seq-map-indexed - :eval (seq-map-indexed (lambda (a i) (cons i a)) '(a b c))) - (seq-mapcat - :eval (seq-mapcat #'upcase '("a" "b" "c") 'string)) - (seq-doseq - :no-eval (seq-doseq (a '("foo" "bar")) (insert a)) - :eg-result ("foo" "bar")) - (seq-do - :no-eval (seq-do (lambda (a) (insert a)) '("foo" "bar")) - :eg-result ("foo" "bar")) - (seq-do-indexed - :no-eval (seq-do-indexed - (lambda (a index) (message "%s:%s" index a)) - '("foo" "bar")) - :eg-result nil) - (seq-reduce - :eval (seq-reduce #'* [1 2 3] 2)) - "Excerpting Sequences" - (seq-drop - :eval (seq-drop '(a b c) 2)) - (seq-drop-while - :eval (seq-drop-while #'numberp '(1 2 c d 5))) - (seq-filter - :eval (seq-filter #'numberp '(a b 3 4 f 6))) - (seq-keep - :eval (seq-keep #'car-safe '((1 2) 3 t (a . b)))) - (seq-remove - :eval (seq-remove #'numberp '(1 2 c d 5))) - (seq-remove-at-position - :eval (seq-remove-at-position '(a b c d e) 3) - :eval (seq-remove-at-position [a b c d e] 0)) - (seq-group-by - :eval (seq-group-by #'natnump '(-1 2 3 -4 -5 6))) - (seq-union - :eval (seq-union '(1 2 3) '(3 5))) - (seq-difference - :eval (seq-difference '(1 2 3) '(2 3 4))) - (seq-intersection - :eval (seq-intersection '(1 2 3) '(2 3 4))) - (seq-partition - :eval (seq-partition '(a b c d e f g h) 3)) - (seq-subseq - :eval (seq-subseq '(a b c d e) 2 4)) - (seq-take - :eval (seq-take '(a b c d e) 3)) - (seq-split - :eval (seq-split [0 1 2 3 5] 2)) - (seq-take-while - :eval (seq-take-while #'integerp [1 2 3.0 4])) - (seq-uniq - :eval (seq-uniq '(a b d b a c)))) - -(define-short-documentation-group buffer - "Buffer Basics" - (current-buffer - :no-eval (current-buffer) - :eg-result-string "#") - (bufferp - :eval (bufferp 23)) - (buffer-live-p - :no-eval (buffer-live-p some-buffer) - :eg-result t) - (buffer-modified-p - :eval (buffer-modified-p (current-buffer))) - (buffer-name - :eval (buffer-name)) - (window-buffer - :eval (window-buffer)) - "Selecting Buffers" - (get-buffer-create - :no-eval (get-buffer-create "*foo*") - :eg-result-string "#") - (pop-to-buffer - :no-eval (pop-to-buffer "*foo*") - :eg-result-string "#") - (with-current-buffer - :no-eval* (with-current-buffer buffer (buffer-size))) - "Points and Positions" - (point - :eval (point)) - (point-min - :eval (point-min)) - (point-max - :eval (point-max)) - (pos-bol - :eval (pos-bol)) - (pos-eol - :eval (pos-eol)) - (bolp - :eval (bolp)) - (eolp - :eval (eolp)) - (line-beginning-position - :eval (line-beginning-position)) - (line-end-position - :eval (line-end-position)) - (buffer-size - :eval (buffer-size)) - (bobp - :eval (bobp)) - (eobp - :eval (eobp)) - "Moving Around" - (goto-char - :no-eval (goto-char (point-max)) - :eg-result 342) - (search-forward - :no-eval (search-forward "some-string" nil t) - :eg-result 245) - (re-search-forward - :no-eval (re-search-forward "some-s.*g" nil t) - :eg-result 245) - (forward-line - :no-eval (forward-line 1) - :eg-result 0 - :no-eval (forward-line -2) - :eg-result 0) - "Strings from Buffers" - (buffer-string - :no-eval* (buffer-string)) - (buffer-substring - :eval (buffer-substring (point-min) (+ (point-min) 10))) - (buffer-substring-no-properties - :eval (buffer-substring-no-properties (point-min) (+ (point-min) 10))) - (following-char - :no-eval (following-char) - :eg-result 67) - (preceding-char - :no-eval (preceding-char) - :eg-result 38) - (char-after - :eval (char-after 45)) - (char-before - :eval (char-before 13)) - (get-byte - :no-eval (get-byte 45) - :eg-result-string "#xff") - "Altering Buffers" - (delete-region - :no-value (delete-region (point-min) (point-max))) - (erase-buffer - :no-value (erase-buffer)) - (delete-line - :no-value (delete-line)) - (insert - :no-value (insert "This string will be inserted in the buffer\n")) - (subst-char-in-region - :no-eval "(subst-char-in-region (point-min) (point-max) ?+ ?-)") - (replace-string-in-region - :no-value (replace-string-in-region "foo" "bar")) - "Locking" - (lock-buffer - :no-value (lock-buffer "/tmp/foo")) - (unlock-buffer - :no-value (unlock-buffer))) - -(define-short-documentation-group overlay - "Predicates" - (overlayp - :no-eval (overlayp some-overlay) - :eg-result t) - "Creation and Deletion" - (make-overlay - :args (beg end &optional buffer) - :no-eval (make-overlay 1 10) - :eg-result-string "#") - (delete-overlay - :no-eval (delete-overlay foo) - :eg-result t) - "Searching Overlays" - (overlays-at - :no-eval (overlays-at 15) - :eg-result-string "(#)") - (overlays-in - :no-eval (overlays-in 1 30) - :eg-result-string "(#)") - (next-overlay-change - :no-eval (next-overlay-change 1) - :eg-result 20) - (previous-overlay-change - :no-eval (previous-overlay-change 30) - :eg-result 20) - "Overlay Properties" - (overlay-start - :no-eval (overlay-start foo) - :eg-result 1) - (overlay-end - :no-eval (overlay-end foo) - :eg-result 10) - (overlay-put - :no-eval (overlay-put foo 'happy t) - :eg-result t) - (overlay-get - :no-eval (overlay-get foo 'happy) - :eg-result t) - (overlay-buffer - :no-eval (overlay-buffer foo)) - "Moving Overlays" - (move-overlay - :no-eval (move-overlay foo 5 20) - :eg-result-string "#")) - -(define-short-documentation-group process - (make-process - :no-eval (make-process :name "foo" :command '("cat" "/tmp/foo")) - :eg-result-string "#") - (processp - :eval (processp t)) - (process-status - :no-eval (process-status process) - :eg-result exit) - (delete-process - :no-value (delete-process process)) - (kill-process - :no-value (kill-process process)) - (set-process-sentinel - :no-value (set-process-sentinel process (lambda (proc string)))) - (process-buffer - :no-eval (process-buffer process) - :eg-result-string "#") - (get-buffer-process - :no-eval (get-buffer-process buffer) - :eg-result-string "#") - (process-live-p - :no-eval (process-live-p process) - :eg-result t)) - -(define-short-documentation-group number - "Arithmetic" - (+ - :args (&rest numbers) - :eval (+ 1 2) - :eval (+ 1 2 3 4)) - (- - :args (&rest numbers) - :eval (- 3 2) - :eval (- 6 3 2)) - (* - :args (&rest numbers) - :eval (* 3 4 5)) - (/ - :eval (/ 10 5) - :eval (/ 10 6) - :eval (/ 10.0 6) - :eval (/ 10.0 3 3)) - (% - :eval (% 10 5) - :eval (% 10 6)) - (mod - :eval (mod 10 5) - :eval (mod 10 6) - :eval (mod 10.5 6)) - (1+ - :eval (1+ 2) - :eval (let ((x 2)) (1+ x) x)) - (1- - :eval (1- 4) - :eval (let ((x 4)) (1- x) x)) - (incf - :eval (let ((x 2)) (incf x) x) - :eval (let ((x 2)) (incf x 2) x)) - (decf - :eval (let ((x 4)) (decf x) x) - :eval (let ((x 4)) (decf x 2)) x) - "Predicates" - (= - :args (number &rest numbers) - :eval (= 4 4) - :eval (= 4.0 4.0) - :eval (= 4 4.0) - :eval (= 4 4 4 4)) - (eql - :eval (eql 4 4) - :eval (eql 4.0 4.0)) - (/= - :eval (/= 4 4)) - (< - :args (number &rest numbers) - :eval (< 4 4) - :eval (< 1 2 3)) - (<= - :args (number &rest numbers) - :eval (<= 4 4) - :eval (<= 1 2 2 3)) - (> - :args (number &rest numbers) - :eval (> 4 4) - :eval (> 3 2 1)) - (>= - :args (number &rest numbers) - :eval (>= 4 4) - :eval (>= 3 2 2 1)) - (zerop - :eval (zerop 0)) - (natnump - :eval (natnump -1) - :eval (natnump 0) - :eval (natnump 23)) - (plusp - :eval (plusp 0) - :eval (plusp 1)) - (minusp - :eval (minusp 0) - :eval (minusp -1)) - (oddp - :eval (oddp 3)) - (evenp - :eval (evenp 6)) - (bignump - :eval (bignump 4) - :eval (bignump (expt 2 90))) - (fixnump - :eval (fixnump 4) - :eval (fixnump (expt 2 90))) - (floatp - :eval (floatp 5.4)) - (integerp - :eval (integerp 5.4)) - (numberp - :eval (numberp "5.4")) - (cl-digit-char-p - :eval (cl-digit-char-p ?5 10) - :eval (cl-digit-char-p ?f 16)) - "Operations" - (max - :args (number &rest numbers) - :eval (max 7 9 3)) - (min - :args (number &rest numbers) - :eval (min 7 9 3)) - (abs - :eval (abs -4)) - (float - :eval (float 2)) - (truncate - :eval (truncate 1.2) - :eval (truncate -1.2) - :eval (truncate 5.4 2)) - (floor - :eval (floor 1.2) - :eval (floor -1.2) - :eval (floor 5.4 2)) - (ceiling - :eval (ceiling 1.2) - :eval (ceiling -1.2) - :eval (ceiling 5.4 2)) - (round - :eval (round 1.2) - :eval (round -1.2) - :eval (round 5.4 2)) - (random - :eval (random 6)) - "Bit Operations" - (ash - :eval (ash 1 4) - :eval (ash 16 -1)) - (logand - :no-eval "(logand #b10 #b111)" - :result-string "#b10") - (logior - :eval (logior 4 16)) - (logxor - :eval (logxor 4 16)) - (lognot - :eval (lognot 5)) - (logcount - :eval (logcount 5)) - "Floating Point" - (isnan - :eval (isnan 5.0)) - (frexp - :eval (frexp 5.7)) - (ldexp - :eval (ldexp 0.7125 3)) - (logb - :eval (logb 10.5)) - (ffloor - :eval (ffloor 1.2)) - (fceiling - :eval (fceiling 1.2)) - (ftruncate - :eval (ftruncate 1.2)) - (fround - :eval (fround 1.2)) - "Standard Math Functions" - (sin - :eval (sin float-pi)) - (cos - :eval (cos float-pi)) - (tan - :eval (tan float-pi)) - (asin - :eval (asin float-pi)) - (acos - :eval (acos float-pi)) - (atan - :eval (atan float-pi)) - (exp - :eval (exp 4)) - (log - :eval (log 54.59)) - (expt - :eval (expt 2 16)) - (sqrt - :eval (sqrt -1))) - -(define-short-documentation-group text-properties - "Examining Text Properties" - (get-text-property - :eval (get-text-property 0 'foo (propertize "x" 'foo t))) - (get-char-property - :eval (get-char-property 0 'foo (propertize "x" 'foo t))) - (get-pos-property - :eval (get-pos-property 0 'foo (propertize "x" 'foo t))) - (get-char-property-and-overlay - :eval (get-char-property-and-overlay 0 'foo (propertize "x" 'foo t))) - (text-properties-at - :eval (text-properties-at (point))) - "Changing Text Properties" - (put-text-property - :eval (let ((s (copy-sequence "abc"))) (put-text-property 0 1 'foo t s) s) - :no-eval (put-text-property (point) (1+ (point)) 'face 'error)) - (add-text-properties - :no-eval (add-text-properties (point) (1+ (point)) '(face error))) - (remove-text-properties - :no-eval (remove-text-properties (point) (1+ (point)) '(face nil))) - (remove-list-of-text-properties - :no-eval (remove-list-of-text-properties (point) (1+ (point)) '(face font-lock-face))) - (set-text-properties - :no-eval (set-text-properties (point) (1+ (point)) '(face error))) - (add-face-text-property - :no-eval (add-face-text-property START END '(:foreground "green"))) - (propertize - :eval (propertize "foo" 'face 'italic 'mouse-face 'bold-italic)) - "Searching for Text Properties" - (next-property-change - :no-eval (next-property-change (point) (current-buffer))) - (previous-property-change - :no-eval (previous-property-change (point) (current-buffer))) - (next-single-property-change - :no-eval (next-single-property-change (point) 'face (current-buffer))) - (previous-single-property-change - :no-eval (previous-single-property-change (point) 'face (current-buffer))) - ;; TODO: There are some more that could be added here. - (text-property-search-forward - :no-eval (text-property-search-forward 'face nil t)) - (text-property-search-backward - :no-eval (text-property-search-backward 'face nil t))) - -(define-short-documentation-group keymaps - "Defining keymaps or adding bindings to existing keymaps" - (define-keymap - :no-eval (define-keymap "C-c C-c" #'quit-buffer) - :no-eval (define-keymap :keymap ctl-x-map - "C-r" #'recentf-open - "k" #'kill-current-buffer)) - (defvar-keymap - :no-eval (defvar-keymap my-keymap "C-c C-c" #'quit-buffer)) - "Setting keys" - (keymap-set - :no-eval (keymap-set map "C-c C-c" #'quit-buffer)) - (keymap-local-set - :no-eval (keymap-local-set "C-c C-c" #'quit-buffer)) - (keymap-global-set - :no-eval (keymap-global-set "C-c C-c" #'quit-buffer)) - (keymap-unset - :no-eval (keymap-unset map "C-c C-c")) - (keymap-local-unset - :no-eval (keymap-local-unset "C-c C-c")) - (keymap-global-unset - :no-eval (keymap-global-unset "C-c C-c")) - (keymap-substitute - :no-eval (keymap-substitute map "C-c C-c" "M-a")) - (keymap-set-after - :no-eval (keymap-set-after map "" menu-bar-separator)) - "Predicates" - (keymapp - :eval (keymapp (define-keymap))) - (key-valid-p - :eval (key-valid-p "C-c C-c") - :eval (key-valid-p "C-cC-c")) - "Lookup" - (keymap-lookup - :eval (keymap-lookup (current-global-map) "C-x x g"))) +;; FIXME: As long as we do not have a better mechanism to load shortdoc +;; definitions on demand, we must require `shortdoc-doc' after above +;; macro to avoid loading cycles. But at least we do not require +;; `shortdoc-doc' while compiling this file, only when loading it. +(if t (require 'shortdoc-doc)) + ;;;###autoload (defun shortdoc-display-group (group &optional function same-window) "Pop to a buffer with short documentation summary for functions in GROUP. @@ -1650,6 +260,9 @@ If SAME-WINDOW, don't pop to a new window." (text-property-search-forward 'shortdoc-function function t) (beginning-of-line))) +;;;###autoload +(defalias 'shortdoc #'shortdoc-display-group) + (defun shortdoc--insert-group-in-buffer (group &optional buf) "Insert a short documentation summary for functions in GROUP in buffer BUF. BUF defaults to the current buffer if nil or omitted." @@ -1685,9 +298,6 @@ BUF defaults to the current buffer if nil or omitted." (shortdoc--display-function data)))) (cdr (assq group shortdoc--groups)))))) -;;;###autoload -(defalias 'shortdoc #'shortdoc-display-group) - (defun shortdoc--display-function (data) (let ((function (pop data)) (start-section (point)) @@ -1875,6 +485,10 @@ Example: (shortdoc-add-function \\='file \"Predicates\" \\='(file-locked-p :no-eval (file-locked-p \"/tmp\")))" + ;; Rely on `shortdoc--check' checking GROUP. + (unless (stringp section) + (signal 'wrong-type-argument (list 'stringp section))) + (shortdoc--check group (list section elem)) (let ((glist (assq group shortdoc--groups))) (unless glist (setq glist (list group)) From bc276230575c3c21233d3cb992dce8f4f28ba77f Mon Sep 17 00:00:00 2001 From: Jens Schmidt Date: Wed, 25 Mar 2026 22:38:38 +0100 Subject: [PATCH 4/4] ; Fix some shortdoc issues unearthed by `shortdoc--check' * lisp/emacs-lisp/shortdoc-doc.el (map, number): Fix issues. * lisp/treesit.el (treesit): Fix issues. --- lisp/emacs-lisp/shortdoc-doc.el | 4 ++-- lisp/treesit.el | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lisp/emacs-lisp/shortdoc-doc.el b/lisp/emacs-lisp/shortdoc-doc.el index 40b98811bf0..eb642c1600b 100644 --- a/lisp/emacs-lisp/shortdoc-doc.el +++ b/lisp/emacs-lisp/shortdoc-doc.el @@ -100,7 +100,7 @@ :eval (map-contains-key [bar foo baz] 1) :eval (map-contains-key #s(hash-table data (bar 1 foo 2 baz 3)) 'foo)) (map-put! - (map key value) + :args (map key value) :eval "(let ((map (list 'bar 1 'baz 3))) (map-put! map 'foo 2) @@ -1301,7 +1301,7 @@ :eval (let ((x 2)) (incf x 2) x)) (decf :eval (let ((x 4)) (decf x) x) - :eval (let ((x 4)) (decf x 2)) x) + :eval (let ((x 4)) (decf x 2) x)) "Predicates" (= :args (number &rest numbers) diff --git a/lisp/treesit.el b/lisp/treesit.el index d7cfe0a9f3f..ebdd2367a52 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -5849,7 +5849,7 @@ language." "Pattern matching" (treesit-query-capture :no-eval (treesit-query-capture node '((identifier) @id "return" @ret)) - :eg-result-string "((id . #) (ret . #))") + :eg-result-string "((id . #) (ret . #))") (treesit-query-compile :no-eval (treesit-query-compile 'c '((identifier) @id "return" @ret)) :eg-result-string "#")