From 82882db8edc00f8964e5d4561f9fce6a96b6c336 Mon Sep 17 00:00:00 2001 From: Jens Schmidt Date: Wed, 25 Mar 2026 18:05:10 +0100 Subject: [PATCH] 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))