diff --git a/ChangeLog b/ChangeLog
index 7f4d1e62..4d4c3198 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,54 @@
+2026-02-19 Bob Weiner
+
+* hui.el (hui:link-possible-types): Fix to include link-to-wikiword only if
+ an existing referent is found.
+
+* hibtypes.el (action): Fix to check for angle bracket delimiters.
+
+* hywiki.el (hywiki-read-page-reference): Add and use in 'interactive' calls of
+ several functions.
+ (hywiki-word-is-p): Revert to using all 'string-match' calls
+ to prevent any regressions.
+ (hywiki-page-read): Add to read only existing HyWiki page names.
+ Not all existing WikiWords have page names, as there are other referent types.
+ (hywiki-consult-file-and-line): Rename to `hywiki-consult-page-and-line'.
+ (hywiki-page-exists-p): Add.
+ (hywiki-format-reference): Fix to accept only existing page references.
+ (hywiki-read-reference): Delete, use 'hywiki-word-read' instead.
+ (hywiki-add-page): Fix a number of issues where non-page WikiWords were
+ allowed.
+ (hywiki-page-read-new): Prevent selection of non-page HyWikiWords.
+
+* hui.el (require 'hywiki):
+ (hui:link-possible-types): Add 'link-to-wikiword' clause.
+* hbut.el (ibut:insert-text): Add 'actypes::link-to-wikiword' clause.
+* man/hyperbole.texi (By Dragging): Add HyWikiWord Reference to referent
+ context table.
+
+* hywiki.el (hywiki-consult-file-and-line): Add.
+ (hywiki-format-reference): Add and use with above function.
+ (hywiki-insert-reference): Add.
+ (hywiki-word-is-p): Use 'string-match-p' instead of 'string-match'.
+ (link-to-wikiword): Add this actype.
+ (hywiki-insert-link): Update to search for section links to insert
+ when 'consult' is available.
+ (require 'hui): Change to an 'eval-when-compile' so does not
+ trigger recursive requirements during normal loading.
+ hui-mini.el (hui:menu-hywiki): Unify with "hui-menu.el". Replace use of
+ 'hywiki-add-path-link' with 'hywiki-insert-link'.
+
+2026-02-17 Bob Weiner
+
+* hasht.el (hash-make): Fix to trigger an error if key is not given as a string.
+
+2026-02-16 Bob Weiner
+
+* hibtypes.el (markdown-follow-inline-link-p): Fix to handle URL arguments sent
+ when point is on the link title and not an included URL (so check if on
+ a URL fails).
+
+* README.md: Add Hyperbole Articles section.
+
2026-02-18 Mats Lidell
* hsys-org.el: require 'find-func for find-library--from-load-history.
diff --git a/README.md b/README.md
index 50d10248..2124a699 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,30 @@ otherwise, skip to the next section.
- [Linking personal info with implicit buttons](https://youtu.be/TQ_fG7b1iHI)
+## Articles
+
+ - [HyWiki: My Favorite Part of Hyperbole](https://kirankp.com/blog/gnu-hyperbole/)
+
+ - [Hyperbole VisionQuest Part 1](https://github.com/termitereform/JunkPile/blob/master/HyperboleNotes.org)
+
+ - [Hyperbole VisionQuest Part 2](https://github.com/termitereform/JunkPile/blob/master/HyperboleNotes2.org)
+
+ - [A Taste of Hyperbole](https://www.reddit.com/r/emacs/comments/1kty4mb/a_taste_of_hyperbole_automatically_linking_to_org/)
+
+ - [My Understanding of GNU Hyperbole](https://www.reddit.com/r/emacs/comments/nirwpk/my_understanding_of_gnu_hyperbole/)
+
+ - [What does GNU Hyperbole do?](https://tilde.town/~ramin_hal9001/articles/intro-to-hyperbole.html)
+
+ - [John Wiegley - The Philosophy Behind Hyperbole](https://mail.gnu.org/archive/html/hyperbole-users/2019-01/msg00037.html)
+
+ - [Daily ways GNU Hyperbole helps me stay in flow and reduces cognitive load](https://www.reddit.com/r/emacs/comments/jk3cn0/daily_ways_gnu_hyperbole_helps_me_stay_in_flow/)
+
+ - [Doing a Research Project and using GNU Hyperbole's Integrated Features](https://www.reddit.com/r/emacs/comments/1g2184d/doing_a_research_project_and_using_gnu_hyperboles/)
+
+ - [AI-generated Hyperbole Architectural Documentation](https://deepwiki.com/rswgnu/hyperbole)
+
+ - [Hypermedia and Hyperbole](https://www.mgmarlow.com/words/2023-10-26-hyperbole/)
+
## Summary
`GNU Hyperbole` (pronounced Ga-new Hi-per-bo-lee), or just `Hyperbole`, is
diff --git a/README.toc.md b/README.toc.md
index 18cb88f5..0231bc28 100644
--- a/README.toc.md
+++ b/README.toc.md
@@ -7,11 +7,10 @@
send us a thank you or a testimonial describing your usage if you like
Hyperbole to [rsw@gnu.org](mailto:rsw@gnu.org)].
-
-
- [GNU Hyperbole 9.0.2pre - The Everyday Hypertextual Information Manager](#gnu-hyperbole-902pre---the-everyday-hypertextual-information-manager)
- [Reference Manual](#reference-manual)
- [Videos](#videos)
+ - [Articles](#articles)
- [Summary](#summary)
- [Installation](#installation)
- [Invocation](#invocation)
@@ -26,8 +25,6 @@
- [User Quotes](#user-quotes)
- [Why was Hyperbole developed?](#why-was-hyperbole-developed)
-
-

## Reference Manual
@@ -74,6 +71,30 @@ otherwise, skip to the next section.
- [Linking personal info with implicit buttons](https://youtu.be/TQ_fG7b1iHI)
+## Articles
+
+ - [HyWiki: My Favorite Part of Hyperbole](https://kirankp.com/blog/gnu-hyperbole/)
+
+ - [Hyperbole VisionQuest Part 1](https://github.com/termitereform/JunkPile/blob/master/HyperboleNotes.org)
+
+ - [Hyperbole VisionQuest Part 2](https://github.com/termitereform/JunkPile/blob/master/HyperboleNotes2.org)
+
+ - [A Taste of Hyperbole](https://www.reddit.com/r/emacs/comments/1kty4mb/a_taste_of_hyperbole_automatically_linking_to_org/)
+
+ - [My Understanding of GNU Hyperbole](https://www.reddit.com/r/emacs/comments/nirwpk/my_understanding_of_gnu_hyperbole/)
+
+ - [What does GNU Hyperbole do?](https://tilde.town/~ramin_hal9001/articles/intro-to-hyperbole.html)
+
+ - [John Wiegley - The Philosophy Behind Hyperbole](https://mail.gnu.org/archive/html/hyperbole-users/2019-01/msg00037.html)
+
+ - [Daily ways GNU Hyperbole helps me stay in flow and reduces cognitive load](https://www.reddit.com/r/emacs/comments/jk3cn0/daily_ways_gnu_hyperbole_helps_me_stay_in_flow/)
+
+ - [Doing a Research Project and using GNU Hyperbole's Integrated Features](https://www.reddit.com/r/emacs/comments/1g2184d/doing_a_research_project_and_using_gnu_hyperboles/)
+
+ - [AI-generated Hyperbole Architectural Documentation](https://deepwiki.com/rswgnu/hyperbole)
+
+ - [Hypermedia and Hyperbole](https://www.mgmarlow.com/words/2023-10-26-hyperbole/)
+
## Summary
`GNU Hyperbole` (pronounced Ga-new Hi-per-bo-lee), or just `Hyperbole`, is
@@ -255,6 +276,8 @@ window control menu if it was not already bound prior to Hyperbole's
initialization. A long video demonstrating many of HyControl's
features is available at https://youtu.be/M3-aMh1ccJk.
+## Hyperbole Manual
+
The above are the best interactive ways to learn about Hyperbole.
Hyperbole also includes the Hyperbole Manual, a full reference manual,
not a simple introduction. It is included in the "man/" subdirectory
diff --git a/hasht.el b/hasht.el
index b20b0a05..4ad4af71 100644
--- a/hasht.el
+++ b/hasht.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 16-Mar-90 at 03:38:48
-;; Last-Mod: 30-Dec-25 at 14:42:14 by Mats Lidell
+;; Last-Mod: 17-Feb-26 at 22:42:00 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -159,16 +159,20 @@ merge all the values for a given instead."
key value sym)
(if reverse
(mapc (lambda (cns)
- (when (consp cns)
- (setq key (car cns) value (cdr cns)))
- (when (setq sym (intern key))
- (puthash sym value hash-table)))
+ (if (consp cns)
+ (setq key (car cns) value (cdr cns))
+ (setq key nil value nil))
+ (if (and (stringp key) (setq sym (intern key)))
+ (puthash sym value hash-table)
+ (error "(hash-make): 'key' must be a string, not %S" key)))
initializer)
(mapc (lambda (cns)
- (when (consp cns)
- (setq key (cdr cns) value (car cns)))
- (when (setq sym (intern key))
- (puthash sym value hash-table)))
+ (if (consp cns)
+ (setq key (cdr cns) value (car cns))
+ (setq key nil value nil))
+ (if (and (stringp key) (setq sym (intern key)))
+ (puthash sym value hash-table)
+ (error "(hash-make): 'key' must be a string, not %S" key)))
initializer))
hash-table))))
diff --git a/hbut.el b/hbut.el
index 96c53c23..850fe36b 100644
--- a/hbut.el
+++ b/hbut.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 18-Sep-91 at 02:57:09
-;; Last-Mod: 30-Dec-25 at 23:55:08 by Mats Lidell
+;; Last-Mod: 18-Feb-26 at 23:49:33 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -2722,6 +2722,11 @@ Summary of operations based on inputs (name arg from \\='hbut:current attrs):
(insert "<" arg1 ">"))))
('actypes::link-to-org-id (insert (format "\"id:%s\"" arg1)))
('actypes::link-to-rfc (insert (format "rfc%d" arg1)))
+ ('actypes::link-to-wikiword (insert (if (and (stringp arg1)
+ (string-match-p "\\s-" arg1))
+ ;; Double-quote when has a space
+ (format "\"%s\"" arg1)
+ arg1)))
('man (insert arg1))
('actypes::man-show (insert arg1))
('actypes::link-to-file-line (insert (format "\"%s:%d\""
diff --git a/hibtypes.el b/hibtypes.el
index 3c465fa4..1786e178 100644
--- a/hibtypes.el
+++ b/hibtypes.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 19-Sep-91 at 20:45:31
-;; Last-Mod: 2-Feb-26 at 18:22:19 by Bob Weiner
+;; Last-Mod: 19-Feb-26 at 21:16:16 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -527,15 +527,17 @@ Return t if jump and nil otherwise."
"If on an inline link, jump to its referent if it is absolute and return non-nil.
Absolute means not relative within the file. Otherwise, if an
internal link, move back to OPOINT and return nil."
- ;; Caller already checked not on a URL (handled elsewhere).
(let ((path (markdown-link-url)))
(goto-char opoint)
(when (markdown-link-p)
(ibut:label-set (match-string-no-properties 0) (match-beginning 0) (match-end 0))
- (if path
- (hact 'link-to-file path)
- (hpath:display-buffer (current-buffer))
- (hact 'markdown-follow-link-at-point)))))
+ (cond ((hpath:url-p path)
+ (hact 'www-url path))
+ (path
+ (hact 'link-to-file path))
+ (t
+ (hpath:display-buffer (current-buffer))
+ (hact 'markdown-follow-link-at-point))))))
(defib markdown-internal-link ()
"Display any in-file Markdown link referent at point.
@@ -1606,110 +1608,112 @@ first identifier in the expression must be an Elisp variable,
action type, function symbol to call or test to execute, i.e.
'<'actype-or-elisp-symbol arg1 ... argN '>'. For example,
."
- (let ((hbut:max-len 0)
- (lbl-key (hattr:get 'hbut:current 'lbl-key))
- (name (hattr:get 'hbut:current 'name))
+ (let ((lbl-key (hattr:get 'hbut:current 'lbl-key))
(start-pos (hattr:get 'hbut:current 'lbl-start))
- (end-pos (hattr:get 'hbut:current 'lbl-end))
- (testing-flag (when (bound-and-true-p ert--running-tests) t))
- actype actype-sym action args lbl var-flag)
-
- ;; Continue only if there if there is a button label and one of:
- ;; 1. `ert--running-tests' is non-nil
- ;; 2. character after start-delim is not a whitespace character
+ (end-pos (hattr:get 'hbut:current 'lbl-end)))
(when (and lbl-key
- (or testing-flag
- (not (memq (if (char-after (1+ start-pos))
- (char-syntax (char-after (1+ start-pos)))
- 0)
- '(?\ ?\>)))))
- (setq lbl (ibut:key-to-label lbl-key))
- ;; Handle $ preceding var name in cases where same name is
- ;; bound as a function symbol
- (when (string-match "\\`\\$" lbl)
- (setq var-flag t
- lbl (substring lbl 1)))
- (setq actype (if (string-match-p " " lbl) (car (split-string lbl)) lbl)
- actype-sym (or (actype:elisp-symbol actype) (intern-soft actype))
- ;; Must ignore that (boundp nil) would be t here.
- actype (and actype-sym
- (or (fboundp actype-sym) (boundp actype-sym)
- (special-form-p actype-sym)
- (ert-test-boundp actype-sym))
- actype-sym))
- (when actype
- ;; For buttons, need to double quote each argument so
- ;; 'read' does not change the idstamp 02 to 2.
- (when (and (memq actype '(hy hynote))
- (string-match-p " " lbl))
- (setq lbl (replace-regexp-in-string "\"\\(.*\\)\\'" "\\1\""
- (combine-and-quote-strings
- (split-string lbl) "\" \""))))
- (setq action (read (concat "(" lbl ")"))
- args (cdr action))
- ;; Ensure action uses an fboundp symbol if executing a
- ;; Hyperbole actype.
- (when (and (car action) (symbolp (car action)))
- (setcar action (or (symtable:hyperbole-actype-p (car action))
- (car action))))
- (unless assist-flag
- (cond ((and (symbolp actype) (fboundp actype)
- (string-match "-p\\'" (symbol-name actype)))
- ;; Is a function with a boolean result
- (setq actype #'display-boolean
- args `(',action)))
- ((and (null args) (symbolp actype) (boundp actype)
- (or var-flag (not (fboundp actype))))
- ;; Is a variable, display its value as the action
- (setq args `(,actype)
- actype #'display-variable))
- ((and (null args) (symbolp actype) (ert-test-boundp actype))
- ;; Is an ert-deftest, display the value from executing it
- (setq actype #'display-value
- args `((hypb-ert-run-test ,lbl))))
- (t
- ;; All other expressions, display the action result in the minibuffer
- (if (string-match "\\b\\(delete\\|kill\\)-region\\'"
- (symbol-name actype-sym))
- ;; With `delete-region' and `kill-region'
- ;; actions, if no args, either use any active
- ;; region or when none, use the region of the
- ;; action button itself, removing it from the
- ;; buffer. The latter action is largely used
- ;; only in internal HyWiki tests.
- (progn (setq actype #'display-value)
- (if (= 1 (length action)) ;; No args
- (if (use-region-p)
- ;; Apply function to the active region
- (setq args `((,actype-sym (region-beginning) (region-end))))
- ;; Apply function to region of the action button itself,
- ;; including delimiters
- (setq args `((,actype-sym ,start-pos
- ,end-pos))))
- (setq args `(',action))))
- (if testing-flag
- ;; Delete action button after activation when
- ;; running an ert test or in a string (so can
- ;; test this behavior interactively),
- (setq actype #'display-value-and-remove-region
- args `(,action ,start-pos ,end-pos))
+ (eq (char-after start-pos) ?\<)
+ (eq (char-before end-pos) ?\>))
+ (let ((hbut:max-len 0)
+ (name (hattr:get 'hbut:current 'name))
+ (testing-flag (when (bound-and-true-p ert--running-tests) t))
+ actype actype-sym action args lbl var-flag)
+
+ ;; Continue only if there if there is one of:
+ ;; 1. `ert--running-tests' is non-nil
+ ;; 2. character after start-delim is not a whitespace character
+ (when (and (or testing-flag
+ (not (memq (if (char-after (1+ start-pos))
+ (char-syntax (char-after (1+ start-pos)))
+ 0)
+ '(?\ ?\>)))))
+ (setq lbl (ibut:key-to-label lbl-key))
+ ;; Handle $ preceding var name in cases where same name is
+ ;; bound as a function symbol
+ (when (string-match "\\`\\$" lbl)
+ (setq var-flag t
+ lbl (substring lbl 1)))
+ (setq actype (if (string-match-p " " lbl) (car (split-string lbl)) lbl)
+ actype-sym (or (actype:elisp-symbol actype) (intern-soft actype))
+ ;; Must ignore that (boundp nil) would be t here.
+ actype (and actype-sym
+ (or (fboundp actype-sym) (boundp actype-sym)
+ (special-form-p actype-sym)
+ (ert-test-boundp actype-sym))
+ actype-sym))
+ (when actype
+ ;; For buttons, need to double quote each argument so
+ ;; 'read' does not change the idstamp 02 to 2.
+ (when (and (memq actype '(hy hynote))
+ (string-match-p " " lbl))
+ (setq lbl (replace-regexp-in-string "\"\\(.*\\)\\'" "\\1\""
+ (combine-and-quote-strings
+ (split-string lbl) "\" \""))))
+ (setq action (read (concat "(" lbl ")"))
+ args (cdr action))
+ ;; Ensure action uses an fboundp symbol if executing a
+ ;; Hyperbole actype.
+ (when (and (car action) (symbolp (car action)))
+ (setcar action (or (symtable:hyperbole-actype-p (car action))
+ (car action))))
+ (unless assist-flag
+ (cond ((and (symbolp actype) (fboundp actype)
+ (string-match "-p\\'" (symbol-name actype)))
+ ;; Is a function with a boolean result
+ (setq actype #'display-boolean
+ args `(',action)))
+ ((and (null args) (symbolp actype) (boundp actype)
+ (or var-flag (not (fboundp actype))))
+ ;; Is a variable, display its value as the action
+ (setq args `(,actype)
+ actype #'display-variable))
+ ((and (null args) (symbolp actype) (ert-test-boundp actype))
+ ;; Is an ert-deftest, display the value from executing it
(setq actype #'display-value
- args `(,action)))))))
-
- ;; Create implicit button object and store in symbol hbut:current.
- (ibut:label-set lbl)
- (ibut:create :name name :lbl-key lbl-key :lbl-start start-pos
- :lbl-end end-pos :categ 'ibtypes::action :actype actype
- :args args)
-
- ;; Necessary so can return a null value, which actype:act cannot.
- (let ((hrule:action
- (if (eq hrule:action #'actype:identity)
- #'actype:identity
- #'actype:eval)))
- (if (eq hrule:action #'actype:identity)
- `(hact ',actype ,@args)
- `(hact ',actype ,@(mapcar #'eval args))))))))
+ args `((hypb-ert-run-test ,lbl))))
+ (t
+ ;; All other expressions, display the action result in the minibuffer
+ (if (string-match "\\b\\(delete\\|kill\\)-region\\'"
+ (symbol-name actype-sym))
+ ;; With `delete-region' and `kill-region'
+ ;; actions, if no args, either use any active
+ ;; region or when none, use the region of the
+ ;; action button itself, removing it from the
+ ;; buffer. The latter action is largely used
+ ;; only in internal HyWiki tests.
+ (progn (setq actype #'display-value)
+ (if (= 1 (length action)) ;; No args
+ (if (use-region-p)
+ ;; Apply function to the active region
+ (setq args `((,actype-sym (region-beginning) (region-end))))
+ ;; Apply function to region of the action button itself,
+ ;; including delimiters
+ (setq args `((,actype-sym ,start-pos
+ ,end-pos))))
+ (setq args `(',action))))
+ (if testing-flag
+ ;; Delete action button after activation when
+ ;; running an ert test or in a string (so can
+ ;; test this behavior interactively),
+ (setq actype #'display-value-and-remove-region
+ args `(,action ,start-pos ,end-pos))
+ (setq actype #'display-value
+ args `(,action)))))))
+
+ ;; Create implicit button object and store in symbol hbut:current.
+ (ibut:label-set lbl)
+ (ibut:create :name name :lbl-key lbl-key :lbl-start start-pos
+ :lbl-end end-pos :categ 'ibtypes::action :actype actype
+ :args args)
+
+ ;; Necessary so can return a null value, which actype:act cannot.
+ (let ((hrule:action
+ (if (eq hrule:action #'actype:identity)
+ #'actype:identity
+ #'actype:eval)))
+ (if (eq hrule:action #'actype:identity)
+ `(hact ',actype ,@args)
+ `(hact ',actype ,@(mapcar #'eval args))))))))))
(defun action:help (hbut)
"Display documentation for action button at point.
diff --git a/hsys-consult.el b/hsys-consult.el
index 45340ccc..13d11296 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: 30-Dec-25 at 14:42:23 by Mats Lidell
+;; Last-Mod: 19-Feb-26 at 00:52:16 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -43,8 +43,10 @@
(declare-function hsys-org-directory-at-tags-p "hsys-org")
(declare-function hsys-org-at-tags-p "hsys-org")
+(declare-function consult--async-command "ext:consult")
(declare-function consult--grep "ext:consult")
(declare-function consult--grep-make-builder "ext:consult")
+(declare-function consult--lookup-member "ext:consult")
(declare-function consult--read "ext:consult")
(declare-function consult--ripgrep-make-builder "ext:consult")
(declare-function consult-grep "ext:consult")
diff --git a/hui-mini.el b/hui-mini.el
index edd5d7c3..8c307dc2 100644
--- a/hui-mini.el
+++ b/hui-mini.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 15-Oct-91 at 20:13:17
-;; Last-Mod: 1-Jan-26 at 18:18:17 by Mats Lidell
+;; Last-Mod: 19-Feb-26 at 00:16:53 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -1069,8 +1069,8 @@ support underlined faces as well."
"Report on a HyWikiWord's attributes or HyWikiWords in general.")
'("Info" (id-info "(hyperbole)HyWiki")
"Display Hyperbole manual section on HyWiki.")
- '("Link" hywiki-add-path-link
- "Prompt for and add a HyWikiWord that links to a path and possible position.")
+ '("Link" hywiki-insert-link
+ "Prompt for and insert at point a HyWikiWord#section reference.")
'("ModeSet/" (menu . cust-hywiki-mode)
"Set hywiki-mode state to determine where HyWikiWord references are recognized.")
'("Org-M-RET/" (menu . cust-org)
diff --git a/hui.el b/hui.el
index 9d90f070..3486e7a8 100644
--- a/hui.el
+++ b/hui.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 19-Sep-91 at 21:42:03
-;; Last-Mod: 31-Dec-25 at 16:02:19 by Mats Lidell
+;; Last-Mod: 19-Feb-26 at 21:32:21 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -37,8 +37,10 @@
(defvar completion-to-accept) ; "completion.el"
(defvar hyperbole-mode-map) ; "hyperbole.el"
+(declare-function actypes::link-to-wikiword "hywiki")
(declare-function bookmark-bmenu-bookmark "bookmark")
(declare-function hui:menu-choose "hui-mini")
+(declare-function hywiki-referent-exists-p "hywiki")
(declare-function kcell-view:absolute-reference "kotl/kview")
(declare-function kcell-view:idstamp "kotl/kview")
(declare-function klink:absolute "kotl/klink")
@@ -2010,6 +2012,7 @@ possible types.
Referent Context Possible Link Type Returned
----------------------------------------------------
+HyWikiWord Reference link-to-wikiword
Org Roam or Org Id link-to-org-id
Global Button link-to-gbut
Explicit Button link-to-ebut
@@ -2034,7 +2037,9 @@ Buffer without File link-to-buffer-tmp"
hbut-sym
lbl-key)
(prog1 (delq nil
- (list (cond ((and (featurep 'org-id)
+ (list (cond ((let ((ref (hywiki-referent-exists-p)))
+ (and ref (list 'link-to-wikiword ref))))
+ ((and (featurep 'org-id)
(cond ((save-excursion
(beginning-of-line)
(when (looking-at "[ \t]*:\\(CUSTOM_\\)?ID:[ \t]+\\([^ \t\r\n\f]+\\)")
diff --git a/hywiki.el b/hywiki.el
index 0579093c..b48e1caf 100644
--- a/hywiki.el
+++ b/hywiki.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 21-Apr-24 at 22:41:13
-;; Last-Mod: 15-Feb-26 at 20:39:33 by Bob Weiner
+;; Last-Mod: 19-Feb-26 at 19:29:13 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -38,7 +38,7 @@
;; - non-special text buffers, when `hywiki-mode' is enabled;
;; - comments and strings in programming buffers, when
;; `hywiki-mode' is enabled.
-;;
+;;
;; As HyWikiWords are typed, highlighting occurs after a trailing
;; whitespace or punctuation character is added, or when it is
;; surrounded by a matching pair of characters such as curly braces
@@ -105,7 +105,7 @@
;; properties with Org's publishing framework, so when in a HyWiki
;; page, you can use the standard {C-c C-e P p} current project publish
;; command.
-;;
+;;
;; There are a few publishing settings you can customize prior to
;; loading Hyperbole's HyWiki code.
;;
@@ -223,7 +223,7 @@ See `current-time' function for the mod time format.")
(defvar hywiki--pages-directory nil)
(defvar hywiki--referent-alist nil
"HyWiki alist generated from `hywiki--referent-hasht' for storage in cache.
-Each element is of the form: (wikiword . (referent-type . referent-value)).")
+Each element is of the form: (\"wikiword\" . (referent-type . referent-value)).")
(defvar hywiki--referent-hasht nil
"HyWiki hash table for fast WikiWord referent lookup.")
@@ -945,9 +945,10 @@ Function used to display is \"hywiki-display-\"."
(defun hywiki-display-referent (&optional wikiword prompt-flag)
"Display HyWiki WIKIWORD referent or a regular file with WIKIWORD nil.
Return the WIKIWORD's referent if successfully found or nil otherwise.
-The referent is a cons of ( . ).
+
For further details, see documentation for `hywiki-find-referent'.
After successfully finding a referent, run `hywiki-display-referent-hook'."
+ (interactive (list (hywiki-read-page-reference)))
(let ((in-page-flag (null wikiword))
(in-hywiki-directory-flag (hywiki-in-page-p)))
(if (or (stringp wikiword) in-hywiki-directory-flag)
@@ -1399,16 +1400,15 @@ After successfully adding a page, run `hywiki-add-page-hook'.
Use `hywiki-get-referent' to determine whether a HyWiki page exists."
(interactive (list (or (hywiki-word-at)
- (hywiki-word-read-new "Add/Edit HyWiki page: "))
+ (hywiki-page-read-new "Add/Edit HyWiki page: "))
current-prefix-arg))
(if (hywiki-word-is-p page-name)
(when (or noninteractive
(not (hash-empty-p (hywiki-get-referent-hasht)))
(hyperb:stack-frame '(ert-run-test))
(y-or-n-p (concat "Create new HyWiki page `" page-name "'? ")))
- (when (match-string-no-properties 2 page-name)
- ;; Remove any #section suffix in PAGE-NAME.
- (setq page-name (match-string-no-properties 1 page-name)))
+ ;; Remove any #section suffix in PAGE-NAME.
+ (setq page-name (hywiki-get-singular-wikiword page-name))
(let* ((page-file (hywiki-get-page-file page-name))
(page-file-readable (file-readable-p page-file))
@@ -1484,8 +1484,7 @@ exists."
prompt-flag)))
(defun hywiki-completion-at-point ()
- "Complete HyWiki references.
-Ensures that selecting a completion replaces only the text after the '#'."
+ "Complete HyWiki references, either the HyWikiWord or the #section."
(let ((ref-start-end (and (hywiki-active-in-current-buffer-p)
(not (hywiki-non-hook-context-p))
(hywiki-word-at t t))))
@@ -1498,7 +1497,7 @@ Ensures that selecting a completion replaces only the text after the '#'."
;; Extract the WikiWord before the '#'
(word (hywiki-word-from-reference ref)))
(save-excursion
- ;; 1. Look for the '#' delimiter on the current line
+ ;; CASE 1. Look for the '#' delimiter on the current line
(if (re-search-backward "#" start t)
(let ((hash-pos (point))
(page (expand-file-name (concat word ".org") hywiki-directory)))
@@ -1648,6 +1647,43 @@ nil, else return \\='(page . \"\")."
(and (or at-tag-flag (hsys-org-at-tags-p))
(or (hywiki-in-page-p) (string-prefix-p "*HyWiki Tags*" (buffer-name)))))
+(defun hywiki-consult-page-and-line ()
+ "Return a list of the file and line selected by consult or nil.
+Use `hywiki-insert-reference' with the result of this function to insert a
+double-quoted HyWikiWord reference at point."
+ (interactive)
+ (let* ((dir (expand-file-name hywiki-directory))
+ (manual-builder
+ (lambda (input)
+ (let* (;; Define the regex inside the builder so it's always in scope
+ (headline-pattern (concat "^\\* .*" input))
+ ;; Ensure all arguments are evaluated as strings
+ (args (list "rg"
+ "--null"
+ "--line-buffered"
+ "--color=never"
+ "--with-filename"
+ "--line-number"
+ "--smart-case"
+ "-g" "*.org"
+ "-e" headline-pattern
+ dir)))
+ (cons args dir))))
+ (selected (consult--read
+ (consult--async-command manual-builder)
+ :prompt "HyWiki Headline: "
+ :require-match t
+ :lookup #'consult--lookup-member
+ :category 'consult-grep)))
+
+ (when (stringp selected)
+ (if (string-match "\\`\\([^\0]+\\)\0\\([0-9]+\\):\\(.*\\)" selected)
+ (let ((file (match-string 1 selected))
+ (line (match-string 3 selected)))
+ (list file line))
+ (message "(hwiki-consult-file-and-line): Parse error on: %s" selected)
+ nil))))
+
;;;###autoload
(defun hywiki-consult-grep (&optional regexp max-matches path-list prompt)
"Interactively search with a consult package grep command.
@@ -2041,12 +2077,62 @@ This includes the delimiters: (), {}, <>, [] and \"\" (double quotes)."
result
(list nil nil))))))
+(defun hywiki-read-page-reference ()
+ "With consult package loaded, read a \"file^@line\" string, else a page name."
+ (interactive)
+ (if (featurep 'consult)
+ (hywiki-format-reference (hywiki-consult-page-and-line))
+ ;; Without consult, can only complete to a HyWiki page
+ ;; without a section
+ (hywiki-page-read "Link to HyWiki page: ")))
+
;;;###autoload
(defun hywiki-insert-link ()
- "Insert at point a link to a HyWiki page."
+ "Insert at point a link to a HyWiki page#section."
(interactive "*")
- (insert (hywiki-word-read "Link to HyWiki page: "))
- (hywiki-maybe-highlight-reference))
+ (let ((ref (hywiki-read-page-reference)))
+ (when ref
+ (insert ref)
+ (skip-chars-backward "\"")
+ (goto-char (1- (point)))
+ (hywiki-maybe-highlight-reference))))
+
+;;;###autoload
+(defun hywiki-format-reference (page-and-line)
+ "Return a HyWikiWord#section reference from PAGE-AND-LINE.
+Call `hywiki-consult-page-and-line' to generate PAGE-AND-LINE.
+Add double quotes if the section contains any whitespace after trimming.
+
+Return t if PAGE-AND-LINE is a valid list, else nil. If the page name
+therein is invalid, trigger an error."
+ (when (and page-and-line (listp page-and-line))
+ (cl-destructuring-bind (page line)
+ page-and-line
+ (setq page (file-name-base page))
+ (unless (and (string-match-p hywiki-word-regexp page)
+ (hywiki-page-exists-p page))
+ (error "(hywiki-format-reference): Invalid HyWiki page name - \"%s\""
+ page))
+ ;; Drop '* ' prefix
+ (setq line (string-trim line "[ \t\n\r]*\\**[ \t\n\r]+"))
+ (format (if (string-match-p "\\s-" line)
+ "\"%s#%s\""
+ "%s#%s")
+ page
+ line))))
+
+(defun hywiki-insert-reference (page-and-line)
+ "Insert a HyWiki page#section reference from PAGE-AND-LINE.
+Add double quotes if the section contains any whitespace after trimming.
+
+Return t if PAGE-AND-LINE is a valid list, else nil. If the page name
+therein is invalid, trigger an error."
+ (let ((ref (hywiki-format-reference page-and-line)))
+ (when ref
+ (insert ref)
+ (skip-chars-backward "\"")
+ (goto-char (1- (point)))
+ t)))
(defun hywiki-maybe-dehighlight-balanced-pairs ()
"Before or after a balanced delimiter, dehighlight HyWikiWords within.
@@ -3233,36 +3319,37 @@ Customize this directory with:
;; spaces replaced with dashes, made unique when necessary.
(org-publish-project "hywiki" all-pages-flag))
-(defun hywiki-referent-exists-p (&optional word start end)
- "Return the HyWikiWord at point or optional HyWiki WORD, if has a referent.
+(defun hywiki-referent-exists-p (&optional ref start end)
+ "Return the HyWiki reference at point or optional REF, if has a referent.
If no such referent exists, return nil.
-The HyWikiWord may be of the form:
+The HyWikiWord reference may be of the form:
1. HyWikiWord#section with an optional #section.
- 2. If WORD is the symbol, :range, and there is a HyWikiWord at point
- with an existing referent, return the tuple of values: \='(
- ) instead of the word; otherwise, return the
- tuple \='(nil nil nil).
-
-When using the word at point, a call to `hywiki-active-in-current-buffer-p'
-at point must return non-nil or this function will return nil."
- (let ((save-input-word word))
- (when (stringp word)
- (setq word (hywiki-strip-org-link word)))
- (if (or (stringp word)
- (setq word (hywiki-word-get-range)))
- (unless (hywiki-get-referent (if (stringp word) word (nth 0 word)))
- (setq word nil))
- (setq word nil))
- (when (and (listp word) (= (length word) 3))
- (setq start (nth 1 word)
- end (nth 2 word)
- ;; `word' must be set last so list version can be referenced
+ 2. If REF is the symbol, :range, and there is a HyWikiWord at point
+ with an existing referent, return the tuple of values: \='([
+ ) instead of the reference alone; otherwise,
+ return the tuple \='(nil nil nil).
+
+When using the reference at point, a call to
+`hywiki-active-in-current-buffer-p' at point must return non-nil or this
+function will return nil."
+ (let ((save-input-word ref))
+ (when (stringp ref)
+ (setq ref (hywiki-strip-org-link ref)))
+ (if (or (stringp ref)
+ (setq ref (hywiki-word-get-range)))
+ (unless (hywiki-get-referent (if (stringp ref) ref (nth 0 ref)))
+ (setq ref nil))
+ (setq ref nil))
+ (when (and (listp ref) (= (length ref) 3))
+ (setq start (nth 1 ref)
+ end (nth 2 ref)
+ ;; `ref' must be set last so list version can be referenced
;; first above
- word (nth 0 word)))
+ ref (nth 0 ref)))
(if (eq save-input-word :range)
- (list word start end)
- word)))
+ (list ref start end)
+ ref)))
(defun hywiki-section-to-headline-reference ()
"Replace file#section dashes with spaces to match to an Org headline.
@@ -3742,11 +3829,10 @@ these are handled by the Org mode link handler."
(eq 0 (string-match
hywiki-word-with-optional-suffix-exact-regexp
word)))
- (save-match-data
- ;; If has a #section, ensure there are no invalid chars
- (if (string-match "#" word)
- (string-match "#[^][#()<>{}\"\n\r\f]+\\'" word)
- t))))))
+ ;; If has a #section, ensure there are no invalid chars
+ (if (string-match-p "#" word)
+ (string-match "#[^][#()<>{}\"\n\r\f]+\\'" word)
+ t)))))
(defun hywiki-word-read (&optional prompt)
"Prompt with completion for and return an existing HyWikiWord.
@@ -3764,13 +3850,35 @@ If point is on one, press RET immediately to use that one."
(hywiki-get-referent-hasht)
nil nil nil nil (hywiki-word-at-point))))
-(defun hywiki-page-read-new (&optional prompt)
- "Prompt with completion for and return an existing/new HyWikiWord with a page.
+(defun hywiki-page-exists-p (word)
+ "Return HyWiki WORD iff it is an existing page reference."
+ (when (eq (car (hywiki-get-referent word)) 'page)
+ word))
+
+(defun hywiki-page-read (&optional prompt)
+ "Prompt with completion for and return an existing HyWiki page name.
If point is on one, press RET immediately to use that one."
- (let ((completion-ignore-case t))
- (completing-read (if (stringp prompt) prompt "HyWikiWord page: ")
+ (let* ((completion-ignore-case t)
+ (wikiword (hywiki-word-at-point))
+ (page (hywiki-page-exists-p wikiword)))
+ (completing-read (if (stringp prompt) prompt "HyWiki page: ")
(hywiki-get-page-list)
- nil nil nil nil (hywiki-word-at-point))))
+ nil t nil nil (when page wikiword))))
+
+(defun hywiki-page-read-new (&optional prompt)
+ "Prompt with completion for and return an existing/new HyWiki page name.
+If point is on one, press RET immediately to use that one."
+ (let ((completion-ignore-case t)
+ page)
+ (while (null page)
+ (setq page (completing-read
+ (if (stringp prompt) prompt "HyWiki page: ")
+ (hywiki-get-page-list)
+ nil nil nil nil (hywiki-word-at-point)))
+ ;; Prevent selection of non-page HyWikiWords
+ (unless (memq (car (hywiki-get-referent page)) '(page nil))
+ (setq page nil)))
+ page))
(defun hywiki-word-set-auto-highlighting (hywiki-from-mode hywiki-to-mode)
"Set HyWikiWord auto-highlighting based on HYWIKI-FROM-MODE HYWIKI-TO-MODE.
@@ -3799,7 +3907,7 @@ occurs with one of these hooks, the problematic hook is removed."
(hywiki-word-highlight-buffers
(set:difference (hywiki-get-buffers hywiki-from-mode)
(hywiki-get-buffers hywiki-to-mode))))
- (t
+ (t
(error "(hywiki-word-set-auto-highlighting): Inputs must be nil, :pages or :all, not '%s' and '%s'"
hywiki-from-mode hywiki-to-mode))))
@@ -3885,6 +3993,11 @@ completion or no completion xandidates are returned."
(when (called-interactively-p 'interactive)
(message "HyWikiWord auto-highlighting disabled")))
+(defact link-to-wikiword (reference)
+ "Display the HyWikiword referent matching WikiWord#section REFERENCE."
+ (interactive (list (hywiki-word-read "Link to HyWiki word: ")))
+ (hywiki-find-referent reference))
+
;;; ************************************************************************
;;; Private functions
;;; ************************************************************************
diff --git a/kotl/kotl-mode.el b/kotl/kotl-mode.el
index 76f4b08d..b997d739 100644
--- a/kotl/kotl-mode.el
+++ b/kotl/kotl-mode.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 6/30/93
-;; Last-Mod: 19-Jan-26 at 22:34:04 by Mats Lidell
+;; Last-Mod: 19-Feb-26 at 01:12:25 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -2491,8 +2491,10 @@ Optional prefix arg RELATIVE-LEVEL means one of the following:
1. when = 0, add as the parent's first child cell (first cell in list);
2. when < 0, add that number of cells as preceding siblings;
- 3. when \\='(4) (universal arg, \\`C-u'), add as the first child of the current cell;
- 4. when > 0 or nil (meaning 1), add that number of cells as following siblings."
+ 3. when \\='(4) (universal arg, \\`C-u'), add as the first child of the
+ current cell;
+ 4. when > 0 or nil (meaning 1), add that number of cells as following
+ siblings."
(interactive "*P")
(unless (or (integerp relative-level) (listp relative-level) )
(error "(kotl-mode:add-cell): `relative-level' must be an integer or a list of integers, not '%s'" relative-level))
diff --git a/man/hyperbole.texi b/man/hyperbole.texi
index 6d630542..ef83bcf8 100644
--- a/man/hyperbole.texi
+++ b/man/hyperbole.texi
@@ -7,7 +7,7 @@
@c Author: Bob Weiner
@c
@c Orig-Date: 6-Nov-91 at 11:18:03
-@c Last-Mod: 7-Feb-26 at 10:10:33 by Bob Weiner
+@c Last-Mod: 18-Feb-26 at 23:43:55 by Bob Weiner
@c %**start of header (This is for running Texinfo on a region.)
@setfilename hyperbole.info
@@ -30,7 +30,7 @@
@set txicodequoteundirected
@set txicodequotebacktick
-@set UPDATED February 7, 2026
+@set UPDATED February 18, 2026
@set UPDATED-MONTH February 2026
@set EDITION 9.0.2pre
@set VERSION 9.0.2pre
@@ -171,7 +171,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.]
Edition 9.0.2pre
-Printed February 7, 2026.
+Printed February 18, 2026.
Published by the Free Software Foundation, Inc.
Author: Bob Weiner
@@ -213,7 +213,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
@example
Edition 9.0.2pre
-February 7, 2026 @c AUTO-REPLACE-ON-SAVE
+February 18, 2026 @c AUTO-REPLACE-ON-SAVE
Published by the Free Software Foundation, Inc.
@@ -3887,6 +3887,7 @@ upon the referent context in which the Action Key is released.
@example
Referent Context Link Type
----------------------------------------------------
+HyWikiWord Reference link-to-wikiword
Org Roam or Org Id link-to-org-id
Global Button link-to-gbut
Explicit Button link-to-ebut