branch: externals/hyperbole
commit 77ccac92fcf77e0e48d1e49b76abb29aeaa10325
Author: bw <[email protected]>
Commit: bw <[email protected]>
hywiki.el and hywiki-tests.el - Highlighting fixes
Set `fter-change-major-mode-hook' to ensure HyWiki buffer-local hooks
are not erased when changing modes.
---
ChangeLog | 43 +++++++++++++++
hywiki.el | 147 +++++++++++++++++++++++++++++----------------------
test/hywiki-tests.el | 88 +++++++++++++++++++-----------
3 files changed, 183 insertions(+), 95 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index e8d9c1b284..f2bc1385db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,46 @@
+2026-02-01 Bob Weiner <[email protected]>
+
+* hywiki.el (hywiki-word-store-around-point): Dehighlight only if Emacs
+ is idle and within a hywikiword in a valid context.
+ (hywiki-highlighted-word-at): Rewrite so matches 'hywiki-word-at'
+ behavior based on point but requires that the HyWikiWord be highlighted
+ or returns nil/nil range.
+
+* test/hywiki-tests.el (hywiki-tests--run-test-case): Remove initial
+ 'erase-buffer' since callers set up the buffer prior to this call.
+ Add 'ert-info' so know the exact buffer state when the test is run.
+
+* hywiki.el (hywiki-maybe-highlight-references): Remove conditonals at the
+ start aside from whether hywiki-mode is active in this buffer since
+ a page may have been added and references to it need to be rehighlighted
+ so can't depend on previous highlighting having been done.
+
+* test/hywiki-tests.el (hywiki-tests--wikiword-identified-in-emacs-lisp-mode,
+ hywiki-tests--face-property-for-wikiword-with-wikipage,
+ hywiki-tests--verify-hywiki-word):
+ Add 'ert-info' to show which test has failed and fix categorization of
+ tests so they all pass.
+
+* hywiki.el (hywiki-word-highlight-in-current-buffer): Add for use as an
+ 'after-change-major-mode-hook'.
+ (hywiki-word-highlight-buffers,
+ hywiki-word-dehighlight-buffers): Most major modes inherit
+ from 'fundamental-mode' which clears the hywiki buffer-local hooks,
+ so need to restore these to the current buffer via
+ 'after-change-major-mode-hook'. Setup to do that in above functions.
+ (hywiki-word-dehighlight-in-buffers): Create this function
+ and call from 'hywiki-word-dehighlight-buffers.
+ (hywiki-get-buffers): Optimize to check 'hywiki-mode-status'
+ only once.
+ (hywiki-get-buffers-in-windows): Fix doc to show that this
+ does not filter to only hywiki buffers. Use 'hywiki-get-buffers'
+ for that.
+ (hywiki-word-highlight-in-frame): Fix to highlight in active
+ hywiki buffers only.
+ (hywiki-maybe-highlight-references): Fix that regexp search
+ leaves point after closing string delimiter and then tests if in
+ string. Move point to within string (grouping 1) before testing.
+
2026-01-31 Bob Weiner <[email protected]>
* hproperty.el (hproperty:char-property-face-p): Rename to
diff --git a/hywiki.el b/hywiki.el
index d5600bdc23..9f49422731 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: 31-Jan-26 at 22:45:09 by Bob Weiner
+;; Last-Mod: 1-Feb-26 at 15:33:59 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -567,9 +567,9 @@ including deletion commands and those in
`hywiki-non-character-commands'."
(set-marker hywiki--buttonize-end nil)
(setq hywiki--buttonize-range nil))
- (when (hywiki-non-hook-context-p)
+ (when (and (current-idle-time) (hywiki-non-hook-context-p) (hywiki-word-at))
;; Dehighlight any previously highlighted WikiWord at point if
- ;; outside of a valid context.
+ ;; it is outside of a valid context.
(save-restriction
(narrow-to-region (line-beginning-position) (line-end-position))
(hywiki-maybe-dehighlight-reference)))
@@ -1373,7 +1373,8 @@ Use `hywiki-get-referent' to determine whether a HyWiki
page exists."
(message "HyWikiWord page exists: \"%s\"" page-file)))
(unless (or (hyperb:stack-frame
'(hywiki-maybe-highlight-wikiwords-in-frame))
(and (not force-flag) page-file-readable page-in-hasht))
- (hywiki-cache-save))
+ (hywiki-cache-save)
+ (hywiki-maybe-highlight-wikiwords-in-frame t))
(run-hooks 'hywiki-add-page-hook)
(when page-file (cons 'page page-file))))
(when (called-interactively-p 'interactive)
@@ -1797,12 +1798,14 @@ This does not test whether a referent exists for the
HyWikiWord; call
A call to `hywiki-active-in-current-buffer-p' at point must return non-nil
or this will return nil."
(when (and (hywiki-active-in-current-buffer-p)
- (setq hywiki--range
- (hproperty:overlay-range (point) 'face hywiki-word-face)))
- (let ((wikiword (buffer-substring-no-properties (car hywiki--range) (cdr
hywiki--range))))
- (if (string-match hywiki-word-with-optional-suffix-exact-regexp wikiword)
+ (setq hywiki--range (hywiki-word-at :range))
+ (car hywiki--range))
+ (cl-destructuring-bind (wikiword start end)
+ hywiki--range
+ (if (and (hproperty:but-get start 'face hywiki-word-face)
+ (string-match hywiki-word-with-optional-suffix-exact-regexp
wikiword))
(if range-flag
- (list wikiword (car hywiki--range) (cdr hywiki--range))
+ (list wikiword start end)
wikiword)
(when range-flag
'(nil nil nil))))))
@@ -2439,12 +2442,7 @@ whenever `hywiki-mode' is enabled/disabled."
;; Highlight HyWikiWords throughout buffers where `hywiki-mode' is enabled
;; or HyWiki pages below `hywiki-directory' whenever displayed in a window.
(if (hywiki-active-in-current-buffer-p)
- (unless (and (or (and (null region-start) (null region-end))
- (and (markerp region-start) (markerp region-end)
- (not (and (marker-position region-start)
- (marker-position region-end)))))
- (eq hywiki-buffer-highlighted-state 'h)
- (not (hywiki-directory-modified-p)))
+ (progn
(unless skip-lookups-update-flag
;; Rebuild lookup tables if any HyWiki page name has changed
(hywiki-get-referent-hasht))
@@ -2459,7 +2457,7 @@ whenever `hywiki-mode' is enabled/disabled."
(cond ((and (markerp region-start) (markerp region-end))
(when (and (marker-position region-start)
(marker-position region-end))
- (narrow-to-region region-start region-end)))
+ (narrow-to-region region-start region-end)))
((and region-start region-end)
(narrow-to-region region-start region-end)))
;; Enable dehighlighting in HyWiki pages only when
@@ -2468,21 +2466,23 @@ whenever `hywiki-mode' is enabled/disabled."
(unless (and region-start region-end)
(let ((hywiki-mode))
(hywiki-maybe-dehighlight-references)))
- (dolist (hywiki-words-regexp hywiki--any-wikiword-regexp-list)
- (goto-char (point-min))
- (let ((highlight-in-comments-and-strings-only
- (and (derived-mode-p 'prog-mode)
- (not (apply #'derived-mode-p
hywiki-highlight-all-in-prog-modes)))))
+ (let ((highlight-in-comments-and-strings-only
+ (and (derived-mode-p 'prog-mode)
+ (not (apply #'derived-mode-p
hywiki-highlight-all-in-prog-modes))))
+ hywiki--start
+ hywiki--end)
+ (dolist (hywiki-words-regexp hywiki--any-wikiword-regexp-list)
+ (goto-char (point-min))
(while (re-search-forward hywiki-words-regexp nil t)
- (when (save-match-data
- (if highlight-in-comments-and-strings-only
- ;; Non-nil if match is inside a comment or a
string
- (or (nth 4 (syntax-ppss)) (hypb:in-string-p))
- t))
- (setq hywiki--start (match-beginning 1)
- hywiki--end (match-end 1))
- (save-excursion
- (goto-char hywiki--start)
+ (setq hywiki--start (match-beginning 1)
+ hywiki--end (match-end 1))
+ (save-excursion
+ (goto-char hywiki--start)
+ (when (save-match-data
+ (if highlight-in-comments-and-strings-only
+ ;; Non-nil if match is inside a comment or
a string
+ (or (nth 4 (syntax-ppss))
(hypb:in-string-p))
+ t))
;; Otherwise, highlight any HyWikiWord found,
including
;; any #section:Lnum:Cnum.
(when (hywiki-maybe-at-wikiword-beginning)
@@ -2556,13 +2556,15 @@ DIRECTION-NUMBER is 1 for forward scanning and -1 for
backward scanning."
#'hywiki-maybe-highlight-references direction-number))
(defun hywiki-maybe-highlight-wikiwords-in-frame (frame &optional
skip-lookups-update-flag)
- "Highlight all non-Org link HyWiki page names displayed in FRAME.
+ "Highlight all non-Org link HyWiki references displayed in FRAME.
+Do not highlight references to the current page unless they have
+sections attached.
+
If FRAME is t, then highlight in all windows across all frames, even
invisible ones. With optional SKIP-LOOKUPS-UPDATE-FLAG non-nil, HyWiki
lookup tables should have already been updated and this is skipped.
-Use `hywiki-word-face' to highlight. Do not highlight references to
-the current page unless they have sections attached."
+Use `hywiki-word-face' to highlight."
(walk-windows
(lambda (window)
(with-selected-window window
@@ -2588,30 +2590,31 @@ Note that HyWiki references can occur in non-HyWiki
page buffers."
(file-name-sans-extension (file-name-nondirectory
(or (hypb:buffer-file-name) (buffer-name)))))
-(defun hywiki-get-buffers-in-windows (&rest frames)
- "Return the set of buffers in all windows where `hywiki-mode' is active.
-This applies to all windows in all live frames or can be filtered to optional
-rest of arguments FRAMES."
- (apply #'set:create
- (apply #'nconc (mapcar (lambda (frame) (mapcar #'window-buffer
- (window-list frame)))
- (or frames (frame-list))))))
-
(defun hywiki-get-buffers (hywiki-mode-status)
- "Return the list of HyWiki buffers displayed in any non-minibuffer window.
-A HyWiki buffer is one where HyWikiWord references are highlighted
-when 'hywiki-mode' is enabled.
+ "Return the set of HYWIKI-MODE-STATUS buffers in any non-minibuffer window.
+This goes across all live frames.
-See the function documentation for `hywiki-mode' for valid input
+See the function documentation for `hywiki-mode' for valid HYWIKI-MODE-STATUS
values (the states of `hywiki-mode')."
- (when hywiki-mode
- (delq nil (mapcar (lambda (buf)
- (with-current-buffer buf
- (and (if (eq hywiki-mode-status :pages)
- (hywiki-in-page-p)
- (hywiki-potential-buffer-p))
- buf)))
- (hywiki-get-buffers-in-windows)))))
+ (when hywiki-mode-status
+ (let ((hywiki-buf-predicate
+ (if (eq hywiki-mode-status :pages)
+ #'hywiki-in-page-p
+ #'hywiki-potential-buffer-p)))
+ (delq nil (mapcar (lambda (buf)
+ (with-current-buffer buf
+ (when (funcall hywiki-buf-predicate)
+ buf)))
+ (hywiki-get-buffers-in-windows))))))
+
+(defun hywiki-get-buffers-in-windows (&rest frames)
+ "Return the set of HyWiki buffers in all windows across all live frames.
+Or include only those in optional rest of arguments FRAMES."
+ (apply #'set:create
+ (apply #'nconc (mapcar (lambda (frame)
+ (mapcar #'window-buffer
+ (window-list frame)))
+ (or frames (frame-list))))))
(defun hywiki-get-page-file (file-stem-name)
"Return possibly non-existent path in `hywiki-directory' from FILE-STEM-NAME.
@@ -3630,8 +3633,17 @@ occurs with one of these hooks, the problematic hook is
removed."
hywiki-from-mode hywiki-to-mode))))
(defun hywiki-word-highlight-in-frame (frame)
- "Auto-highlight HyWikiWords in `hywiki-mode' buffers displayed FRAME."
- (hywiki-word-highlight-in-buffers (hywiki-get-buffers-in-windows frame)))
+ "Auto-highlight HyWikiWords in `hywiki-mode' buffers displayed in FRAME."
+ (when hywiki-mode
+ (let ((hywiki-buf-predicate
+ (if (eq hywiki-mode :pages)
+ #'hywiki-in-page-p
+ #'hywiki-potential-buffer-p)))
+ (hywiki-word-highlight-in-buffers
+ (seq-filter hywiki-buf-predicate (hywiki-get-buffers-in-windows
frame))))))
+
+(defun hywiki-word-highlight-in-current-buffer ()
+ (hywiki-word-highlight-in-buffers (list (current-buffer))))
(defun hywiki-word-highlight-in-buffers (buffers)
"Auto-highlight HyWikiWords in BUFFERS."
@@ -3649,8 +3661,9 @@ occurs with one of these hooks, the problematic hook is
removed."
(hywiki-maybe-directory-updated))
(defun hywiki-word-highlight-buffers (buffers)
- "Setup to auto-highlight HyWikiWords in BUFFERS."
+ "Setup HyWikiWord auto-highlighting and highlight in BUFFERS."
(interactive)
+ (add-hook 'after-change-major-mode-hook
'hywiki-word-highlight-in-current-buffer)
(add-hook 'window-buffer-change-functions 'hywiki-word-highlight-in-frame)
(add-to-list 'yank-handled-properties
'(hywiki-word-face . hywiki-highlight-on-yank))
@@ -3658,13 +3671,9 @@ occurs with one of these hooks, the problematic hook is
removed."
(when (called-interactively-p 'interactive)
(message "HyWikiWord auto-highlighting enabled")))
-(defun hywiki-word-dehighlight-buffers (buffers)
- "Disable auto-highlighting of HyWikiWords in BUFFERS."
+(defun hywiki-word-dehighlight-in-buffers (buffers)
+ "Dehighlight HyWikiWords in BUFFERS."
(interactive)
- (remove-hook 'window-buffer-change-functions 'hywiki-word-highlight-in-frame)
- (setq yank-handled-properties
- (delete '(hywiki-word-face . hywiki-highlight-on-yank)
- yank-handled-properties))
(dolist (buf buffers)
(with-current-buffer buf
(remove-hook 'pre-command-hook 'hywiki-word-store-around-point
:local)
@@ -3674,7 +3683,17 @@ occurs with one of these hooks, the problematic hook is
removed."
;; long-running font-locking
(sit-for 0)
(hywiki-maybe-dehighlight-references)))
- (hywiki-maybe-directory-updated)
+ (hywiki-maybe-directory-updated))
+
+(defun hywiki-word-dehighlight-buffers (buffers)
+ "Disable HyWikiWord auto-highlighting and dehighlight in BUFFERS."
+ (interactive)
+ (remove-hook 'after-change-major-mode-hook
'hywiki-word-highlight-in-current-buffer)
+ (remove-hook 'window-buffer-change-functions 'hywiki-word-highlight-in-frame)
+ (setq yank-handled-properties
+ (delete '(hywiki-word-face . hywiki-highlight-on-yank)
+ yank-handled-properties))
+ (hywiki-word-dehighlight-in-buffers buffers)
(when (called-interactively-p 'interactive)
(message "HyWikiWord auto-highlighting disabled")))
diff --git a/test/hywiki-tests.el b/test/hywiki-tests.el
index bc103488f5..1c40262ebc 100644
--- a/test/hywiki-tests.el
+++ b/test/hywiki-tests.el
@@ -3,7 +3,7 @@
;; Author: Mats Lidell
;;
;; Orig-Date: 18-May-24 at 23:59:48
-;; Last-Mod: 31-Jan-26 at 17:35:30 by Bob Weiner
+;; Last-Mod: 1-Feb-26 at 15:52:57 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -774,17 +774,20 @@ Both mod-time and checksum must be changed for a test to
return true."
"Verify WikiWord for a wiki page gets face property hywiki-word-face."
(skip-unless (not noninteractive))
(hywiki-tests--preserve-hywiki-mode
- (let* ((hsys-org-enable-smart-keys t))
- (hywiki-tests--insert "WikiWor")
+ (let* ((hsys-org-enable-smart-keys t)
+ str)
+ (hywiki-tests--insert (setq str "WikiWor"))
(hywiki-tests--command-execute #'self-insert-command 1 ?d)
(goto-char 4)
- (should (hywiki-word-face-at-p))
+ (ert-info ((format "str = \"%s\"" str))
+ (should (hywiki-word-face-at-p)))
(erase-buffer)
- (hywiki-tests--insert "WikiWord")
+ (hywiki-tests--insert (setq str "WikiWord"))
(hywiki-tests--command-execute #'newline 1 'interactive)
(goto-char 4)
- (should (hywiki-word-face-at-p)))))
+ (ert-info ((format "str = \"%s\"" str))
+ (should (hywiki-word-face-at-p))))))
(ert-deftest hywiki-tests--no-face-property-for-no-wikipage ()
"Verify WikiWord for no wiki page does not get face property
hywiki-word-face."
@@ -1121,6 +1124,7 @@ WikiWord#Csection-subsection
("\"WikiWord Text WikiWord\"" . ,(format "\"%s%s\""
href href))
;; ^
Missing " Text "
("WikiWord WikiWord WikiWord" . ,(format "%s %s %s"
href href href))
+ ;; !! TODO FIXME
;; (cons "\"WikiWord WikiWord WikiWord\"" (format
"\"%s %s %s\"" href href href))
;; ^ Crashes due to (wrong-type-argument
integer-or-marker-p nil) caused by buffer-substring-no-properties(nil nil)
))
@@ -1649,13 +1653,27 @@ comparison with expected overlays stable."
"Verify that `hywiki-word-at' returns t if a wikiword is EXPECTED.
If EXPECTED is a string, also verify that the wikiword matches the
string."
- (if (not expected)
- (should-not (hywiki-tests--word-at))
- (let ((hywiki-word-found (hywiki-tests--word-at)))
- (if (stringp expected)
- (should (string= expected hywiki-word-found))
- (should hywiki-word-found))
- (should (hywiki-word-is-p hywiki-word-found)))))
+ (ert-info ((format (concat "buffer name = \"%s\"\n"
+ "major-mode = %s\n"
+ "(hywiki-active-in-current-buffer-p) = %s\n"
+ "pre-command-hook = %s\n"
+ "buffer contents = \"%s\"\n"
+ "hywiki-tests--with-face-test = %s\n"
+ "expected = \"%s\"")
+ (buffer-name)
+ major-mode
+ (hywiki-active-in-current-buffer-p)
+ pre-command-hook
+ (buffer-substring-no-properties (point-min) (point-max))
+ hywiki-tests--with-face-test
+ expected))
+ (if (not expected)
+ (should-not (hywiki-tests--word-at))
+ (let ((hywiki-reference (hywiki-tests--word-at)))
+ (if (stringp expected)
+ (should (string= expected hywiki-reference))
+ (should hywiki-reference))
+ (should (hywiki-word-is-p hywiki-reference))))))
(defun hywiki-tests--run-test-case (test-case)
"Run the TEST-CASE from point.
@@ -1668,7 +1686,6 @@ or non-nil for a wikiword. The state is checked after
all chars
of the string are inserted. If equal to a string it is checked
for match with the wikiword. Movement of point is relative to
point when the function is called."
- (erase-buffer)
(let ((origin (point)))
;; For traceability when looking through the list of should
@@ -1693,8 +1710,9 @@ point when the function is called."
(hywiki-tests--verify-hywiki-word vfy)))
((and (symbolp step) (string-prefix-p "p" (symbol-name step)))
(let* ((pos (string-to-number (substring (symbol-name step) 1)))
- (newpos (+ origin (1- pos))))
- (when (or (> 0 newpos) (< (point-max) newpos))
+ (newpos (max (min (+ origin (1- pos)) (point-max))
+ (point-min))))
+ (when (or (> (point-min) newpos) (< (point-max) newpos))
(ert-fail (format "New point: '%s' is outside of buffer"
newpos)))
(goto-char newpos))
(hywiki-tests--verify-hywiki-word vfy))
@@ -1733,6 +1751,7 @@ resulting state at point is a WikiWord or not."
(hywiki-tests--preserve-hywiki-mode
(let ((hywiki-tests--with-face-test nil))
(dolist (testcase hywiki-tests--wikiword-step-check)
+ (erase-buffer)
(hywiki-tests--run-test-case testcase)))))
(ert-deftest hywiki-tests--wikiword-step-check-verification-with-faces ()
@@ -1806,37 +1825,43 @@ face is verified during the change."
(ert-deftest hywiki-tests--wikiword-identified-in-emacs-lisp-mode ()
"Verify WikiWord is identified when surrounded by delimiters in
`emacs-lisp-mode'."
(hywiki-tests--preserve-hywiki-mode
- (let* ((hsys-org-enable-smart-keys t))
+ (emacs-lisp-mode)
+ (let* ((hsys-org-enable-smart-keys t)
+ str)
;; Matches a WikiWord
- (dolist (v '("WikiWord" "[WikiWord]" "[[WikiWord]]" "{WikiWord}"
"(WikiWord)"
- "<WikiWord>" "<<WikiWord>>" "{[[WikiWord]]}"
"([[WikiWord]])"
- "[WikiWord AnotherWord]"
+ (dolist (v '("WikiWord" "[WikiWord]" "{WikiWord}" "(WikiWord)"
+ "<WikiWord>" "[WikiWord AnotherWord]"
))
- (emacs-lisp-mode)
- (hywiki-tests--insert (format ";; %s" v))
+ (erase-buffer)
+ (hywiki-tests--insert (setq str (format ";; %s" v)))
(hywiki-tests--command-execute #'newline 1 'interactive)
(goto-char 9)
- (should (string= "WikiWord" (hywiki-tests--word-at)))
+ (ert-info ((format "str = \"%s\"" str))
+ (should (string= "WikiWord" (hywiki-tests--word-at))))
(erase-buffer)
- (hywiki-tests--insert (format "(setq var \"%s\")" v))
+ (hywiki-tests--insert (setq str (format "(setq var \"%s\")" v)))
(hywiki-tests--command-execute #'newline 1 'interactive)
(goto-char 16)
- (should (string= "WikiWord" (hywiki-tests--word-at))))
+ (ert-info ((format "str = \"%s\"" str))
+ (should (string= "WikiWord" (hywiki-tests--word-at)))))
- ;; Does not match as a WikiWord
- (dolist (v '("WikiWord#"))
+ ;; Does not highlight as a WikiWord
+ (dolist (v '("WikiWord#" "[[WikiWord]]" "<<WikiWord>>"
+ "{[[WikiWord]]}" "([[WikiWord]])"))
(erase-buffer)
- (hywiki-tests--insert (format ";; %s" v))
+ (hywiki-tests--insert (setq str (format ";; %s" v)))
(hywiki-tests--command-execute #'newline 1 'interactive)
(goto-char 9)
- (should-not (hywiki-tests--word-at))
+ (ert-info ((format "str = \"%s\"" str))
+ (should-not (hywiki-tests--word-at)))
(erase-buffer)
- (hywiki-tests--insert (format "(setq var \"%s\")" v))
+ (hywiki-tests--insert (setq str (format "(setq var \"%s\")" v)))
(hywiki-tests--command-execute #'newline 1 'interactive)
(goto-char 16)
- (should-not (hywiki-tests--word-at))))))
+ (ert-info ((format "str = \"%s\"" str))
+ (should-not (hywiki-tests--word-at)))))))
(ert-deftest hywiki-tests--wikiword-identified-in-strings-in-emacs-lisp-mode ()
"Verify WikiWord is identified when in strings in `emacs-lisp-mode'."
@@ -1900,6 +1925,7 @@ face is verified during the change."
(dolist (testcase
'((("\"Hi#a b c\"") (p3 . "Hi#a b c") (p11) (-1) (p3 . "Hi#a")
(p10) ("\"") (p3 . "Hi#a b c"))
(("(Hi#s n)" . "Hi#s n") (-1) (p3 . "Hi#s") (p8) (")" . "Hi#s
n"))))
+ (erase-buffer)
(hywiki-tests--run-test-case testcase)))))
(ert-deftest hywiki-tests--wikiword-yanked-with-extra-words ()