branch: externals/hyperbole
commit 8eaf97e901ab88704d8266daaf0a67309ba23850
Merge: c1636759c0 ad181e27e0
Author: Robert Weiner <r...@gnu.org>
Commit: GitHub <nore...@github.com>

    Merge pull request #733 from rswgnu/rsw
    
    hywiki-word-at - Allow #section spaces only in single delim WikiWord ref
---
 ChangeLog |  20 +++++
 hargs.el  |  11 +--
 hypb.el   |  24 ++++--
 hywiki.el | 292 +++++++++++++++++++++++++++++++++-----------------------------
 4 files changed, 199 insertions(+), 148 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 6e1f1e44bc..624b898e65 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2025-05-23  Bob Weiner  <r...@gnu.org>
+
+* hywiki.el (hywiki-word-with-optional-suffix-regexp): Change #section
+    expression ordinality from "??" to "?" to get longest rather than
+    shortest match.
+            (hywiki-word-with-optional-suffix-exact-regexp): Fix to not
+    allow # within a section.
+            (hywiki-word-at): Fix to allow spaces in #section only if the
+    delimited string is a single HyWikiWord reference.
+
+2025-05-22  Bob Weiner  <r...@gnu.org>
+
+* hypb.el (hypb:in-string-p): Doc optional MAX-LINES arg.  Add optional
+    RANGE-FLAG arg that returns (string-matched start-pos end pos) for
+    a match.
+
+* hywiki.el (hywiki-delimited-p): Ensure string check is limited to 2-lines
+    to match limit in the documentation.  Make any non-nil value returned
+    be a list of (string-matched start-pos end-pos).
+
 2025-05-20  Mats Lidell  <ma...@gnu.org>
 
 * test/kotl-mode-tests.el (kotl-mode--add-prior-cell): Add test.
diff --git a/hargs.el b/hargs.el
index d22f3da1fc..9b800c650a 100644
--- a/hargs.el
+++ b/hargs.el
@@ -3,7 +3,7 @@
 ;; Author:       Bob Weiner
 ;;
 ;; Orig-Date:    31-Oct-91 at 23:17:35
-;; Last-Mod:     27-Feb-25 at 21:21:40 by Bob Weiner
+;; Last-Mod:     22-May-25 at 22:49:03 by Bob Weiner
 ;;
 ;; SPDX-License-Identifier: GPL-3.0-or-later
 ;;
@@ -128,10 +128,11 @@ line.  START-DELIM and END-DELIM are strings that specify 
the
 argument delimiters.  With optional START-REGEXP-FLAG non-nil,
 START-DELIM is treated as a regular expression.  END-REGEXP-FLAG
 is similar.  With optional LIST-POSITIONS-FLAG, return list
-of (string-matched start-pos end-pos).  Optional
-EXCLUDE-REGEXP is compared against the match string with its delimiters
-included; any string that matches this regexp is ignored.  With optional
-AS-KEY = \\='none, return t rather than the string result.  Any other
+of (string-matched start-pos end-pos), where the positions
+exclude the delimiters.  Optional EXCLUDE-REGEXP is compared
+against the match string with its delimiters included; any string
+that matches this regexp is ignored.  With optional AS-KEY =
+\\='none, return t rather than the string result.  Any other
 non-nil value, means return the string normalized as a Hyperbole
 button key (no spaces)." 
   (let* ((opoint (point))
diff --git a/hypb.el b/hypb.el
index ec6bb56db2..b744533766 100644
--- a/hypb.el
+++ b/hypb.el
@@ -3,7 +3,7 @@
 ;; Author:       Bob Weiner
 ;;
 ;; Orig-Date:     6-Oct-91 at 03:42:38
-;; Last-Mod:     20-May-25 at 23:46:09 by Mats Lidell
+;; Last-Mod:     22-May-25 at 23:01:15 by Bob Weiner
 ;;
 ;; SPDX-License-Identifier: GPL-3.0-or-later
 ;;
@@ -680,8 +680,13 @@ This will this install the Emacs helm package when needed."
               (error "(hypb:hkey-help-file): Non-existent file: \"%s\""
                      help-file))))))
 
