branch: externals/hyperbole commit 8aee5dff97311f72cc208830349cd508d0371e29 Merge: 8e4d0ffb70 aa3b1481cb Author: Robert Weiner <r...@gnu.org> Commit: GitHub <nore...@github.com>
Merge pull request #740 from rswgnu/rsw hyrolo.el - Add consult completion support; fix www.domain.com not displaying; add :html-prefer-user-labels for HyWiki html publishing --- ChangeLog | 44 ++++++++++++++++++++++++++++++ hsys-consult.el | 63 ++++++++++++++++++++++++++++++------------- hsys-www.el | 12 +++++---- hyrolo.el | 78 +++++++++++++++++++++++++++++++++++------------------ hywiki.el | 7 ++--- test/hargs-tests.el | 4 +-- 6 files changed, 154 insertions(+), 54 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9ec6944e06..86b3fd57cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ +2025-06-02 Bob Weiner <r...@gnu.org> + +* hyrolo.el (hyrolo-kill): Add consult completion support. + (hyrolo-to): Get 'outline-regexp' value from (current-buffer) + not (get-buffer hyrolo-display-buffer) to fix bug when display buffer + does not exist. + +* hsys-consult.el (hsys-consult-selected-candidate): Rewrite to fix byte + compilation errors. + +2025-06-01 Bob Weiner <r...@gnu.org> + +* hyrolo.el (hyrolo-grep-file): Fix to handle regexp-quoted asterisks, i.e. + "\\*". This fixes a bug where 'hyrolo-yank' did not match to entries + starting with multiple asterisks. + (hyrolo-edit): Add consult completion support. + +* hsys-consult.el (hsys-consult-selected-candidate): Rewrite to allow non-command + functions that take arguments (so can send a prompt) and remove + 'no-properties-flag'. Use 'substring-no-properties' to handle that in a + separate call. + (hsys-consult-grep-headlines-read-regexp): Add. + hyrolo.el (hyrolo-consult-yank-grep): Generalize and rename to + 'hsys-consult-grep-headlines-with-prompt' in "hsys-consult.el". + +* hyrolo.el (hyrolo--cache-major-mode): Initialize hash table if not done + already. Fixes bug when 'hyrolo-grep' is called prior to init. + (hyrolo-yank): Fix to get for (fboundp 'consult-grep) rather than + (featurep 'consult) so that the consult package can be autoloaded. Also + add error handling if invalid 'name' value is given. + (hyrolo-consult-yank-grep): Add (require 'consult) before locally + disabling 'consult-preview-key' so do not get this error when consult + is loaded lower in the call stack: + (error "Defining as dynamic an already lexical var") + custom-declare-variable(consult-preview-key) + 2025-06-01 Mats Lidell <ma...@gnu.org> * test/hy-test-helpers.el (hy-test-helpers:should-last-message): Change @@ -21,6 +57,14 @@ 2025-05-27 Bob Weiner <r...@gnu.org> +* hsys-www.el (www-url): Fix bug where "www.google.com" would not actually + be sent to the browser for display even though recognized as a url. Had + to add "https://" to the front of any such url. + +* hywiki.el (hywiki-org-make-publish-project-alist): Add + :html-prefer-user-labels t to make Org use normalized headlines as href + IDs. + * hypb.el (hypb:in-string-p): Fix 'texinfo-mode' string not returning a list when 'range-flag' is given. test/hypb-tests.el (hypb--in-string-p): Enable this test since fixed now. diff --git a/hsys-consult.el b/hsys-consult.el index 375931a073..47e328565c 100644 --- a/hsys-consult.el +++ b/hsys-consult.el @@ -2,7 +2,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 4-Jul-24 at 09:57:18 -;; Last-Mod: 27-May-25 at 23:40:50 by Mats Lidell +;; Last-Mod: 2-Jun-25 at 22:18:49 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -84,7 +84,8 @@ (kill-buffer buf)))))) ;;;###autoload -(defun hsys-consult-grep (grep-includes ripgrep-globs &optional regexp max-matches path-list prompt) +(defun hsys-consult-grep (grep-includes ripgrep-globs &optional regexp max-matches + path-list prompt) "Interactively search PATH-LIST with a consult package grep command. With GREP-INCLUDES or RIPGREP-GLOBS file suffixes to include, search @@ -94,6 +95,7 @@ Use ripgrep (rg) if found, otherwise, plain grep. Initialize search with optional REGEXP and interactively prompt for changes. Limit matches per file to the absolute value of MAX-MATCHES, if given and not 0. If 0, match to headlines only (lines that start with a '^[*#]+[ \t]+' regexp). + With optional PROMPT string, use this as the first part of the grep prompt; omit any trailing colon and space in the prompt." (unless (package-installed-p 'consult) @@ -124,6 +126,34 @@ omit any trailing colon and space in the prompt." path-list))) (hsys-consult--grep-paths paths regexp max-matches prompt))) +(defun hsys-consult-grep-headlines-with-prompt (grep-function prompt + &optional regexp) + "Call Hyperbole consult GREP-FUNCTION over headlines with PROMPT. +Optional REGEXP is the initial pattern for the grep. +Suppress preview and return the selected \"file:line:line-contents\". + +GREP-FUNCTION must take these arguments: regexp max-matches path-list +prompt." + (let ((consult-preview-key nil)) + (funcall grep-function regexp 0 nil prompt))) + +(defun hsys-consult-grep-headlines-read-regexp (grep-function prompt + &optional regexp) + "With `consult', completing read a string with GREP-FUNCTION and PROMPT. +Optional REGEXP is the initial pattern for the grep. +Suppress preview and return the selected \"file:line:line-contents\". + +GREP-FUNCTION must take these arguments: regexp max-matches path-list +prompt." + (if (fboundp 'consult-grep) + (substring-no-properties + (hsys-consult-selected-candidate + #'hsys-consult-grep-headlines-with-prompt + grep-function + prompt + regexp)) + (read-regexp (concat prompt ": ") regexp))) + (defun hsys-consult-grep-tags (org-consult-grep-function) "When on an Org tag, call ORG-CONSULT-GREP-FUNCTION to find matches. If on a colon, match to sections with all tags around point; @@ -211,21 +241,17 @@ that start with the '^[*#]+[ \t]*' regexp)." (org-roam-node-find nil nil (lambda (node) (zerop (org-roam-node-level node))))))) ;;;###autoload -(defun hsys-consult-selected-candidate (consult-command &optional no-properties-flag) - "Return the input from interactively calling CONSULT-COMMAND, a symbol. -CONSULT-COMMAND is called with no arguments. Add optional -NO-PROPERTIES-FLAG non-nil to strip the properties from the -returned input string." - (unless (commandp consult-command) - (user-error "(hsys-consult-selected-candidate): First arg must be a command, not `%s'" consult-command)) +(defun hsys-consult-selected-candidate (consult-function &rest args) + "Return the input from calling CONSULT-FUNCTION, a symbol, with rest of ARGS." + (unless (fboundp consult-function) + (user-error "(hsys-consult-selected-candidate): First arg must be a bound function, not `%s'" + consult-function)) (save-excursion (save-window-excursion - (cl-flet ((mapcar (lambda (state-function) - `(,state-function () cand)) - (apropos-internal "consult--.+-state" #'fboundp))) - (if no-properties-flag - (substring-no-properties (or (call-interactively consult-command) "")) - (call-interactively consult-command)))))) + (eval `(cl-flet ((mapcar (lambda (state-function) + `(,state-function () cand)) + (apropos-internal "consult--.+-state" #'fboundp))) + (apply ',consult-function ',args)))))) ;;; ************************************************************************ ;;; Private functions @@ -241,9 +267,10 @@ Initialize search with optional REGEXP and interactively prompt for changes. Limit matches per file to the absolute value of optional MAX-MATCHES, if given and not 0. If 0, match to the start of headline text only (lines that start with a '^[*#]+[ -\t]*' regexp). With optional PROMPT string, use this as the first -part of the grep prompt; omit any trailing colon and space in the -prompt." +\t]*' regexp). + +With optional PROMPT string, use this as the first part of the +grep prompt; omit any trailing colon and space in the prompt." (unless (package-installed-p 'consult) (package-install 'consult)) (require 'consult) diff --git a/hsys-www.el b/hsys-www.el index 57c173d43d..a78c3ccaee 100644 --- a/hsys-www.el +++ b/hsys-www.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 7-Apr-94 at 17:17:39 by Bob Weiner -;; Last-Mod: 27-May-25 at 00:57:00 by Bob Weiner +;; Last-Mod: 28-May-25 at 01:21:16 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -112,8 +112,10 @@ The variable, `browse-url-browser-function', customizes the url browser that is used. Valid values of this variable include `browse-url-default-browser' and `browse-url-generic'." (interactive "sURL to follow: ") - (or (stringp url) - (error "(www-url): URL = `%s' but must be a string" url)) + (unless (stringp url) + (error "(www-url): URL = `%s' but must be a string" url)) + (unless (seq-position url ?:) + (setq url (concat "https://" url))) (if (or (functionp browse-url-browser-function) ;; May be a predicate alist of functions from which to select (consp browse-url-browser-function)) @@ -141,12 +143,12 @@ are included as parameters in the mailto url." (mailto (if (string-prefix-p "mailto:" to) to (concat "mailto:" to)))) ;; Add subject if provided (when subject - (setq mailto (concat mailto "?subject=" (url-encode-string subject)))) + (setq mailto (concat mailto "?subject=" (url-encode-url subject)))) ;; Add body if provided (when body (unless subject (setq mailto (concat mailto "?"))) - (setq mailto (concat mailto "&body=" (url-encode-string body)))) + (setq mailto (concat mailto "&body=" (url-encode-url body)))) (hact 'www-url mailto))) ;;;###autoload diff --git a/hyrolo.el b/hyrolo.el index 1512caf89e..81dd05ec9e 100644 --- a/hyrolo.el +++ b/hyrolo.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 7-Jun-89 at 22:08:29 -;; Last-Mod: 27-May-25 at 23:47:02 by Mats Lidell +;; Last-Mod: 2-Jun-25 at 23:27:51 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -382,7 +382,7 @@ String search expressions are converted to regular expressions.") ;;;###autoload (defun hyrolo-add (name &optional file) - "Add a new entry in personal rolo for NAME. + "Add a new entry for NAME in the first file from `hyrolo-file-list'. Last name first is best, e.g. \"Smith, John\". With prefix argument, prompts for optional FILE to add entry within. NAME may be of the form: parent/child to insert child below a parent @@ -398,7 +398,8 @@ entry which begins with the parent string." (or name email)))) (list (if (and email name (string-match (concat "\\`" (regexp-quote entry)) name)) - (format hyrolo-email-format entry email) entry) + (format hyrolo-email-format entry email) + entry) current-prefix-arg)))) (when (or (not (stringp name)) (string-equal name "")) (error "(hyrolo-add): Invalid name: `%s'" name)) @@ -593,12 +594,25 @@ within which to locate entry. With no NAME arg, simply display FILE-OR-BUF or the first entry in `hyrolo-file-list' in an editable mode. NAME may be of the form: parent/child to edit child below a parent entry which begins with the parent string." - (interactive "sEdit rolo entry named: \nP") + (interactive (list + (hsys-consult-grep-headlines-read-regexp + #'hyrolo-consult-grep "Edit rolo entry named") + current-prefix-arg)) (when (string-empty-p name) (setq name nil)) (when (and name (not (stringp name))) (error "(hyrolo-edit): Invalid name: `%s'" name)) + ;; With consult-grep, 'name' is the entire line matched prefixed + ;; by filename and line number, so remove these prefixes. + (when (and name + (fboundp 'consult-grep) + (string-match "\\([^ \t\n\r\"'`]*[^ \t\n\r:\"'`0-9]\\): ?\\([1-9][0-9]*\\)[ :]" + name)) + (setq file-or-buf (expand-file-name (match-string 1 name)) + name (substring name (match-end 0))) + (put-text-property 0 1 'hyrolo-line-entry 0 name)) + (let* ((found-point) (all-files-or-bufs (hyrolo-get-file-list)) (file-or-buf-list (if file-or-buf (list file-or-buf) all-files-or-bufs))) @@ -610,6 +624,7 @@ a parent entry which begins with the parent string." (mapcar #'list all-files-or-bufs))))) (unless file-or-buf (setq file-or-buf (car file-or-buf-list))) + (if (or (null name) (setq found-point (hyrolo-to name (list file-or-buf)))) (cond ((stringp file-or-buf) @@ -900,8 +915,11 @@ With prefix argument, prompts for optional FILE to locate entry within. NAME may be of the form: parent/child to kill child below a parent entry which begins with the parent string. Return t if entry is killed, nil otherwise." - (interactive "sKill rolo entry named: \nP") - (if (or (not (stringp name)) (string-equal name "") (string-match "\\*" name)) + (interactive (list + (hsys-consult-grep-headlines-read-regexp + #'hyrolo-consult-grep "Kill rolo entry named") + current-prefix-arg)) + (if (or (not (stringp name)) (string-empty-p name)) (error "(hyrolo-kill): Invalid name: `%s'" name)) (if (and (called-interactively-p 'interactive) current-prefix-arg) (setq file (completing-read "Entry's File: " @@ -911,7 +929,12 @@ Return t if entry is killed, nil otherwise." (unless file (setq file (car file-list))) (save-excursion - (if (hyrolo-to name file-list) + (if (if (and (fboundp 'consult-grep) + (string-match "\\([^ \t\n\r\"'`]*[^ \t\n\r:\"'`0-9]\\): ?\\([1-9][0-9]*\\)[ :]" + name)) + (hyrolo-to (substring name (match-end 0)) + (list (setq file (match-string-no-properties 1 name)))) + (hyrolo-to name file-list)) (progn (setq file (hypb:buffer-file-name)) (if (file-writable-p file) @@ -1020,7 +1043,7 @@ or NAME is invalid, return nil." (require 'markdown-mode) ;; Don't actually derive from `markdown-mode' to avoid its costly setup - ;; but set its parent mode property to org-mode so `derived-mode-p' checks + ;; but set its parent mode property to `markdown-mode' so `derived-mode-p' checks ;; will pass. (put 'hyrolo-markdown-mode 'derived-mode-parent 'markdown-mode) @@ -1520,22 +1543,26 @@ hyrolo-file-list." ;;;###autoload (defun hyrolo-yank (name &optional regexp-flag) "Insert at point the first rolo entry with a headline containing NAME. -If the `consult' package is loaded, interactively select and complete +If the `consult' package is installed, interactively select and complete the entry to be inserted. With optional prefix arg, REGEXP-FLAG, treat NAME as a regular expression instead of a string." (interactive (list - (if (featurep 'consult) - (hsys-consult-selected-candidate 'hyrolo-consult-yank-grep t) - (read-string "Yank rolo headline matching: ")) + (hsys-consult-grep-headlines-read-regexp + #'hyrolo-consult-grep "Yank rolo headline matching") current-prefix-arg)) + (when (string-empty-p name) + (setq name nil)) + (when (or (null name) (not (stringp name))) + (error "(hyrolo-yank): Invalid name: `%s'" name)) + (let ((hyrolo-display-buffer (current-buffer)) (start (point)) found) (save-excursion (setq found - (if (and (featurep 'consult) + (if (and (fboundp 'consult-grep) (string-match "\\([^ \t\n\r\"'`]*[^ \t\n\r:\"'`0-9]\\): ?\\([1-9][0-9]*\\)[ :]" name)) (hyrolo-grep-file (match-string-no-properties 1 name) @@ -1543,8 +1570,8 @@ instead of a string." -1 nil t) (hyrolo-grep (if regexp-flag name (regexp-quote name)) -1 nil nil t)))) ;; Let user reformat the region just yanked. - (if (= found 1) - (funcall hyrolo-yank-reformat-function start (point))) + (when (= found 1) + (funcall hyrolo-yank-reformat-function start (point))) found)) ;;; ************************************************************************ @@ -2028,8 +2055,10 @@ Return number of matching entries found." max-matches (- max-matches))))) (set-buffer actual-buf) + ;; Allow for initial asterisks being regexp-quoted in + ;; string-match below. (when (and headline-only - (not (string-match (concat "\\`\\([*#]+[ \t]+\\|" + (not (string-match (concat "\\`\\([\\*#]+[ \t]+\\|" "\\\\\\*+[ \t]+\\|" "#+[ \t]+\\|" (regexp-quote "^") "\\|" @@ -2679,7 +2708,8 @@ begins or nil if not found." (t (error "(hyrolo-to): Second argument must be a file or buffer, not: `%s'" file-or-buf))) (set-buffer (if (stringp file-or-buf) - (or (get-file-buffer file-or-buf) (hyrolo-find-file-noselect file-or-buf)) + (or (get-file-buffer file-or-buf) + (hyrolo-find-file-noselect file-or-buf)) ;; must be a buffer file-or-buf)) (let ((case-fold-search t) (real-name name) (parent "") (level) @@ -2722,7 +2752,7 @@ begins or nil if not found." (setq found (when (or (looking-at (buffer-local-value 'outline-regexp - (get-buffer hyrolo-display-buffer))) + (current-buffer))) ;; Jump to non-first line within an entry (progn (back-to-indentation) (looking-at (regexp-quote name)))) @@ -2930,12 +2960,6 @@ HYROLO-BUF may be a file-name, `buffer-name', or buffer." hyrolo-buf)) (buffer-list)))) -(defun hyrolo-consult-yank-grep () - "Support function for `hyrolo-yank'." - (interactive) - (let ((consult-preview-key nil)) - (hyrolo-consult-grep nil 0 nil "Yank rolo headline matching"))) - (defun hyrolo-current-date () "Return the current date (a string) in a form used for rolo entry insertion." (format-time-string hyrolo-date-format)) @@ -3357,7 +3381,7 @@ proper major mode." (narrow-to-region start end)) (let ((font-lock-mode)) ;; (message "%s" (hyrolo-cache-get-major-mode-from-pos - ;; (funcall (if backward-flag '1- '1+) start))) + ;; (funcall (if backward-flag '1- '1+) start))) (if (and backward-flag (looking-at hyrolo-hdr-regexp)) (hyrolo-cache-set-major-mode (max (1- start) 1)) (hyrolo-cache-set-major-mode (min (1+ start) (point-max)))) @@ -3375,7 +3399,7 @@ proper major mode." (when (and (fboundp 'orgtbl-mode) orgtbl-mode) ;; Disable as overrides single letter keys (orgtbl-mode 0)) - ;; Need to leave point on a visible character or since + ;; !! TODO: Need to leave point on a visible character or since ;; hyrolo uses reveal-mode, redisplay will rexpand ;; hidden entries to make point visible. ;; (hyrolo-back-to-visible-point) @@ -3472,6 +3496,8 @@ Push (point-max) of `hyrolo-display-buffer' onto `hyrolo--cache-major-mode-indexes'. Ensure MATCHED-BUF's `major-mode' is stored in the hash table." (with-current-buffer hyrolo-display-buffer + (unless (hash-table-p hyrolo--cache-major-mode-to-index-hasht) + (hyrolo--cache-initialize)) (let* ((matched-buf-file-name (buffer-local-value 'buffer-file-name matched-buf)) (matched-buf-major-mode (or (hyrolo-major-mode-from-file-name matched-buf-file-name) (buffer-local-value 'major-mode matched-buf))) diff --git a/hywiki.el b/hywiki.el index 11872f0b4f..83fc41a824 100644 --- a/hywiki.el +++ b/hywiki.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 21-Acpr-24 at 22:41:13 -;; Last-Mod: 27-May-25 at 02:05:56 by Bob Weiner +;; Last-Mod: 28-May-25 at 01:15:45 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -439,6 +439,7 @@ where PATH is the un-resolvable reference." :html-postable-format '(("en" "<p class=\"author\">Author: %a (%e)</p> <p class=\"last-mod\">Last Modified: %C</p> <p class=\"creator\">%c</p>")) + :html-prefer-user-labels t :makeindex nil :publishing-directory hywiki-org-publishing-directory :publishing-function hywiki-org-publishing-function @@ -3540,13 +3541,13 @@ This must be called within a `save-excursion' or it may move point." "Return a unique reference for DATUM, as a string. Like `org-export-get-reference' but uses modified heading strings as link ids rather than generated ids. To form an id, spaces in headings -are replaces with dashes and to make each id unique, heading parent +are replaced with dashes and to make each id unique, heading parent ids are prepended separated by '--'. DATUM is either an element or an object. INFO is the current export state, as a plist. -References for the current document are stored in +References for the current document are stored in the `:internal-references' property. Its value is an alist with associations of the following types: diff --git a/test/hargs-tests.el b/test/hargs-tests.el index 17b518e4b6..1a67828a68 100644 --- a/test/hargs-tests.el +++ b/test/hargs-tests.el @@ -3,7 +3,7 @@ ;; Author: Mats Lidell <ma...@gnu.org> ;; ;; Orig-Date: 04-Feb-22 at 23:00:00 -;; Last-Mod: 25-Apr-25 at 19:57:44 by Mats Lidell +;; Last-Mod: 2-Jun-25 at 23:48:30 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -44,7 +44,7 @@ (ert-deftest hargs-get-verify-extension-characters-+K () "Verify hyperbole extension character +K is indentified." - (cl-letf (((symbol-function 'hargs:read) (lambda (prompt &optional a b c d) "xyz"))) + (cl-letf (((symbol-function 'hargs:read) (lambda (_prompt &optional _a _b _c _d) "xyz"))) (should (string= (hargs:get "+K: ") "xyz")))) (ert-deftest hargs-tests--sexpression-p ()