-(defun hypb:in-string-p (&optional max-lines)
-  "Return t iff point is within a string.
+(defun hypb:in-string-p (&optional max-lines range-flag)
+  "Return non-nil iff point is within a string.
+
+With optional MAX-LINES, an integer, match only within that many
+lines from point.  With optional RANGE-FLAG, return list
+of (string-matched start-pos end-pos), where the positions
+exclude the delimiters.
 
 To prevent searching back to the buffer start and producing slow
 performance, this limits its count of quotes found prior to point
@@ -705,12 +710,15 @@ Quoting conventions recognized are:
       ;; `change-log-mode' due to its syntax-table.
       (let ((opoint (point))
            (start (point-min))
-           (open-match-string ""))
+           (open-match-string "")
+           str-start
+           str-end)
        (cl-destructuring-bind (open-regexp close-regexp)
            (eval hypb:in-string-modes-regexps)
          (save-match-data
            (when (and (re-search-backward open-regexp nil t)
-                      (setq open-match-string (match-string 2))
+                      (setq open-match-string (match-string 2)
+                            str-start (match-end 2))
                       ;; If this is the start of a string, it must be
                       ;; at the start of line, preceded by whitespace
                       ;; or preceded by another string end sequence.
@@ -748,7 +756,11 @@ Quoting conventions recognized are:
                                          (regexp-quote open-match-string))
                                  start (point))))
                     (re-search-forward close-regexp nil t)
-                    t)))))))))
+                    (if range-flag
+                        (progn
+                          (setq str-end (match-beginning 2))
+                          (list (buffer-substring-no-properties str-start 
str-end) str-start str-end))
+                      t))))))))))
 
 (defun hypb:indirect-function (obj)
   "Return the function at the end of OBJ's function chain.
diff --git a/hywiki.el b/hywiki.el
index ab81e64d65..627bc6d376 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:      4-May-25 at 10:46:18 by Bob Weiner
+;; Last-Mod:     23-May-25 at 02:39:29 by Bob Weiner
 ;;
 ;; SPDX-License-Identifier: GPL-3.0-or-later
 ;;
@@ -495,7 +495,7 @@ Group 6 is any optional 0-based column number to jump to 
for any
 file-based referents.")
 
 (defconst hywiki-word-with-optional-suffix-exact-regexp
-  (concat "\\`" hywiki-word-regexp "\\(#[^][\n\r\f]+\\)??"
+  (concat "\\`" hywiki-word-regexp "\\(#[^][#\n\r\f]+\\)??"
          hywiki-word-line-and-column-numbers-regexp "?\\'")
   "Exact match regexp for a HyWiki word with an optional #section.
 The section may contain spaces or tabs but not square brackets;
@@ -551,7 +551,7 @@ Non-nil is the default."
 
 (defun hywiki-debuttonize-non-character-commands ()
   "Store any HyWikiWord before or after point for later comparison.
-Triggered by `pre-command-hook' for non-character commands, including
+Triggered by `pre-command-hook' for non-character -commands, including
 deletion commands and those in `hywiki-non-character-commands'."
   (setq hywiki--word-pre-command nil)
   (set-marker hywiki--buttonize-start nil)
@@ -2982,6 +2982,7 @@ A call to `hywiki-active-in-current-buffer-p' at point 
must return non-nil
 or this will return nil."
   (if (hywiki-active-in-current-buffer-p)
       (save-excursion
+       ;; First look for an Org-type [[hy:WikiWord]] reference.
        ;; Don't use `cl-destructuring-bind' here since the `hargs:delimited' 
call
        ;; can return nil rather than the 3 arg list that would be required
        (let* ((wikiword-start-end
@@ -2997,141 +2998,156 @@ or this will return nil."
               (end      (nth 2 wikiword-start-end)))
          (with-syntax-table hywiki--org-mode-syntax-table
            (if (and (cond (wikiword
-                      ;; Handle an Org link [[HyWikiWord]] [[hy:HyWikiWord]]
-                      ;; or [[HyWikiWord#section][Description Text]].
-                      ;; Get the HyWikiWord link reference, ignoring any
-                      ;; description given in the link
-                      ;; Don't use next line so don't have to load all of Org
-                      ;; mode just to check for HyWikiWords; however, disables
-                      ;; support for Org mode aliases.
-                      ;; (setq wikiword (org-link-expand-abbrev 
(org-link-unescape (string-trim wikiword))))
-                      (setq wikiword (hywiki-strip-org-link wikiword))
-                      (when (and wikiword end)
-                        ;; Update start and end to newly stripped
-                        ;; string positions
-                        (save-excursion
-                          (save-restriction
-                            (narrow-to-region start end)
-                            (goto-char (point-min))
-                            (when (search-forward wikiword nil t)
-                              (setq start (match-beginning 0)
-                                    end   (match-end 0))))))
-                      (hywiki-word-is-p wikiword))
-
-                     ;; Handle delimited HyWikiWord references with
-                     ;; multiple words in their sections,
-                     ;; e.g. (MyWikiWord WikiWord#one two three)
-                     ((let ((case-fold-search nil)
-                            (bol (line-beginning-position))
-                            opoint)
-                        ;; May be a HyWikiWord ending character to skip past
-                        (skip-chars-backward (hywiki-get-buttonize-characters) 
bol)
-                        (setq opoint (point))
-                        (when (hywiki-delimited-p)
-                          (unless (progn
-                                    ;; Skip past HyWikiWord or section with
-                                    ;; possible whitespace
-                                    (skip-syntax-backward "^$()<>._\"\'" bol)
-                                    (unless (= (or (char-before) 0) ?#)
-                                      (goto-char opoint)
-                                      (skip-syntax-backward "^-$()<>._\"\'" 
bol))
-                                    ;; Move to start of wikiword reference
-                                    (skip-chars-backward "-_*#:[:alnum:]" bol)
-                                    (skip-syntax-backward "-" bol)
-                                    ;; Preceding char must now be the
-                                    ;; opening delimiter or else there may
-                                    ;; be multiple non-section words within
-                                    ;; the delimiters, so reprocess and do
-                                    ;; not allow spaces in the #section part
-                                    (memq (char-syntax (or (char-before) 0))
-                                          '(?\( ?\< ?\")))
-                            (goto-char opoint)
-                            (skip-syntax-backward "^-$()<>._\"\'" bol)
-                            ;; Move to start of wikiword reference
-                            (skip-chars-backward "-_*#:[:alnum:]" bol)
-                            (skip-syntax-backward "-" bol))
-                          (when (and
-                                 ;; (or (bolp)
-                                 ;;     (string-match (regexp-quote
-                                 ;;                 (char-to-string 
(char-before)))
-                                 ;;                "\[\(\{\<\""))
-                                 (progn
-                                   (skip-chars-forward " \t")
-                                   (hywiki-maybe-at-wikiword-beginning))
-                                 (looking-at (concat
-                                              hywiki-word-regexp
-                                              "\\(#[^][#()<>{}\"\n\r\f]+\\)?"
-                                              
hywiki-word-line-and-column-numbers-regexp "?"))
-                                 ;; Can't be followed by a # character
-                                 (/= (or (char-after (match-end 0)) 0)
-                                     ?#)
-                                 (progn (goto-char (match-end 0))
-                                        (skip-syntax-forward "-")))
-                            (setq start (match-beginning 0)
-                                  end   (match-end 0)
-                                  ;; No following char
-                                  wikiword (string-trim
-                                            (buffer-substring-no-properties 
start end)))))))
-
-                     ;; Handle non-delimited HyWikiWord references
-                     ;; with multiple dash-separated words in their sections,
-                     ;; e.g. WikiWord#one-two-three.
-                     ((let ((case-fold-search nil)
-                            (bol (line-beginning-position))
-                            opoint)
-                        ;; May be a HyWikiWord ending character to skip past
-                        (skip-chars-backward (hywiki-get-buttonize-characters) 
bol)
-                        (setq opoint (point))
-                        (goto-char opoint)
-                        (skip-syntax-backward "^-$()<>._\"\'" bol)
-                        ;; Move to start of wikiword reference
-                        (skip-chars-backward "-_*#:[:alnum:]" bol)
-                        (skip-syntax-backward "-" bol)
-                        (when (and (or (bolp)
-                                       (string-match (regexp-quote
-                                                      (char-to-string 
(char-before)))
-                                                     "\[\(\{\<\""))
-                                   (progn
-                                     (skip-chars-forward " \t")
-                                     (hywiki-maybe-at-wikiword-beginning))
-                                   (looking-at (concat
-                                                hywiki-word-regexp
-                                                "\\(#[^][#()<>{}\" 
\t\n\r\f]+\\)?"
-                                                
hywiki-word-line-and-column-numbers-regexp "?"))
-                                   ;; Can't be followed by a # character
-                                   (/= (or (char-after (match-end 0)) 0)
-                                       ?#)
-                                   (goto-char (match-end 0)))
-                          (setq start (match-beginning 0)
-                                end   (match-end 0)
-                                ;; No following char
-                                wikiword (string-trim
-                                          (buffer-substring-no-properties 
start end))))))
-
-                     ;; Handle a non-delimited HyWikiWord with optional
-                     ;; #section:Lnum:Cnum; if it is an Org link, it may
-                     ;; optionally have a hy: link-type prefix.  Ignore
-                     ;; wikiwords preceded by any non-whitespace
-                     ;; character, except any of these: "([\"'`'"
-                     (t (let ((case-fold-search nil))
-                          (skip-chars-forward " \t")
-                          (when (hywiki-maybe-at-wikiword-beginning)
-                            (when (looking-at (concat hywiki-org-link-type 
":"))
-                              (goto-char (match-end 0)))
-                            (cond ((looking-at 
hywiki--word-and-buttonize-character-regexp)
-                                   (setq start (match-beginning 1)
-                                         end (match-end 1)
-                                         wikiword (string-trim
-                                                   
(buffer-substring-no-properties start end))))
-                                  ((and (looking-at 
hywiki-word-with-optional-suffix-regexp)
+                           ;; Handle an Org link [[HyWikiWord]] 
[[hy:HyWikiWord]]
+                           ;; or [[HyWikiWord#section][Description Text]].
+                           ;; Get the HyWikiWord link reference, ignoring any
+                           ;; description given in the link
+                           ;; Don't use next line so don't have to load all of 
Org
+                           ;; mode just to check for HyWikiWords; however, 
disables
+                           ;; support for Org mode aliases.
+                           ;; (setq wikiword (org-link-expand-abbrev 
(org-link-unescape (string-trim wikiword))))
+                           (setq wikiword (hywiki-strip-org-link wikiword))
+                           (when (and wikiword end)
+                             ;; Update start and end to newly stripped
+                             ;; string positions
+                             (save-excursion
+                               (save-restriction
+                                 (narrow-to-region start end)
+                                 (goto-char (point-min))
+                                 (when (search-forward wikiword nil t)
+                                   (setq start (match-beginning 0)
+                                         end   (match-end 0))))))
+                           (hywiki-word-is-p wikiword))
+
+                          ;; Handle delimited HyWikiWord references with
+                          ;; multiple words in their sections,
+                          ;; e.g. (MyWikiWord WikiWord#one two three)
+                          ((let ((case-fold-search nil)
+                                 (bol (line-beginning-position))
+                                 opoint)
+                             ;; May be a HyWikiWord ending character to skip 
past
+                             (skip-chars-backward 
(hywiki-get-buttonize-characters) bol)
+                             (setq opoint (point))
+                             (when (setq wikiword-start-end 
(hywiki-delimited-p)) ;; limited to 2 lines
+                               (setq start (nth 1 wikiword-start-end)
+                                     end   (nth 2 wikiword-start-end))
+                               (goto-char start)
+                               (if (and (save-restriction
+                                          (narrow-to-region (point) end)
+                                          (looking-at 
hywiki-word-with-optional-suffix-exact-regexp))
+                                        ;; WikiWord ref is the entirety of the 
string
+                                        (= end (match-end 0))
                                         ;; Can't be followed by a # character
                                         (/= (or (char-after (match-end 0)) 0)
                                             ?#))
+                                   (setq wikiword (match-string-no-properties 
0)
+                                         start (match-beginning 0)
+                                         end   (match-end 0))
+                                 (goto-char opoint)
+                                 (unless (or (progn
+                                               (skip-chars-backward 
"-_*#:[:alnum:]" bol)
+                                               
(hywiki-maybe-at-wikiword-beginning))
+                                             (progn
+                                               ;; Skip past HyWikiWord or 
section with
+                                               ;; possible whitespace
+                                               (skip-syntax-backward 
"^$()<>._\"\'" bol)
+                                               (unless (= (or (char-before) 0) 
?#)
+                                                 (goto-char opoint)
+                                                 (skip-syntax-backward 
"^-$()<>._\"\'" bol))
+                                               ;; Move to start of wikiword 
reference
+                                               (skip-chars-backward 
"-_*#:[:alnum:]" bol)
+                                               (skip-syntax-backward "-" bol)
+                                               ;; Preceding char must now be 
the
+                                               ;; opening delimiter or else 
there may
+                                               ;; be multiple non-section 
words within
+                                               ;; the delimiters, so reprocess 
and do
+                                               ;; not allow spaces in the 
#section part
+                                               (memq (char-syntax (or 
(char-before) 0))
+                                                     '(?\( ?\< ?\"))))
+                                   (goto-char opoint)
+                                   (skip-syntax-backward "^-$()<>._\"\'" bol)
+                                   ;; Move to start of wikiword reference
+                                   (skip-chars-backward "-_*#:[:alnum:]" bol)
+                                   (skip-syntax-backward "-" bol))
+                                 (when (and
+                                        ;; (or (bolp)
+                                        ;;     (string-match (regexp-quote
+                                        ;;                  (char-to-string 
(char-before)))
+                                        ;;                 "\[\(\{\<\""))
+                                        (progn
+                                          (skip-chars-forward " \t")
+                                          (hywiki-maybe-at-wikiword-beginning))
+                                        (looking-at (concat
+                                                     hywiki-word-regexp
+                                                     "\\(#[^][#()<>{}\" 
\t\n\r\f]+\\)?"
+                                                     
hywiki-word-line-and-column-numbers-regexp "?"))
+                                        ;; Can't be followed by a # character
+                                        (/= (or (char-after (match-end 0)) 0)
+                                            ?#)
+                                        (progn (goto-char (match-end 0))
+                                               (skip-syntax-forward "-")))
                                    (setq start (match-beginning 0)
                                          end   (match-end 0)
                                          ;; No following char
                                          wikiword (string-trim
-                                                   
(buffer-substring-no-properties start end)))))))))
+                                                   
(buffer-substring-no-properties start end))))))))
+
+                          ;; Handle non-delimited HyWikiWord references
+                          ;; with multiple dash-separated words in their 
sections,
+                          ;; e.g. WikiWord#one-two-three.
+                          ((let ((case-fold-search nil)
+                                 (bol (line-beginning-position))
+                                 opoint)
+                             ;; May be a HyWikiWord ending character to skip 
past
+                             (skip-chars-backward 
(hywiki-get-buttonize-characters) bol)
+                             (setq opoint (point))
+                             (goto-char opoint)
+                             (skip-syntax-backward "^-$()<>._\"\'" bol)
+                             ;; Move to start of wikiword reference
+                             (skip-chars-backward "-_*#:[:alnum:]" bol)
+                             (skip-syntax-backward "-" bol)
+                             (when (and (or (bolp)
+                                            (string-match (regexp-quote
+                                                           (char-to-string 
(char-before)))
+                                                          "\[\(\{\<\""))
+                                        (progn
+                                          (skip-chars-forward " \t")
+                                          (hywiki-maybe-at-wikiword-beginning))
+                                        (looking-at (concat
+                                                     hywiki-word-regexp
+                                                     "\\(#[^][#()<>{}\" 
\t\n\r\f]+\\)?"
+                                                     
hywiki-word-line-and-column-numbers-regexp "?"))
+                                        ;; Can't be followed by a # character
+                                        (/= (or (char-after (match-end 0)) 0)
+                                            ?#)
+                                        (goto-char (match-end 0)))
+                               (setq start (match-beginning 0)
+                                     end   (match-end 0)
+                                     ;; No following char
+                                     wikiword (string-trim 
(match-string-no-properties 0))))))
+
+                          ;; Handle a non-delimited HyWikiWord with optional
+                          ;; #section:Lnum:Cnum; if it is an Org link, it may
+                          ;; optionally have a hy: link-type prefix.  Ignore
+                          ;; wikiwords preceded by any non-whitespace
+                          ;; character, except any of these: "([\"'`'"
+                          (t (let ((case-fold-search nil))
+                               (skip-chars-forward " \t")
+                               (when (hywiki-maybe-at-wikiword-beginning)
+                                 (when (looking-at (concat 
hywiki-org-link-type ":"))
+                                   (goto-char (match-end 0)))
+                                 (cond ((looking-at 
hywiki--word-and-buttonize-character-regexp)
+                                        (setq start (match-beginning 1)
+                                              end (match-end 1)
+                                              wikiword (string-trim 
(match-string-no-properties 1))))
+                                       ((and (looking-at 
hywiki-word-with-optional-suffix-regexp)
+                                             ;; Can't be followed by a # 
character
+                                             (/= (or (char-after (match-end 
0)) 0)
+                                                 ?#))
+                                        (setq start (match-beginning 0)
+                                              end   (match-end 0)
+                                              ;; No following char
+                                              wikiword (string-trim 
(match-string-no-properties 0)))))))))
                     ;; If `wikiword' has a #section, ensure there are
                     ;; no invalid chars
                     (if (and (stringp wikiword) (string-match "#" wikiword))
@@ -3165,6 +3181,7 @@ or this will return nil."
 
 (defun hywiki-delimited-p (&optional pos)
   "Return non-nil if optional POS or point is surrounded by matching 
delimiters.
+Any non-nil value returned is a list of (string-matched start-pos end-pos).
 The delimited range must be two lines or less.
 
 Use `hywiki-word-at', which calls this, to determine whether there is
@@ -3172,12 +3189,13 @@ a HyWikiWord at point."
   (save-excursion
     (when (natnump pos)
       (goto-char pos))
-    (or (hypb:in-string-p)
-       (let ((range (hargs:delimited-p "[\[<\(\{]" "[\]\}\)\>]" t t t)))
-         (when range
-           ;; Ensure closing delimiter is a match for the opening one
-           (= (matching-paren (char-before (nth 1 range)))
-              (char-after (nth 2 range))))))))
+    (or (hypb:in-string-p 2 t)
+       (let ((range (hargs:delimited "[\[<\(\{]" "[\]\}\)\>]" t t t)))
+         (and range
+              ;; Ensure closing delimiter is a match for the opening one
+              (= (matching-paren (char-before (nth 1 range)))
+                 (char-after (nth 2 range)))
+              range)))))
 
 (defun hywiki-word-face-at-p (&optional pos)
   "Non-nil if but at point or optional POS has `hywiki-word-face' property."

Reply via email to