branch: externals/hyperbole commit 1773ce63928ea44706b5878fedac8551d356867e Merge: 62e9eb4b24 29674ef91a Author: Robert Weiner <r...@gnu.org> Commit: GitHub <nore...@github.com>
Merge pull request #679 from rswgnu/rsw - hywiki.el - Simplify per-char hook functions; improve hywiki-word-at - Latest rsw updates; hywiki hooks and hywiki-word-at rewritten - hywiki.el - Fix dehighlighting when first WikiWord char is deleted - hywiki-word-at - Fix delimited/non-delimited HyWikiWord handling - hywiki.el - Improve hywiki-word-at and hywiki-word-is-p #sections - hywiki.el - Fix much of hywiki-word-at and remove use of cl-lib - Fix hkey-alist vertico handling and more - hywiki.el, hsys-org.el - Fix a bunch of predicates and tests --- ChangeLog | 228 +++++++++++++ HY-ABOUT | 2 +- Makefile | 6 +- README.md | 2 +- README.toc.md | 19 +- hargs.el | 14 +- hib-kbd.el | 7 +- hibtypes.el | 17 +- hmouse-drv.el | 15 +- hmouse-tag.el | 4 +- hpath.el | 6 +- hproperty.el | 9 +- hsys-org.el | 88 ++++- hsys-xref.el | 6 +- hui-mouse.el | 29 +- hui-select.el | 31 +- hypb.el | 16 +- hyperbole.el | 4 +- hywiki.el | 906 ++++++++++++++++++++++++++++++++----------------- man/hyperbole.texi | 30 +- test/MANIFEST | 1 + test/hsys-org-tests.el | 7 +- test/hywiki-tests.el | 160 ++++++--- test/kcell-tests.el | 4 +- 24 files changed, 1160 insertions(+), 451 deletions(-) diff --git a/ChangeLog b/ChangeLog index 215bbbc140..ba518fa3f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,81 @@ +2025-04-13 Bob Weiner <r...@gnu.org> + +* hsys-org.el (hsys-org-thing-at-p): Fix to work outside of Org mode by + suppressing warnings and ignoring Org regex errors. This fixes two + hsys-org tests where the 'www-url' ibtype fired instead of + 'org-link-outside-org-mode'. + (hsys-org-link-at-p): Rewrite to fix sending back (start . end) + positions when on a link, to be used as an implicit button label. + +* hywiki.el (hywiki-section-to-headline-reference): Add to convert #section + dashes to spaces for matching to Org headlines. + (org-link-set-parameters): Set HyWiki's link type to call the above + function on HyWiki links so they properly match Org headlines. + +2025-04-12 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki-cache-save): Change to save any modified cache rather than + triggering an error (which can occur during regression testing). This fixes + the `hywiki-tests--save-referent-info-node-use-menu' test. + (hywiki--buttonized-region-p): Add so buttonized region tests are + always handled correctly. Use in the functions below. + (hywiki-convert-words-to-org-links, + hywiki-maybe-dehighlight-balanced-pairs, + hywiki-maybe-highlight-balanced-pairs, + hywiki-maybe-dehighlight-page-name, + hywiki-maybe-highlight-page-name, + hywiki--maybe-dehighlight-at-point, + hywiki--maybe-rehighlight-at-point): Fix to avoid using region + markers in a buffer other than the current one. This also fixes a bug when + doing HyWiki exports to html and referencing the wrong buffer. + +* test/hywiki-tests.el (hywiki-tests--wikiword-step-check): Fix to not expect + closing paren delimiter as part of the WikiWord. + +* hywiki.el (hywiki-at-range-delimiter): Handle if scan-sexps returns nil then + return '(nil nil). + +* hypb.el (hypb:in-string-p): Update to handle change-log-mode which disables + syntax parsing and so is handled with a string search. + +* hmouse-drv.el (hkey-actions): Add for use in Hyperbole testing and call from + "test/hui-mouse-tests.el". + +* hui-mouse.el (hkey-alist): Ignore 'ivy-mode' if 'vertico-mode' is actively + prompting for an argument and allow for this outside of the minibuffer + since vertico may prompt within another buffer. Also, ignore the value + of hargs:reading-type here, otherwise, it will not catch all uses of + 'vertico-mode'. + +2025-04-07 Mats Lidell <ma...@gnu.org> + +* test/MANIFEST: Add hui-mouse-tests.el + +* test/hui-mouse-tests.el (hui-mouse-tests--hkey-alist): Verify a + predicate setting leads to the proper action. + (hui-mouse-tests--hkey-get-action): Helper that gets primary action + and assist action from hkey-alist for the predicates in effect. + +2025-04-06 Bob Weiner <r...@gnu.org> + +* test/hywiki-tests.el (hywiki-tests--wikiword-identified-in-emacs-lisp-mode): + Add this test from Mats with one correction. + +* hywiki.el (require cl-lib): Remove use. + (hywiki--org-export-new-title-reference): Remove use of 'cl-incf'. + (hywiki-maybe-at-wikiword-beginning, + hywiki-maybe-dehighlight-page-name, + hywiki-maybe-highlight-page-name + hywiki-word-at): Replace 'cl-find' with 'string-match' for speed. + (hywiki-buttonize-character-commands): Fix 'post-self-insert-hook' + so it highlights wikiwords after a closing delimiter is inserted. + (hywiki--maybe-rehighlight-at-point): Remove nullifying of + 'hywiki--word-pre-command', 'hywiki--buttonize-start', 'hywiki--buttonize-end' + and move this to the start of the hywiki 'pre-command-hook' + (hywiki-debuttonize-non-character-commands) so they are not nullified + by 'post-command-hook' before they can be used by 'post-self-insert-hook'. + (hywiki-delimited-p): Add support for strings as well. + 2025-04-06 Mats Lidell <ma...@gnu.org> * test/hy-test-helpers.el (hy-test-run-failing-flag): Set to non-nil to @@ -12,6 +90,30 @@ (hywiki-tests--verify-hywiki-word): Add verification that hywiki-word-is-p agrees with hywiki-word-at. +2025-04-05 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki-word-at, hywiki-word-is-p): If has a #section, ensure + there are no invalid chars. + (hywiki-word-at): Fix missing test for double quote as an opening + delimiter. + +* test/hywiki-tests.el (hywiki-tests--sections-with-dash-space): Update code + to ensure #section cannot contain quote marks. + +2025-03-30 Bob Weiner <r...@gnu.org> + +* test/hywiki-tests.el (hywiki-tests--wikiword-step-check): Fix by removing + matching to trailing delimiters, since that is now fixed. + +* hywiki.el (hywiki-word-at): Fix so if not in a delimited HyWikiWord reference + or if such a reference contains more than one non-section word, then + disallow spaces in section names. + (hywiki-delimited-p): Add and use in above function. + (hywiki--get-delimited-range-backward, hywiki--get-delimited-range-forward): + Clarify required point position and inclusion of delimiters in the range. + (hywiki-word-highlighted-at-p): Add to return any highlighted wikiword ref + at point. + 2025-03-30 Mats Lidell <ma...@gnu.org> * test/hywiki-tests.el (hywiki-tests--add-find): Remove hywiki-directory @@ -58,6 +160,127 @@ command while also executing the pre and post command hooks. Use it instead of the with-hywiki-buttonize macros. +2025-03-17 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki-maybe-dehighlight-between-page-names): Fix to dehighlight + at point if on a highlighted prior WikiWork. + (hywiki--buttonize-characters): Add missing double quote char as + a match char to fix "WikiWord" not highlighting in a file when read in. + +2025-03-16 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki--rehighlight-at-point): Fix to dehighlight when the first + char of a wikiword is deleted. + (hywiki--maybe-rehighlight-at-point): Add maybe to the name. + (hywiki-maybe-dehighlight-page-name): Extract from above function + and use this standalone. + +* HY-ABOUT: + MELPA-RECIPE.el: + README.md: + man/hyperbole.texi: + hyperbole-pkg.el ("hyperbole"): Update requirement to Emacs version 28 from + 27.2 since Markdown mode now requires this. + +2025-03-14 Bob Weiner <r...@gnu.org> + +* hypb.el (hypb:in-string-p): Made return the start position of the string + +* hywiki.el (hywiki--page-name): Rename to 'hywiki--word-only'. + (hywiki--word-pre-command): Add for use in command hooks. + (hywiki-debuttonize-non-character-commands, + hywiki-buttonize-character-commands, + hywiki-buttonize-non-character-commands): Rewrite all of these + to capture the first HyWikiWord and then only dehighlight in post hooks + if the HyWikiWords has changed. + +2025-03-13 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki-word-at): Clarify :range return value in doc string. + +2025-03-08 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki-word-at): Fix so hy:WikiWord is recognized without any + delimiters. + hsys-org.el (hsys-org-link-at-p): Fix to handle Org links without square or + angle brackets, e.g. file:WikiWord.org. + (hsys-org-thing-at-p): Add to test if point is on a specific org + construct type, e.g. a link. + (hsys-org-link-at-p): Remove 'with-suppressed-warnings' since + inner call to 'hsys-org-thing-at-p' is now run only when in org mode. + +* test/hywiki-tests.el (hywiki-tests--convert-words-to-org-link): Fix expected +value to be [[WikiWord]] not [[file:WikiWord.org][WikiWord]]. + +2025-03-07 Bob Weiner <r...@gnu.org> + +* test/hywiki-tests.el (hywiki-tests--wikiword-identified-with-delimiters): + Fix to show which wikiword item fails. + +* hywiki.el (hywiki-maybe-highlight-page-name): Fix to eliminate narrowing + of region that hides any balanced delimiters around a HyWikiWord. Also + generalize HyWikiWord range finding using updated 'hywiki-word-at'. + (hywiki-word-at): Fix to allow whitespace between delimiters and + between #section words when delimited. Also, if not on a wikiword and + 'range-flag' is non-nil, return '(nil nil nil). + +2025-03-06 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki-maybe-highlight-between-page-names): Allow for whitespace + after opening delimiter. + +2025-03-02 Bob Weiner <r...@gnu.org> + +* test/hywiki-tests.el (hywiki-tests--convert-words-to-org-link): Fix expected + link match strings. + +* hsys-xref.el (hsys-xref-identifier-at-point): Fix to return nil if xref returns + a pathname as an identifier. This fixes a problem with the Action Key + triggering the wrong action; it should recognize it as a pathname. + +* test/hywiki-tests.el (hywiki-tests--add-org-roam-node): Mock variable, + `cl-struct-org-roam-node-tags' needed when org-roam package is not installed. + +2025-02-27 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki--maybe-de/highlight-sexp): Fix to narrow to region of + current HyWikiWord range before calling 'func'. + (hywiki--maybe-de/highlight-sexp): Add optional 'sexp-start' and + 'sexp-end' args when known; range should include delimiters. + (hywiki-buttonize-character-commands, + hywiki-buttonize-non-character-commands): Pass 'hywiki--buttonize-start' + and 'hywiki--buttonize-end' positions to 'hywiki--maybe-de/highlight-sexp' + when known rather than recomputing. + +* hproperty.el (hproperty:but-move): Add and use in "hywiki.el". + +* hywiki.el (hywiki-get-delimited-range): Rename to 'hywiki-at-range-delimiter'. + (hywiki-referent-exists-p): Fix return value doc when given :range + arg. Fix to send word string rather than tuple to 'hywiki-get-referent'. + +2025-02-25 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki-word-with-optional-spaces-suffix-exact-regexp): Remove + use in 'hywiki-word-is-p' and replace with + 'hywiki-word-with-optional-suffix-exact-regexp' which actually allows spaces. + (hywiki-word-at): Add support for finding delimited WikiWords with + #sections that contain multiple space separated words and returning the + wikiword, start and end. + (hywiki-word-at): Fix last 'looking-at' expression to not match to + end of buffer. + (hywiki-buttonize-character-commands, + hywiki-buttonize-non-character-commands, + hywiki-debuttonize-non-character-commands): Rewrite and simplify. + +2025-02-24 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki--maybe-de/highlight-sexp): Ensure return the result of + calling 'func'. + (hywiki-buttonize-non-character-commands): Remove insert commands + handled by 'hywiki-buttonize-character-commands' attached to + 'post-self-insert-hook'. + (hywiki-non-hook-context-p): Add. + 2025-03-07 Mats Lidell <ma...@gnu.org> * hywiki.el (hywiki-convert-words-to-org-links) @@ -128,8 +351,13 @@ * hywiki.el (hywiki-referent-menu): Re-add accidentally deleted "Keys" key series referent type in the menu. + (hywiki-set-directory): Call as part of loading hywiki.el to ensure + HyWiki Org project publish settings are initialized. (hywiki--org-export-new-title-reference): Replace '--any' call from dash.el package with 'cl-some'. + (hywiki-buttonize-character-commands): In prog modes, limit to comments + and strings. If within a delimited pair, highlight #sections with multiple + words. * test/hywiki-tests.el (hywiki-tests--convert-words-to-org-link): Fix to not expect 'hy:' prefix in HyWiki Org links. diff --git a/HY-ABOUT b/HY-ABOUT index da0954216c..9d43f05daa 100644 --- a/HY-ABOUT +++ b/HY-ABOUT @@ -11,7 +11,7 @@ GNU Hyperbole (pronounced Ga-new Hi-per-bo-lee), or just Hyperbole, is an efficient and programmable hypertextual information management system implemented as a GNU Emacs package. It works well on GNU Emacs -27.2 or above. (See also: "HY-WHY.kotl" for Hyperbole uses). +28 or above. (See also: "HY-WHY.kotl" for Hyperbole uses). Hyperbole includes easy-to-use, powerful hypertextual buttons without the need to learn a markup language; a hierarchical, record-based diff --git a/Makefile b/Makefile index 5611dac951..2a39b140be 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,9 @@ # Author: Bob Weiner # # Orig-Date: 15-Jun-94 at 03:42:38 -# Last-Mod: 6-Apr-25 at 19:45:20 by Mats Lidell +# Last-Mod: 12-Apr-25 at 13:15:02 by Bob Weiner # -# Copyright (C) 1994-2023 Free Software Foundation, Inc. +# Copyright (C) 1994-2025 Free Software Foundation, Inc. # See the file HY-COPY for license information. # # This file is part of GNU Hyperbole. @@ -81,7 +81,7 @@ # # To interactively run a docker version of Emacs with Hyperbole: # make docker-run - default to running master -# make docker-run version=27.2 - run Emacs V27.2 +# make docker-run version=28.2 - run Emacs V28.2 # # To build and test a dockerized version of Emacs with Hyperbole: # make docker - defaults: version=master targets='clean bin test' diff --git a/README.md b/README.md index bf039a4d66..c91f9c8fe3 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ keyboard and mouse-based control of information display within multiple windows. It also provides point-and-click access to World-Wide Web URLs, Info manuals, ftp archives, etc. -Hyperbole works well on GNU Emacs 27.2 or above. It is designed and written +Hyperbole works well on GNU Emacs 28 or above. It is designed and written by Bob Weiner. It is maintained by him and Mats Lidell. Its main distribution site is: <https://www.gnu.org/software/hyperbole/>. If any term in here is new or unfamiliar to you, you can look it up in the diff --git a/README.toc.md b/README.toc.md index 9fb84e7230..c91f9c8fe3 100644 --- a/README.toc.md +++ b/README.toc.md @@ -2,12 +2,23 @@ [We work on Hyperbole as a gift to the Emacs community and request you send us a thank you or a testimonial describing your usage if you like - Hyperbole (mailto:r...@gnu.org)]. + Hyperbole to [r...@gnu.org](mailto:r...@gnu.org)]. [TOC]  +## Reference Manual + +Hyperbole has many features you can explore interactively or by reading sections of +the reference manual: + + - [Browse Hyperbole Web Manual](man/hyperbole.html) + + - [In Emacs, Browse Hyperbole Info Manual](man/hyperbole.info) + + - [Display or Print PDF Manual](man/hyperbole.pdf) + ## Videos If you prefer video introductions, visit the videos linked to below; @@ -135,7 +146,7 @@ keyboard and mouse-based control of information display within multiple windows. It also provides point-and-click access to World-Wide Web URLs, Info manuals, ftp archives, etc. -Hyperbole works well on GNU Emacs 27.2 or above. It is designed and written +Hyperbole works well on GNU Emacs 28 or above. It is designed and written by Bob Weiner. It is maintained by him and Mats Lidell. Its main distribution site is: <https://www.gnu.org/software/hyperbole/>. If any term in here is new or unfamiliar to you, you can look it up in the @@ -228,9 +239,9 @@ not a simple introduction. It is included in the "man/" subdirectory of the Hyperbole package directory in four forms: [hyperbole.info](man/hyperbole.info) - online Info browser version -[hyperbole.html](man/hyperbole.html) - web HTML version +[hyperbole.html](man/hyperbole.html) - web version [hyperbole.pdf](man/hyperbole.pdf) - printable version -[hyperbole.texi](man/hyperbole.texi) - source form +[hyperbole.texi](man/hyperbole.texi) - source version The Hyperbole package installation places the Info version of this manual where needed and adds an entry for Hyperbole into the Info directory under diff --git a/hargs.el b/hargs.el index 4e9cb7067a..d22f3da1fc 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: 6-Oct-24 at 22:47:25 by Bob Weiner +;; Last-Mod: 27-Feb-25 at 21:21:40 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -221,7 +221,7 @@ button key (no spaces)." ;; disk drive prefix, in which case the backslash is ;; considered part of a pathname. (and (if (and (> end (point-min)) - (= (char-before end) ?\\) + (= (or (char-before end) 0) ?\\) (not (string-match (concat "\\(\\`[\\][\\]\\)\\|" hpath:mswindows-mount-prefix) (hargs:buffer-substring start end)))) @@ -525,8 +525,14 @@ following sexpression is returned. Otherwise, the innermost sexpression that point is within is returned or nil if none." (let ((not-quoted '(condition-case () - (not (and (eq (char-syntax (char-after (- (point) 2))) ?\\) - (not (eq (char-syntax (char-after (- (point) 3))) ?\\)))) + (not (and (= (if (char-after (- (point) 2)) + (char-syntax (char-after (- (point) 2))) + 0) + ?\\) + (not (= (if (char-after (- (point) 3)) + (char-syntax (char-after (- (point) 3))) + 0) + ?\\)))) (error t)))) (save-excursion (ignore-errors diff --git a/hib-kbd.el b/hib-kbd.el index 53cb6a2d3d..0d446a0f6a 100644 --- a/hib-kbd.el +++ b/hib-kbd.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 22-Nov-91 at 01:37:57 -;; Last-Mod: 23-Nov-24 at 21:15:04 by Bob Weiner +;; Last-Mod: 25-Feb-25 at 02:14:39 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -144,7 +144,10 @@ Any key sequence within the series must be a string of one of the following: ;; In Texinfo, allow for @bkbd{} or @kbd{}, so an ;; alpha char preceding (and (derived-mode-p 'texinfo-mode) - (= (char-syntax (char-before start)) ?w))) + (= (if (char-before start) + (char-syntax (char-before start)) + 0) + ?w))) (when (and (stringp key-series) (not (string-empty-p key-series))) ;; Replace any ${} internal or env vars; leave ;; $VAR untouched for the shell to evaluate. diff --git a/hibtypes.el b/hibtypes.el index 91ba677533..25b424ddee 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: 29-Jan-25 at 10:07:49 by Mats Lidell +;; Last-Mod: 2-Mar-25 at 12:05:51 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -1591,10 +1591,19 @@ action type, function symbol to call or test to execute, i.e. ;; at the end of the buffer ;; or is followed by a space, punctuation or grouping character. (when (and lbl-key (or (null (char-before start-pos)) - (memq (char-syntax (char-before start-pos)) '(?\ ?\> ?\( ?\)))) - (not (memq (char-syntax (char-after (1+ start-pos))) '(?\ ?\>))) + (memq (if (char-before start-pos) + (char-syntax (char-before start-pos)) + 0) + '(?\ ?\> ?\( ?\)))) + (not (memq (if (char-after (1+ start-pos)) + (char-syntax (char-after (1+ start-pos))) + 0) + '(?\ ?\>))) (or (null (char-after end-pos)) - (memq (char-syntax (char-after end-pos)) '(?\ ?\> ?. ?\( ?\))) + (memq (if (char-after end-pos) + (char-syntax (char-after end-pos)) + 0) + '(?\ ?\> ?. ?\( ?\))) ;; Some of these characters may have symbol-constituent syntax ;; rather than punctuation, so check them individually. (memq (char-after end-pos) '(?. ?, ?\; ?: ?! ?\' ?\")))) diff --git a/hmouse-drv.el b/hmouse-drv.el index bacb22e1ce..c2ea344f78 100644 --- a/hmouse-drv.el +++ b/hmouse-drv.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 04-Feb-90 -;; Last-Mod: 22-Feb-25 at 11:52:57 by Bob Weiner +;; Last-Mod: 12-Apr-25 at 15:47:32 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -992,6 +992,19 @@ frame instead." (mouse-drag-frame-move start-event) (mouse-drag-frame start-event 'move)))))) +(defun hkey-actions () + "Return the cons of the Action and Assist Key actions at point. +Useful in testing Smart Key contexts." + (let ((hkey-forms hkey-alist) + pred-value hkey-actions hkey-form pred) + (while (and (null pred-value) (setq hkey-form (car hkey-forms))) + (if (setq hkey-actions (cdr hkey-form) + pred (car hkey-form) + pred-value (hypb:eval-debug pred)) + nil + (setq hkey-forms (cdr hkey-forms)))) + hkey-actions)) + (defun hkey-debug (pred pred-value hkey-action) "Display a message with the context and values from Smart Key activation." (message (concat "(HyDebug) %sContext: %s; %s: %s; Buf: %s; Mode: %s; MinibufDepth: %s\n" diff --git a/hmouse-tag.el b/hmouse-tag.el index 7cb755198a..d861eb9b5a 100644 --- a/hmouse-tag.el +++ b/hmouse-tag.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 24-Aug-91 -;; Last-Mod: 29-Jan-25 at 20:26:54 by Mats Lidell +;; Last-Mod: 12-Apr-25 at 18:46:48 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -90,7 +90,7 @@ should insert the implicit link type definition name.") ;; default, `xref-find-definitions', so is not dependent on TAGS ;; tables or the `default-directory' in the ERT results buffer. When ;; a test is loaded, its symbol property, `ert--test', holds the -;; absolute path to its file, and find-function uses that when its +;; absolute path to its file, and `find-function' uses that when its ;; entry in `find-function-regexp-alist' is a regexp. (defconst find-ert-test-regexp "^\\s-*(ert-deftest\\s-+%s\\s-" "The regexp used to search for an ert test definition. diff --git a/hpath.el b/hpath.el index f35f004b85..89e1bd60a9 100644 --- a/hpath.el +++ b/hpath.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 1-Nov-91 at 00:44:23 -;; Last-Mod: 7-Mar-25 at 00:22:47 by Mats Lidell +;; Last-Mod: 13-Apr-25 at 01:06:44 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -1613,7 +1613,7 @@ but locational suffixes within the file are utilized." (current-buffer))))))))))) (defun hpath:spaces-to-dashes-markup-anchor (anchor) - "Replace dashes with spaces in ANCHOR if not a prog mode and no existing dashes." + "Replace spaces with dashes in ANCHOR if not a prog mode and no existing dashes." (if (or (derived-mode-p 'prog-mode) (string-match-p "-.* \\| .*-" anchor)) anchor @@ -1623,7 +1623,7 @@ but locational suffixes within the file are utilized." (subst-char-in-string ?\ ?- anchor))) (defun hpath:dashes-to-spaces-markup-anchor (anchor) - "Replace dashes with spaces in ANCHOR if not a prog mode and no existing dashes." + "Replace dashes with spaces in ANCHOR if not a prog mode and no existing spaces." (if (or (derived-mode-p 'prog-mode) (string-match-p "-.* \\| .*-" anchor)) anchor diff --git a/hproperty.el b/hproperty.el index ca27419f7a..c7fcc9e364 100644 --- a/hproperty.el +++ b/hproperty.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 21-Aug-92 -;; Last-Mod: 18-Jan-25 at 20:53:20 by Bob Weiner +;; Last-Mod: 27-Feb-25 at 22:50:13 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -274,6 +274,13 @@ If optional PROPERTY and VALUE are given, return only the first button with that PROPERTY and VALUE." (car (hproperty:but-get-first-in-region pos (1+ pos) property value))) +(defun hproperty:but-move (hproperty-but start end &optional buffer) + "Set the endpoints of HPROPERTY-BUT to START and END in optional BUFFER. +If BUFFER is nil and HPROPERTY-BUT has no buffer, put it in the current buffer; +otherwise, if BUFFER is omitted, leave HPROPERTY-BUT in the same +buffer it presently inhabits." + (move-overlay hproperty-but start end buffer)) + (defun hproperty:but-start (hproperty-but) "Return the end position of an HPROPERTY-BUT. See `hproperty:but-get'." diff --git a/hsys-org.el b/hsys-org.el index a66d35b3c9..d4a74c6477 100644 --- a/hsys-org.el +++ b/hsys-org.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 2-Jul-16 at 14:54:14 -;; Last-Mod: 5-Jan-25 at 12:06:01 by Bob Weiner +;; Last-Mod: 13-Apr-25 at 04:30:30 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -497,19 +497,50 @@ Match to all todos if `keyword' is nil or the empty string." (looking-at org-babel-src-block-regexp)))) (defun hsys-org-link-at-p () - "Return non-nil iff point is on a square-bracketed Org mode link. + "Return (start . end) iff point is on an Org mode link, else nil. +Start and end are the buffer positions of the label that point is on +delimited by square brackets. + Ignore [[hy:HyWiki]] buttons and return nil (handle these as implicit buttons). Assume caller has already checked that the current buffer is in `org-mode' or is looking for an Org link in -another buffer type." +a non-Org buffer type." (unless (or (smart-eolp) (smart-eobp)) - (with-suppressed-warnings nil - (let ((in-org-link (org-in-regexp org-link-bracket-re nil t))) - (when in-org-link - (save-match-data - ;; If this Org link matches a potential HyWiki word, ignore it. - (unless (and (fboundp 'hywiki-word-at) (hywiki-word-at)) - in-org-link))))))) + (if (derived-mode-p 'org-mode) + (let* ((org-plist (hsys-org-thing-at-p)) + (type (plist-get org-plist :type)) + (path (plist-get org-plist :path)) + label-start-end) + (when (eq type 'link) + (save-match-data + ;; If this Org link matches a potential HyWiki word, ignore it. + (when (not (and (fboundp 'hywiki-word-at) (hywiki-word-at))) + (if (setq label-start-end (ibut:label-p t "[" "]" t)) + (cons (nth 1 label-start-end) (nth 2 label-start-end)) + t))))) + ;; non-Org mode (can't call org-element (which + ;; hsys-org-thing-at-p calls) outside of Org mode + (when (bound-and-true-p org-link-bracket-re) + (let ((pos (point))) + (when (save-excursion + (or + ;; Check if point is inside a link + (and (re-search-backward org-link-bracket-re + (line-beginning-position) t) + (> pos (point)) + (< pos (match-end 0))) + ;; If not found before, check if we're in the middle of a link + (and (forward-line 0) + (re-search-forward org-link-bracket-re + (line-end-position 2) t) + (> (point) pos) + (< pos (match-end 0))))) + (save-match-data + ;; If this Org link matches a potential HyWiki word, ignore it. + (when (not (and (fboundp 'hywiki-word-at) (hywiki-word-at))) + (if (setq label-start-end (ibut:label-p t "[" "]" t)) + (cons (nth 1 label-start-end) (nth 2 label-start-end)) + t))))))))) ;; Assume caller has already checked that the current buffer is in org-mode. (defun hsys-org-heading-at-p (&optional _) @@ -525,6 +556,43 @@ Assume caller has already checked that the current buffer is in `org-mode'." (hsys-org-face-at-p 'org-target)) +;; Derived from `org-open-at-point' in "org.el". +(defun hsys-org-thing-at-p () + "Return a plist of properties for Org thing at point or nil if none. +This must work on Org links outside of Org mode. + +The plist form is: (:type <type> :value <value> :context <context>). +The thing can be a link, citation, timestamp, footnote, src-block or +tags. + +On top of syntactically correct links, this function also works +on links and timestamps in comments, node properties, and +keywords if point is on something looking like a timestamp or a +link." + (ignore-errors + (org-load-modules-maybe) + ;; Org regex matching can fail in non-thing contexts, so we ignore + ;; these errors and return nil then. Org-element will also warn to + ;; not use it outside of Org mode although it works, so we suppress + ;; those warnings as well. + (let* ((warning-minimum-level :error) + (warning-suppress-types '(org-element rx)) + (warning-suppress-log-types '(org-element rx)) + (context + ;; Only consider supported types, even if they are not the + ;; closest one. + (org-element-lineage + (org-element-context) + '(citation citation-reference clock comment comment-block + footnote-definition footnote-reference headline + inline-src-block inlinetask keyword link node-property + planning src-block timestamp) + t)) + (type (org-element-type context)) + (value (org-element-property :value context))) + (when type + (list :type type :value value :context context))))) + (defun hsys-org-todo-at-p () "Return non-nil iff point is on an Org mode todo keyword. Assume caller has already checked that the current buffer is in `org-mode'." diff --git a/hsys-xref.el b/hsys-xref.el index d633699af1..798f598119 100644 --- a/hsys-xref.el +++ b/hsys-xref.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 24-Aug-91 -;; Last-Mod: 14-Apr-24 at 19:03:32 by Bob Weiner +;; Last-Mod: 2-Mar-25 at 18:11:45 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -49,7 +49,9 @@ (car (hsys-xref-definitions identifier))) (defun hsys-xref-identifier-at-point () - (xref-backend-identifier-at-point (xref-find-backend))) + ;; Return nil if xref returns a pathname as an identifier + (unless (hpath:at-p nil t) + (xref-backend-identifier-at-point (xref-find-backend)))) (defun hsys-xref-item-buffer (item) "Return the buffer in which xref ITEM is defined." diff --git a/hui-mouse.el b/hui-mouse.el index 8f5b775234..30af357e44 100644 --- a/hui-mouse.el +++ b/hui-mouse.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 04-Feb-89 -;; Last-Mod: 22-Feb-25 at 16:18:02 by Bob Weiner +;; Last-Mod: 12-Apr-25 at 14:29:25 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -253,20 +253,19 @@ Its default value is `smart-scroll-down'. To disable it, set it to . ((smart-push-button nil (mouse-event-p last-command-event)) . (smart-push-button-help nil (mouse-event-p last-command-event)))) ;; - ;; If in the minibuffer and reading an argument with vertico - ;; run the vertico command on {M-RET} which accepts the first - ;; line of minibuffer input, rather than any candidate. - ((and hargs:reading-type - (> (minibuffer-depth) 0) - (eq (selected-window) (minibuffer-window)) - (not (bound-and-true-p ivy-mode)) - (and (bound-and-true-p vertico-mode) - ;; Is vertico prompting for an argument? - (vertico--command-p nil (current-buffer)))) - . ((vertico-exit-input) . (vertico-exit-input))) - ;; - ;; If in the minibuffer and reading an argument (aside from - ;; with vertico or ivy), accept argument or give completion help. + ;; If in a window reading an argument with vertico, run the + ;; vertico command on {M-RET} which by default accepts the + ;; existing input at the prompt rather than the candidate pointed + ;; to. + ((and (bound-and-true-p vertico-mode) + ;; Is vertico prompting for an argument? + (vertico--command-p nil (current-buffer))) + . ((funcall (lookup-key vertico-map (kbd "M-RET"))) + . (funcall (lookup-key vertico-map (kbd "M-RET"))))) + ;; + ;; If in the minibuffer and reading a non-menu Hyperbole argument + ;; (aside from with vertico or ivy), accept the argument or give + ;; completion help. ((and hargs:reading-type (> (minibuffer-depth) 0) (eq (selected-window) (minibuffer-window)) diff --git a/hui-select.el b/hui-select.el index 4429e69c15..44e734f071 100644 --- a/hui-select.el +++ b/hui-select.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 19-Oct-96 at 02:25:27 -;; Last-Mod: 7-Mar-25 at 00:39:21 by Mats Lidell +;; Last-Mod: 27-Feb-25 at 21:24:19 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -327,7 +327,6 @@ Used to include a final line when marking indented code.") ;;; Public declarations ;;; ************************************************************************ -(defvar hbut:syntax-table) ; "hbut.el" (defvar help-mode-syntax-table) ; "help-mode.el" (defvar hkey-init) ; "hyperbole.el" (defvar hkey-value) ; "hui-mouse.el" @@ -431,13 +430,13 @@ returned is the function to call to select that syntactic unit." (unless (smart-eobp) (or (numberp pos) (setq pos (point))) (setq hui-select-previous 'char) - (let* ((syntax (char-syntax (or (char-after pos) (char-before pos)))) + (let* ((syntax (char-syntax (or (char-after pos) (char-before pos) 0))) (pair (assq syntax hui-select-syntax-alist))) (and pair (or hui-select-whitespace (not (eq (cdr pair) 'thing-whitespace))) ;; Ignore matches that are preceded by '\' as a quote, e.g. ?\' (or (not (char-after pos)) (= pos (point-min)) - (and (char-before pos) (/= ?\\ (char-before pos)))) + (and (char-before pos) (/= ?\\ (or (char-before pos) 0)))) (cdr pair))))) ;;;###autoload @@ -850,7 +849,7 @@ If an error occurs during syntax scanning, return nil." (setq hui-select-previous 'char) (if (save-excursion (goto-char pos) (eolp)) (hui-select-line pos) - (let* ((syntax (char-syntax (or (char-after pos) (char-before pos)))) + (let* ((syntax (char-syntax (or (char-after pos) (char-before pos) 0))) (pair (assq syntax hui-select-syntax-alist))) (cond ((and pair (or hui-select-whitespace @@ -904,7 +903,7 @@ Use `hui-select-mark-delimited-sexp' to select it." (syn-after (if (char-after) (char-syntax (char-after)) 0))) (or (and (/= syn-before ?\\) (or (= syn-after ?\() (= syn-after ?\)))) (and (= syn-before ?\)) (char-before (1- (point))) - (/= ?\\ (char-syntax (char-before (1- (point)))))))))) + (/= ?\\ (if (char-before (1- (point))) (char-syntax (char-before (1- (point)))) 0))))))) (defun hui-select-mark-delimited-sexp () "When point is before or after an sexp deactivate the mark and mark the sexp. @@ -916,7 +915,7 @@ end sexp delimiters, ignore it, and return nil." (when (region-active-p) (deactivate-mark)) (mark-sexp) t))) (ignore-errors - (let ((syn-after (char-syntax (char-after))) + (let ((syn-after (if (char-after) (char-syntax (char-after)) 0)) syn-before) (cond ((eq syn-after ?\() (funcall mark-sexp-func)) @@ -925,7 +924,7 @@ end sexp delimiters, ignore it, and return nil." (backward-sexp) (funcall mark-sexp-func)) ((and (not (eolp)) - (setq syn-before (char-syntax (char-before))) + (setq syn-before (if (char-before) (char-syntax (char-before)) 0)) (eq syn-before ?\))) (backward-sexp) (funcall mark-sexp-func))))))) @@ -1051,14 +1050,14 @@ string." (with-syntax-table hbut:syntax-table (or (and (equal start-delim "\"") (equal end-delim "\"") (ignore-errors - (cond ((and (= (char-after) ?\") - (/= (char-before) ?\\)) + (cond ((and (= (or (char-after) 0) ?\") + (/= (or (char-before) 0) ?\\)) (if (hypb:in-string-p) (hui-select-set-region (1+ (point)) (scan-sexps (1+ (point)) -1)) (hui-select-set-region (point) (scan-sexps (point) 1)))) - ((and (= (char-before) ?\") - (/= (char-before (1- (point))) ?\\)) + ((and (= (or (char-before) 0) ?\") + (/= (or (char-before (1- (point))) 0) ?\\)) (if (hypb:in-string-p) (hui-select-set-region (1- (point)) (scan-sexps (1- (point)) 1)) (hui-select-set-region (point) (scan-sexps (point) -1))))))) @@ -1339,8 +1338,8 @@ included in the list, hui-select-brace-modes." The region includes sexpressions before and after POS" (or (hui-select-markup-pair pos) (hui-select-delimited-thing-call #'hui-select-thing) - (and (or (and (= (char-after) ?\") (/= (char-before) ?\\)) - (and (= (char-before) ?\") (/= (char-before (1- (point))) ?\\))) + (and (or (and (= (or (char-after) 0) ?\") (/= (or (char-before) 0) ?\\)) + (and (= (or (char-before) 0) ?\") (/= (or (char-before (1- (point))) 0) ?\\))) (hui-select-string pos)) (hui-select-comment pos) (hui-select-preprocessor-def pos) @@ -1348,9 +1347,9 @@ The region includes sexpressions before and after POS" (save-excursion (setq hui-select-previous 'punctuation) (goto-char (min (1+ pos) (point-max))) - (cond ((and (char-after pos) (= ?\ (char-syntax (char-after pos)))) + (cond ((and (char-after pos) (= ?\ (if (char-after pos) (char-syntax (char-after pos)) 0))) (hui-select-set-region pos (1+ pos))) - ((and (char-before pos) (= ?\ (char-syntax (char-before pos)))) + ((and (char-before pos) (= ?\ (if (char-before pos) (char-syntax (char-before pos)) 0))) (hui-select-set-region (1- pos) pos)) (t (goto-char pos) (ignore-errors (hui-select-set-region diff --git a/hypb.el b/hypb.el index 90fd98bbc6..1ca259e9d6 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: 29-Jan-25 at 20:27:07 by Mats Lidell +;; Last-Mod: 12-Apr-25 at 16:58:59 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -664,9 +664,17 @@ This will this install the Emacs helm package when needed." help-file)))))) (defun hypb:in-string-p () - "Return non-nil iff point is in a double quoted string." - (syntax-ppss-flush-cache (line-beginning-position)) - (nth 3 (syntax-ppss))) + "Return non-nil iff point is in the first line of a double quoted string." + (if (derived-mode-p 'change-log-mode) + ;; limited to single line strings; count must be odd to be + ;; inside a string + (when (cl-oddp (count-matches "\"" (line-beginning-position) (point))) + (save-excursion (search-backward "\"" (line-beginning-position) t))) + (syntax-ppss-flush-cache (line-beginning-position)) + (let ((sexp-state-list (syntax-ppss))) + (when (eq ?\" (nth 3 sexp-state-list)) ; in a double quoted string + ;; return start of str position (opening quote) + (nth 8 sexp-state-list))))) (defun hypb:indirect-function (obj) "Return the function at the end of OBJ's function chain. diff --git a/hyperbole.el b/hyperbole.el index 6db1ed9c7e..30c4871bc1 100644 --- a/hyperbole.el +++ b/hyperbole.el @@ -9,12 +9,12 @@ ;; Maintainer: Robert Weiner <r...@gnu.org> ;; Maintainers: Robert Weiner <r...@gnu.org>, Mats Lidell <ma...@gnu.org> ;; Created: 06-Oct-92 at 11:52:51 -;; Last-Mod: 2-Feb-25 at 07:27:43 by Bob Weiner +;; Last-Mod: 16-Mar-25 at 10:11:31 by Bob Weiner ;; Released: 10-Mar-24 ;; Version: 9.0.2pre ;; Keywords: comm, convenience, files, frames, hypermedia, languages, mail, matching, mouse, multimedia, outlines, tools, wp ;; Package: hyperbole -;; Package-Requires: ((emacs "27.2")) +;; Package-Requires: ((emacs "28")) ;; URL: http://www.gnu.org/software/hyperbole ;; See the "HY-COPY" file for license information. diff --git a/hywiki.el b/hywiki.el index 76bb894123..3c9ed8dc4d 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: 7-Mar-25 at 01:00:19 by Mats Lidell +;; Last-Mod: 13-Apr-25 at 02:03:01 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -136,7 +136,6 @@ ;;; Other required Elisp libraries ;;; ************************************************************************ -(require 'cl-lib) ;; For `cl-find' and `cl-incf' (require 'hactypes) ;; For `link-to-file-interactively' (require 'hargs) (require 'hasht) @@ -148,6 +147,7 @@ (require 'hui-mini) ;; For `hui:menu-act' (require 'hypb) ;; Requires `seq' (require 'outline) ;; For `outline-mode-syntax-table' +(require 'seq) ;; For 'seq-contains-p' and 'seq-difference' (require 'subr-x) ;; For `string-remove-prefix' (require 'thingatpt) @@ -234,8 +234,10 @@ Each element is of the form: (wikiword . (referent-type . referent-value)).") (defvar hywiki--buttonize-start (make-marker)) ;; This must always stay a marker (defvar hywiki--current-page nil) (defvar hywiki--end nil) +(defvar hywiki--flag nil) (defvar hywiki--highlighting-done-flag t) -(defvar hywiki--page-name nil) +(defvar hywiki--word-pre-command nil) +(defvar hywiki--word-only nil) (defvar hywiki--range nil) (defvar hywiki--save-case-fold-search nil) (defvar hywiki--save-org-link-type-required nil) @@ -311,7 +313,7 @@ See `hywiki-org-publishing-directory' for exported pages in html format." (defun hywiki-directory-changed (option set-to-value operation _where) "Watch function for variable `hywiki-directory'. -Function is called with 4 arguments: (SYMBOL SET-TO-VALUE OPERATION WHERE)." +Function is called with 4 arguments: (OPTION SET-TO-VALUE OPERATION WHERE)." (if (memq operation '(let unlet)) ;; not setting global value (hywiki-let-directory option set-to-value) (hywiki-set-directory option set-to-value))) @@ -355,7 +357,7 @@ Presently, there are no key bindings; this is for future use.") "HyWiki string prefix type for Org links. Excludes trailing colon.") (defvar hywiki-org-link-type-required t - "When non-nil, HyWiki Org links must start with `hywiki-org-link-type'. + "When t, [[hy:HyWiki Org links]] must start with `hywiki-org-link-type':. Otherwise, this prefix is not needed and HyWiki word Org links override standard Org link lookups. See \"(org)Internal Links\".") @@ -437,6 +439,7 @@ where PATH is the un-resolvable reference." :publishing-directory hywiki-org-publishing-directory :publishing-function hywiki-org-publishing-function :section-numbers t + :shell "shell-command" :sitemap-filename "index.org" ;; sitemap (TOC) is stored in "sitemap.html" :sitemap-title hywiki-org-publishing-sitemap-title @@ -464,7 +467,7 @@ Do not use a start or end line/string anchor in this regexp.") (defconst hywiki-word-section-regexp "\\(#[^][# \t\n\r\f]+\\)" - "Regexp that matches a HyWiki word #section extension. + "Regexp that matches a non-delimited HyWiki word #section extension. After the first # character, this may contain any non-square-bracket, non-# and non-whitespace characters.") @@ -487,19 +490,6 @@ file-based referents (relative to any section given). Group 6 is any optional 0-based column number to jump to for any file-based referents.") -(defconst hywiki-word-with-optional-spaces-suffix-exact-regexp - (concat "\\`" hywiki-word-with-optional-suffix-regexp "\\'") - "Exact regexp for a HyWiki word with optional #section, :Lline-num, :Ccol-num. -Section may not contain whitespace or square brackets. Use '-' to -substitute for spaces in the section/headline name. - -Group 1 is the HyWiki word. -Group 2 is any optional #section with the # included. -Group 4 is any optional 1-based line number to jump to for any -file-based referents (relative to any section given). -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]+\\)??" hywiki-word-line-and-column-numbers-regexp "?\\'") @@ -552,84 +542,122 @@ Non-nil is the default." :group 'hyperbole-hywiki) ;;; ************************************************************************ -;;; hywiki minor mode +;;; hywiki minor mode and text edit command hooks ;;; ************************************************************************ -(defun hywiki-buttonize-character-commands () - "Turn any HyWikiWords between point into highlighted Hyperbole buttons. -Triggered by `post-self-insert-hook' for self-inserting characters. -Highlight after inserting any non-word character." - (unless (or (minibuffer-window-active-p (selected-window)) - (and (boundp 'edebug-active) edebug-active - (active-minibuffer-window))) - (hywiki-maybe-highlight-between-page-names))) +(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 +deletion commands and those in `hywiki-non-character-commands'." + (setq hywiki--word-pre-command nil) + (set-marker hywiki--buttonize-start nil) + (set-marker hywiki--buttonize-end nil) + + (unless (hywiki-non-hook-context-p) + ;; Record the WikiWord from any WikiWord ref that point is on + (setq hywiki--word-pre-command (hywiki-get-singular-wikiword (hywiki-word-at))) + (when (or (memq this-command hywiki-non-character-commands) + (and (symbolp this-command) + (string-match-p "^\\(org-\\)?\\(delete-\\|kill-\\)\\|\\(-delete\\|-kill\\)\\(-\\|$\\)" (symbol-name this-command)))) + ;; Test if at delimiters surrounding a WikiWord and if so, + ;; record those for use by post hooks. + (cl-destructuring-bind (start end) + ;; Get delimited region only if before or after delimiters, + ;; else return (nil nil). + (hywiki-at-range-delimiter) ;; includes delimiters + ;; Use these to store any range of a delimited HyWikiWord#section + (set-marker hywiki--buttonize-start start) + (set-marker hywiki--buttonize-end end) + start))) + (setq hywiki--flag nil)) (defun hywiki-buttonize-non-character-commands () "Highlight any HyWikiWord before or after point as a Hyperbole button. Triggered by `post-command-hook' for non-character-commands, including deletion commands and those in `hywiki-non-character-commands'." - (unless (or (minibuffer-window-active-p (selected-window)) - (and (boundp 'edebug-active) edebug-active - (active-minibuffer-window)) - (and (derived-mode-p 'prog-mode) - (not (apply #'derived-mode-p hywiki-highlight-all-in-prog-modes)) - ;; Not inside a comment or a string - (not (or (nth 4 (syntax-ppss)) (hypb:in-string-p))))) + (unless (or hywiki--flag (hywiki-non-hook-context-p)) (when (or (memq this-command hywiki-non-character-commands) (and (symbolp this-command) - (string-match-p "^\\(org-\\)?\\(delete-\\|kill-\\)\\|\\(-delete\\|-kill\\|insert\\)\\(-\\|$\\)" (symbol-name this-command)))) - (when (and (marker-position hywiki--buttonize-start) - (marker-position hywiki--buttonize-end)) - ;; This means the command just deleted an opening or closing - ;; delimiter of a range that now needs any HyWikiWords - ;; inside to be re-highlighted. - (save-excursion - (goto-char hywiki--buttonize-start) - (let ((opening-char (char-after)) - closing-char) - (when (memq opening-char '(?\( ?\")) - (delete-char 1)) - (goto-char hywiki--buttonize-end) - (setq closing-char (char-before)) - (when (memq closing-char '(?\) ?\")) - (delete-char -1) - (insert " ")) - (goto-char hywiki--buttonize-start) - (hywiki-maybe-highlight-between-page-names) - (when (memq opening-char '(?\( ?\")) - (insert opening-char)) - (when (memq closing-char '(?\) ?\")) - (goto-char (1+ hywiki--buttonize-end)) - (delete-char -1) - (insert closing-char) - )))) - (hywiki-maybe-highlight-between-page-names)))) + (string-match-p "^\\(org-\\)?\\(delete-\\|kill-\\)\\|\\(-delete\\|-kill\\)\\(-\\|$\\)" (symbol-name this-command)))) + (setq hywiki--range nil) -(defun hywiki-debuttonize-non-character-commands () - "Dehighlight any HyWikiWord before or after point. -Triggered by `pre-command-hook' for non-character-commands, including -deletion commands and those in `hywiki-non-character-commands'." - (when (and (markerp hywiki--buttonize-start) (markerp hywiki--buttonize-end)) - (set-marker hywiki--buttonize-start nil) - (set-marker hywiki--buttonize-end nil)) - (when (and (or (memq this-command hywiki-non-character-commands) - (and (symbolp this-command) - (string-match-p "\\`\\(org-\\)?\\(delete-\\|kill-\\)\\|-delete-\\|-kill-" - (symbol-name this-command)))) - (or (not (derived-mode-p 'prog-mode)) - (apply #'derived-mode-p hywiki-highlight-all-in-prog-modes) - ;; Inside a comment or a string - (nth 4 (syntax-ppss)) - (hypb:in-string-p))) - (cl-destructuring-bind (start end) - (hywiki-get-delimited-range) ;; includes delimiters - ;; Use these to store any range of a delimited HyWikiWord#section - (set-marker hywiki--buttonize-start start) - (set-marker hywiki--buttonize-end end) - ;; Enable dehighlighting in HyWiki pages - (unless (and start end) - ;; Dehighlight any page name at point - (hywiki-maybe-dehighlight-between-page-names))))) + ;; Dehighlight any previously highlighted WikiWord at point + ;; before we move to the start of any current WikiWord and + ;; rehighlight that. + (hywiki--maybe-dehighlight-at-point) + + (save-excursion + (cond ((marker-position hywiki--buttonize-start) + ;; Point was before or after a WikiWord delimiter + (goto-char (1+ hywiki--buttonize-start))) + ((setq hywiki--range (hywiki-word-at :range)) + (cl-destructuring-bind (_ start end) + hywiki--range + (if (and start end) + (progn + ;; On a non-delimited HyWikiWord + (set-marker hywiki--buttonize-start start) + (set-marker hywiki--buttonize-end end) + (goto-char start) + (skip-chars-backward "-" (line-beginning-position)) + t) + (setq hywiki--range nil))))) + + (hywiki--maybe-rehighlight-at-point))))) + +(defun hywiki-buttonize-character-commands () + "Turn any HyWikiWords between point into highlighted Hyperbole buttons. +Triggered by `post-self-insert-hook' after self-inserting one or more +characters after `post-command-hook' has run." + ;; If `hywiki--flag' is set non-nil below, then + ;; `hywiki-buttonize-non-character-commands' on `post-command-hook' + ;; does nothing. + (unless (setq hywiki--flag (hywiki-non-hook-context-p)) + (setq hywiki--range nil) + + ;; Dehighlight any previously highlighted WikiWord at point + ;; before we move to the start of any current WikiWord and + ;; rehighlight that. + (hywiki--maybe-dehighlight-at-point) + + (save-excursion + (cond ((marker-position hywiki--buttonize-start) + ;; Point was before or after a WikiWord delimiter + (goto-char hywiki--buttonize-start) + (skip-chars-backward "-" (line-beginning-position)) + (goto-char (1- (point)))) + ((not (equal (setq hywiki--range (hywiki-word-at :range)) + '(nil nil nil))) + (cl-destructuring-bind (_ start end) + hywiki--range + (if (and start end) + (progn + ;; On a non-delimited HyWikiWord + (set-marker hywiki--buttonize-start start) + (set-marker hywiki--buttonize-end end) + (goto-char start) + (skip-chars-backward "-" (line-beginning-position)) + t) + (setq hywiki--range nil)))) + ((not (equal (setq hywiki--range (hywiki-at-range-delimiter)) ;; includes delimiters + '(nil nil))) + ;; At delimiters surrounding a WikiWord + (let ((start (nth 0 hywiki--range)) + (end (nth 1 hywiki--range))) + (when (and start end) + ;; Use these to store any range of a delimited HyWikiWord#section + (set-marker hywiki--buttonize-start (1+ start)) + (set-marker hywiki--buttonize-end (1- end)))))) + + ;; This first rehighlighting is needed to ensure + ;; any wikiword before an inserted whitespace character is + ;; properly highlighted when separating two words or after a + ;; closing delimiter. + (save-excursion + (goto-char (max (1- (point)) (point-min))) + (hywiki--maybe-rehighlight-at-point)) + + (hywiki--maybe-rehighlight-at-point)))) (defun hywiki-buttonize-word (func start end face) "Create a HyWikiWord button by calling FUNC with START and END positions. @@ -656,7 +684,7 @@ the button." cmd (cdr key-cmd)) (when (eq cmd 'self-insert-command) (cond ((and (characterp key) - (= (char-syntax key) ?.)) + (eq (char-syntax key) ?.)) ;; char with punctuation/symbol syntax (setq result (cons key result))) ((and (consp key) @@ -669,6 +697,15 @@ the button." (when (memq (char-syntax k) '(?. ?_)) (setq result (cons k result))))))))))) +(defun hywiki-non-hook-context-p () + (or (minibuffer-window-active-p (selected-window)) + (and (boundp 'edebug-active) edebug-active + (active-minibuffer-window)) + (and (derived-mode-p 'prog-mode) + (not (apply #'derived-mode-p hywiki-highlight-all-in-prog-modes)) + ;; Not inside a comment or a string + (not (or (nth 4 (syntax-ppss)) (hypb:in-string-p)))))) + ;;;###autoload (define-minor-mode hywiki-mode "Toggle HyWiki global minor mode with \\[hywiki-mode]. @@ -1430,15 +1467,8 @@ Use when publishing a HyWiki file to another format, e.g. html. For example, the link: \"WikiWord#Multi-Word Section\" -or - \"[[hy:WikiWord#Multi-Word Section]]\" is converted to: - \"[[file:<hywiki-directory>/WikiWord.org::Multi-Word Section] - [WikiWord#Multi-Word Section]]\". - -If the reference is in a file within the `hywiki-directory', it -simplifies to: - \"[[file:WikiWord.org::Multi-Word Section][WikiWord#Multi-Word Section]]\". + \"[[hy:WikiWord#Multi-Word Section]]\". If the reference is within the WikiWord page to which it refers, it simplifies to: @@ -1447,7 +1477,9 @@ simplifies to: The finalized Org link is then exported to html format by the Org publish process." (barf-if-buffer-read-only) - (hywiki-maybe-highlight-page-names) + ;; Need to be explicit about the region here so does not use markers + ;; from a region pointing to another buffer + (hywiki-maybe-highlight-page-names (point-min) (point-max)) (let ((make-index (hywiki-org-get-publish-property :makeindex)) org-link wikiword-and-section @@ -1516,7 +1548,9 @@ Do not test whether or not a page exists for the HyWiki word. Use `hywiki-get-referent' to determine whether a HyWiki page exists." ;; Ignore wikiwords preceded by any non-whitespace character, except ;; any of these: [({<"'`' - (when (or (bolp) (cl-find (char-before) "\[\(\{\<\"'`\t\n\r\f ")) + (when (or (bolp) + (string-match (regexp-quote (char-to-string (char-before))) + "\[\(\{\<\"'`\t\n\r\f ")) t)) (defun hywiki-directory-edit () @@ -1640,8 +1674,8 @@ positions of each HyWikiWord and its optional #section." (overlays-in (point-min) (point-max))))) nil) -(defun hywiki-get-delimited-range () - "Before or after a balanced delimiter, return the delimited range list. +(defun hywiki-at-range-delimiter () + "Immediately before or after a balanced delimiter, return the delimited range. If no such range, return \\='(nil nil). This includes the delimiters: (), {}, <>, [] and \"\" (double quotes)." (save-excursion @@ -1687,7 +1721,7 @@ This includes the delimiters: (), {}, <>, [] and \"\" (double quotes)." (not (hypb:in-string-p))) (list (point) (scan-sexps (point) 1)))) (error nil)))) - (if result + (if (and result (integerp (nth 0 result)) (integerp (nth 1 result))) (sort result #'<) (list nil nil)))))) @@ -1706,8 +1740,7 @@ and radio targets. Ignore return value; it has no meaning." (save-excursion (save-restriction - (if (and (marker-position hywiki--buttonize-start) - (marker-position hywiki--buttonize-end)) + (if (hywiki--buttonized-region-p) (narrow-to-region hywiki--buttonize-start hywiki--buttonize-end) ;; Limit balanced pair checks to the next two lines for speed (narrow-to-region (line-beginning-position) (line-end-position 2))) @@ -1773,8 +1806,7 @@ and radio targets. Return t if no errors and a pair was found, else nil." (save-excursion (save-restriction - (if (and (marker-position hywiki--buttonize-start) - (marker-position hywiki--buttonize-end)) + (if (hywiki--buttonized-region-p) (narrow-to-region hywiki--buttonize-start hywiki--buttonize-end) ;; Limit balanced pair checks to the next two lines for speed (narrow-to-region (line-beginning-position) (line-end-position 2))) @@ -1847,11 +1879,28 @@ Return t if no errors and a pair was found, else nil." (when result t))))) (defun hywiki-maybe-dehighlight-between-page-names () - "Dehighlight any non-Org link HyWiki page#section names between point. -If in a programming mode, must be within a comment. Use + "Dehighlight any non-Org link HyWiki page#section between point. +If in a programming mode, must be within a comment or string. Use `hywiki-word-face' to dehighlight." - (hywiki-maybe-dehighlight-off-page-name) - (hywiki-maybe-dehighlight-on-page-name)) + (when (hproperty:char-property-range (point) 'face hywiki-word-face) + (hproperty:but-clear-all-in-list + (hproperty:but-get-all-in-region (point) (1+ (point)) + 'face hywiki-word-face))) + + (cond ((cl-destructuring-bind (start end) + (hywiki-at-range-delimiter) + (when (and start end) + (save-excursion + (goto-char (1+ start)) + (and (hproperty:char-property-range (point) 'face hywiki-word-face) + (equal (hywiki-referent-exists-p :range) + '(nil nil nil)) + ;; non-existing wikiword + (hywiki-maybe-dehighlight-on-page-name))) + t))) + ((looking-at "[ \t\n\r\f]") + (hywiki-maybe-dehighlight-off-page-name) + (hywiki-maybe-dehighlight-on-page-name)))) (defun hywiki-maybe-dehighlight-off-page-name () "Dehighlight any non-Org link HyWiki page#section at or one char before point. @@ -1862,7 +1911,7 @@ in a programming mode, must be within a comment." (hywiki-maybe-dehighlight-page-name ;; Flag on-page-name if on a whitespace character (or (= (point) (point-max)) - (= (char-syntax (char-after)) ? )))) + (= (if (char-after) (char-syntax (char-after)) 0) ? )))) (defun hywiki-maybe-dehighlight-on-page-name () "Dehighlight any non-Org link HyWiki page#section at or one char before point. @@ -1873,7 +1922,7 @@ be within a comment." (hywiki-maybe-dehighlight-page-name ;; Flag on-page-name if not on a whitespace character (and (/= (point) (point-max)) - (/= (char-syntax (char-after)) ? )))) + (/= (if (char-after) (char-syntax (char-after)) 0) ? )))) ;;;###autoload (defun hywiki-maybe-dehighlight-page-name (&optional on-page-name) @@ -1886,6 +1935,8 @@ use these as the region in which to dehighlight. If in a programming mode, must be within a comment. Use `hywiki-word-face' to dehighlight." (interactive) + (setq hywiki--start nil + hywiki--end nil) (when (and (hywiki-active-in-current-buffer-p) (if (and (derived-mode-p 'prog-mode) (not (apply #'derived-mode-p hywiki-highlight-all-in-prog-modes))) @@ -1893,7 +1944,8 @@ If in a programming mode, must be within a comment. Use (or (nth 4 (syntax-ppss)) (hypb:in-string-p)) t) (or on-page-name - (cl-find (char-syntax last-command-event) + (string-match (regexp-quote + (char-to-string (char-syntax last-command-event))) " _()<>$.\"'")) (not executing-kbd-macro) (not noninteractive)) @@ -1901,8 +1953,7 @@ If in a programming mode, must be within a comment. Use (with-syntax-table hbut:syntax-table (save-excursion (save-restriction - (when (and (marker-position hywiki--buttonize-start) - (marker-position hywiki--buttonize-end)) + (when (hywiki--buttonized-region-p) (narrow-to-region hywiki--buttonize-start hywiki--buttonize-end) (goto-char hywiki--buttonize-start)) @@ -1915,7 +1966,7 @@ If in a programming mode, must be within a comment. Use (unless hywiki--highlighting-done-flag (unless on-page-name ;; May be a closing delimiter that we have to skip past - (skip-chars-backward (regexp-quote (hywiki-get-buttonize-characters)))) + (skip-chars-backward (hywiki-get-buttonize-characters))) ;; Skip past HyWikiWord or section (skip-syntax-backward "^-$()<>._\"\'") (skip-chars-backward "-_*#:[:alnum:]") @@ -1924,31 +1975,29 @@ If in a programming mode, must be within a comment. Use case-fold-search nil hywiki--save-org-link-type-required hywiki-org-link-type-required hywiki-org-link-type-required t) - (if (and (hywiki-maybe-at-wikiword-beginning) - (looking-at hywiki--word-and-buttonize-character-regexp) - (progn - (setq hywiki--page-name (match-string-no-properties 2) - hywiki--start (match-beginning 1) - hywiki--end (match-end 1)) - (hywiki-get-referent hywiki--page-name))) - (when (setq hywiki--buts (hproperty:but-get-all-in-region - hywiki--start hywiki--end - 'face hywiki-word-face)) - (hproperty:but-clear-all-in-list hywiki--buts)) + (unless (and (hywiki-maybe-at-wikiword-beginning) + (looking-at hywiki--word-and-buttonize-character-regexp) + (progn + (setq hywiki--word-only (match-string-no-properties 2) + hywiki--start (match-beginning 1) + hywiki--end (match-end 1)) + (hywiki-get-referent hywiki--word-only))) ;; Remove any potential earlier highlighting since the ;; previous word may have changed. - (skip-syntax-backward "^-$()<>._\"\'") - (hproperty:but-clear-all-in-list - (hproperty:but-get-all-in-region (point) (1+ (point)) - 'face hywiki-word-face))))))))) + (skip-syntax-backward "^-$()<>._\"\'")) + + (hproperty:but-clear-all-in-list + (hproperty:but-get-all-in-region (or hywiki--start (point)) + (or hywiki--end (1+ (point))) + 'face hywiki-word-face)))))))) ;;;###autoload (defun hywiki-maybe-highlight-page-name (&optional on-page-name) - "Highlight any non-Org link HyWiki page#section at or one char before point. + "Highlight any non-Org link HyWikiWord#section at or one char before point. With optional ON-PAGE-NAME non-nil, assume point is within the page or -section name. Otherwise, if `pre-command-hook' has set +section name. Otherwise, if a HyWiki per-character hook has set `hywiki--buttonize-start' `hywiki--buttonize-end' global variables, -use these as the region in which to highlight. +use these as the region to highlight. If in a programming mode, must be within a comment. Use `hywiki-word-face' to highlight. Do not highlight references to @@ -1957,89 +2006,87 @@ the current page unless they have sections attached." (when (and (hywiki-active-in-current-buffer-p) (if (and (derived-mode-p 'prog-mode) (not (apply #'derived-mode-p hywiki-highlight-all-in-prog-modes))) - ;; Non-nil if match is inside a comment + ;; Non-nil if match is inside a comment or string (or (nth 4 (syntax-ppss)) (hypb:in-string-p)) t) ;; (or on-page-name - ;; (cl-find (char-syntax last-command-event) - ;; " _()<>$.\"'")) + ;; (string-match (regexp-quote (char-to-string (char-syntax last-command-event))) + ;; " _()<>$.\"'")) (not executing-kbd-macro) (not noninteractive)) (setq hywiki--highlighting-done-flag nil) (with-syntax-table hbut:syntax-table (save-excursion - (save-restriction - (when (and (marker-position hywiki--buttonize-start) - (marker-position hywiki--buttonize-end)) - (narrow-to-region hywiki--buttonize-start hywiki--buttonize-end) - (goto-char hywiki--buttonize-start)) + (when (hywiki--buttonized-region-p) + (goto-char hywiki--buttonize-start)) - (unless on-page-name - ;; after page name - (skip-syntax-backward ">-")) + (unless on-page-name + ;; after page name + (skip-syntax-backward ">-")) + + (unless (or hywiki--highlighting-done-flag + (hywiki-maybe-highlight-balanced-pairs)) - (unless (or hywiki--highlighting-done-flag - (hywiki-maybe-highlight-balanced-pairs)) + (unless on-page-name + ;; May be a HyWikiWord ending character to skip past + (skip-chars-backward (hywiki-get-buttonize-characters) + (line-beginning-position))) + ;; Skip past HyWikiWord or section + (skip-syntax-backward "^-$()<>._\"\'") + (skip-chars-backward "-_*#:[:alnum:]") - (unless on-page-name - ;; May be a closing delimiter that we have to skip past - (skip-chars-backward (regexp-quote (hywiki-get-buttonize-characters)))) - ;; Skip past HyWikiWord or section + (setq hywiki--save-case-fold-search case-fold-search + case-fold-search nil + hywiki--save-org-link-type-required hywiki-org-link-type-required + hywiki-org-link-type-required t + hywiki--start nil + hywiki--end nil) + + (if (and (cl-destructuring-bind (word start end) + (hywiki-word-at :range) + (setq hywiki--word-only word + hywiki--start start + hywiki--end end)) + hywiki--start + (hywiki-get-referent hywiki--word-only) + (goto-char hywiki--start)) + (progn + (setq hywiki--current-page (hywiki-get-buffer-page-name)) + ;; Don't highlight current-page matches unless they + ;; include a #section. + (unless (string-equal hywiki--current-page + (buffer-substring-no-properties + hywiki--start hywiki--end)) + (if (setq hywiki--buts (hproperty:but-get-all-in-region + hywiki--start hywiki--end + 'face hywiki-word-face)) + (if (> (length hywiki--buts) 1) + (progn (hproperty:but-clear-all-in-list hywiki--buts) + (hywiki-maybe-highlight-page-names + hywiki--start hywiki--end)) + ;; There is only one existing button + (setq hywiki--buts (car hywiki--buts) + hywiki--but-start (hproperty:but-start hywiki--buts) + hywiki--but-end (hproperty:but-end hywiki--buts)) + (unless (and (= hywiki--start hywiki--but-start) + (= hywiki--end hywiki--but-end)) + (hproperty:but-delete hywiki--buts) + (hywiki-maybe-highlight-page-names + hywiki--start hywiki--end))) + (hywiki-maybe-highlight-page-names + hywiki--start hywiki--end)))) + ;; Remove any potential earlier highlighting since the + ;; previous word may have changed. (skip-syntax-backward "^-$()<>._\"\'") - (skip-chars-backward "-_*#:[:alnum:]") - - (setq hywiki--save-case-fold-search case-fold-search - case-fold-search nil - hywiki--save-org-link-type-required hywiki-org-link-type-required - hywiki-org-link-type-required t - hywiki--start nil - hywiki--end nil) - - (if (and (hywiki-maybe-at-wikiword-beginning) - (looking-at hywiki--word-and-buttonize-character-regexp) - (progn - (setq hywiki--page-name (match-string-no-properties 2) - hywiki--start (match-beginning 1) - ;; This excludes optional char after the page#section - hywiki--end (match-end 1)) - (hywiki-get-referent hywiki--page-name))) - (progn - (setq hywiki--current-page (hywiki-get-buffer-page-name)) - ;; Don't highlight current-page matches unless they - ;; include a #section. - (unless (string-equal hywiki--current-page - (buffer-substring-no-properties - hywiki--start hywiki--end)) - (if (setq hywiki--buts (hproperty:but-get-all-in-region - hywiki--start hywiki--end - 'face hywiki-word-face)) - (if (> (length hywiki--buts) 1) - (progn (hproperty:but-clear-all-in-list hywiki--buts) - (hywiki-maybe-highlight-page-names - hywiki--start hywiki--end)) - ;; There is only one existing button - (setq hywiki--buts (car hywiki--buts) - hywiki--but-start (hproperty:but-start hywiki--buts) - hywiki--but-end (hproperty:but-end hywiki--buts)) - (unless (and (= hywiki--start hywiki--but-start) - (= hywiki--end hywiki--but-end)) - (hproperty:but-delete hywiki--buts) - (hywiki-maybe-highlight-page-names - hywiki--start hywiki--end))) - (hywiki-maybe-highlight-page-names - hywiki--start hywiki--end)))) - ;; Remove any potential earlier highlighting since the - ;; previous word may have changed. - (skip-syntax-backward "^-$()<>._\"\'") - (if (setq hywiki--buts (hproperty:but-get-all-in-region + (when (setq hywiki--buts (hproperty:but-get-all-in-region (point) (1+ (point)) 'face hywiki-word-face)) - (if (> (length hywiki--buts) 1) - (hproperty:but-clear-all-in-list hywiki--buts) - ;; There is only one existing button - (setq hywiki--buts (car hywiki--buts) - hywiki--but-start (hproperty:but-start hywiki--buts) - hywiki--but-end (hproperty:but-end hywiki--buts)) - (hproperty:but-delete hywiki--buts)))))))))) + (if (> (length hywiki--buts) 1) + (hproperty:but-clear-all-in-list hywiki--buts) + ;; There is only one existing button + (setq hywiki--buts (car hywiki--buts) + hywiki--but-start (hproperty:but-start hywiki--buts) + hywiki--but-end (hproperty:but-end hywiki--buts)) + (hproperty:but-delete hywiki--buts))))))))) (defun hywiki-maybe-highlight-between-page-names () "Highlight any non-Org link HyWiki page#section names between point. @@ -2047,8 +2094,31 @@ the current page unless they have sections attached." If in a programming mode, must be within a comment. Use `hywiki-word-face' to highlight. Do not highlight references to the current page unless they have sections attached." - (hywiki-maybe-highlight-off-page-name) - (hywiki-maybe-highlight-on-page-name)) + (cond ((hproperty:char-property-range (point) 'face hywiki-word-face)) + ((cl-destructuring-bind (word start end) + (hywiki-word-at :range) + (when (and start end) + (save-excursion + (goto-char start) + (when (hywiki-referent-exists-p word) + ;; existing wikiword + (hywiki-maybe-highlight-on-page-name))) + t))) + ((cl-destructuring-bind (start end) + (hywiki-at-range-delimiter) + (when (and start end) + (save-excursion + (goto-char (1+ start)) + (skip-syntax-forward "-" (line-end-position)) + (unless (equal (hywiki-referent-exists-p :range) + '(nil nil nil)) + ;; existing wikiword + (hywiki-maybe-highlight-on-page-name))) + t))) + ((looking-at "[ \t\n\r\f]") + (hywiki-maybe-highlight-off-page-name) + (hywiki-maybe-highlight-on-page-name)) + (t (hywiki-maybe-highlight-on-page-name)))) (defun hywiki-maybe-highlight-off-page-name () "Highlight any non-Org link HyWiki page#section at or one char before point. @@ -2062,9 +2132,9 @@ the current page unless they have sections attached." (hywiki-maybe-highlight-page-name ;; flag on-page-name if on a whitespace character (and (or (= (point) (point-max)) - (= (char-syntax (char-after)) ? )) + (= (if (char-after) (char-syntax (char-after)) 0) ?\ )) (or (= (point) (point-min)) - (/= (char-syntax (char-before)) ? ))))) + (/= (if (char-before) (char-syntax (char-before)) 0) ?\ ))))) (defun hywiki-maybe-highlight-on-page-name () "Highlight any non-Org link HyWiki page#section at or one char before point. @@ -2077,7 +2147,7 @@ the current page unless they have sections attached." (hywiki-maybe-highlight-page-name ;; flag on-page-name if not on a whitespace character (and (/= (point) (point-max)) - (/= (char-syntax (char-after)) ? )))) + (/= (if (char-after) (char-syntax (char-after)) 0) ? )))) (defun hywiki-maybe-dehighlight-org-element-backward () "Dehighlight HyWikiWords within a closing double/single square/angle bracket." @@ -2136,8 +2206,8 @@ interactively), limit dehighlighting to the region." 'face hywiki-word-face)) (unless (or region-start region-end) (setq hywiki-buffer-highlighted-state 'd)))) - -;;;###autoload +; +;;###autoload (defun hywiki-maybe-highlight-page-names (&optional region-start region-end skip-lookups-update-flag) "Highlight each non-Org link HyWiki page#section in a buffer/region. With optional REGION-START and REGION-END positions or markers (active @@ -2179,9 +2249,12 @@ value of `hywiki-word-highlight-flag' is changed." (narrow-to-region region-start region-end))) ((and region-start region-end) (narrow-to-region region-start region-end))) - ;; Enable dehighlighting in HyWiki pages - (let ((hywiki-word-highlight-flag)) - (hywiki-maybe-dehighlight-page-names)) + ;; Enable dehighlighting in HyWiki pages only when + ;; whole buffer is being processed; this prevents an + ;; error when called from `hywiki-maybe-highlight-sexp'. + (unless (and region-start region-end) + (let ((hywiki-word-highlight-flag)) + (hywiki-maybe-dehighlight-page-names))) (dolist (hywiki-words-regexp hywiki--any-wikiword-regexp-list) (goto-char (point-min)) (let ((highlight-in-comments-and-strings-only @@ -2406,7 +2479,7 @@ regexps of wikiwords, if the hash table is out-of-date." (defun hywiki-get-singular-wikiword (wikiword) "Return the singular version of the given WIKIWORD with any suffix removed. If `hywiki-allow-plurals-flag' is nil, return unchanged WIKIWORD name -with andy suffix removed." +with any suffix removed." (setq wikiword (hywiki-word-strip-suffix wikiword)) (if (or (not hywiki-allow-plurals-flag) (not (stringp wikiword))) @@ -2492,7 +2565,11 @@ save and potentially set `hywiki--directory-mod-time' and (or (file-writable-p save-file) (error "(hywiki-cache-save): Non-writable Environment file, \"%s\"" save-file)) (let ((buf (get-file-buffer save-file))) - (and buf (kill-buffer buf))) + (when buf + (if (buffer-modified-p buf) + (save-buffer) + ;; (error "(hywiki-cache-save): Attempt to kill modified Environment file failed to save, \"%s\"" save-file) + (kill-buffer buf)))) (let ((dir (or (file-name-directory save-file) default-directory))) (or (file-writable-p dir) @@ -2518,8 +2595,9 @@ save and potentially set `hywiki--directory-mod-time' and (princ ")\n") (save-buffer) - (set-buffer-modified-p nil) - (kill-buffer standard-output))))) + (if (buffer-modified-p) + (error "(hywiki-cache-save): Attempt to kill modified Environment file failed to save, \"%s\"" save-file) + (kill-buffer standard-output)))))) (defun hywiki-make-referent-hasht () "Rebuld referent hasht from list of HyWiki page files and non-page entries." @@ -2713,6 +2791,7 @@ variables." :complete #'hywiki-org-link-complete :export #'hywiki-org-link-export :follow #'hywiki-find-referent + :htmlize-link #'hywiki-section-to-headline-reference :store #'hywiki-org-link-store)) (defun hywiki-word-strip-suffix (page-name) @@ -2737,8 +2816,8 @@ Customize this directory with: {M-x customize-variable RET hywiki-org-publishing-directory RET}." (interactive "P") ;; Export Org to html with useful link ids. - ;; Instead of random ids like \"orga1b2c3\", use heading titles, - ;; made unique when necessary." + ;; Instead of random ids like "orga1b2c3", use heading titles with + ;; spaces replaced with dashes, made unique when necessary. (unwind-protect (progn (advice-add #'org-export-get-reference :override #'hywiki--org-export-get-reference) @@ -2746,37 +2825,48 @@ Customize this directory with: (advice-remove #'org-export-get-reference #'hywiki--org-export-get-reference))) (defun hywiki-referent-exists-p (&optional word start end) - "Return an optional HyWiki WORD or word at point, if has an existing referent. + "Return the HyWikiWord at point or optional HyWiki WORD, if has a referent. If no such referent exists, return nil. Word may be of 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: (word - word-start word-end) instead of the word. + word-start word-end) 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." - (setq hywiki--page-name word) +at point must return non-nil or this function will return nil." + (setq hywiki--word-only word) (when (stringp word) (setq word (hywiki-strip-org-link word))) (if (or (stringp word) (setq word (hywiki-word-at word))) - (unless (hywiki-get-referent word) + (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 + ;; first above word (nth 0 word))) - (if (eq hywiki--page-name :range) - (if (and word (setq hywiki--range - (hproperty:char-property-range - (point) 'face hywiki-word-face))) - (list word (or start (car hywiki--range)) (or end (cdr hywiki--range))) - (list word start end)) + (if (eq hywiki--word-only :range) + (or (hywiki-word-at :range) + (list word start end)) word)) +(defun hywiki-section-to-headline-reference () + "Replace file#section dashes with spaces to match to an Org headline. +Does replacement only when not in a programming mode and section +contains no spaces." + (let ((link (get-text-property (point) 'org-link))) + (if (and link (string-match "#" link)) + (let* ((file (substring link 0 (match-beginning 0))) + (section (substring link (match-beginning 0)))) + (concat file (hpath:dashes-to-spaces-markup-anchor section))) + link))) + (defun hywiki-strip-org-link (link-str) "Return the hy:HyWikiWord#section part of an Org link string. Strip any square bracket delimiters, description and leading or @@ -2846,12 +2936,13 @@ Action Key press; with a prefix ARG, emulate an Assist Key press." (hywiki-find-referent word) (hkey-either arg)))) -(defun hywiki-word-at (&optional range-flag) - "Return potential HyWikiWord and optional #section:Lnum:Cnum at point or nil. -Point should be on the HyWikiWord itself. +(defun hywiki-word-highlighted-at-p (&optional range-flag) + "Return highlighted HyWikiWord and optional #section:Lnum:Cnum at point or nil. +If the HyWikiWord is delimited, point must be within the delimiters. With optional RANGE-FLAG, return a list of (HyWikiWord start-position -end-position); the positions are for only the HyWikiWord itself. +end-position); the positions include the entire +HyWikiWord#section:Lnum:Cnum string but exclude any delimiters. This does not test whether a referent exists for the HyWiki word; call `hywiki-referent-exists-p' without an argument for that. @@ -2862,10 +2953,29 @@ or this will return nil." (if (setq hywiki--range (hproperty:char-property-range (point) 'face hywiki-word-face)) (let ((wikiword (buffer-substring-no-properties (car hywiki--range) (cdr hywiki--range)))) - (when (string-match hywiki-word-with-optional-suffix-exact-regexp wikiword) - (if range-flag - (list wikiword (car hywiki--range) (cdr hywiki--range)) - wikiword))) + (if (string-match hywiki-word-with-optional-suffix-exact-regexp wikiword) + (if range-flag + (list wikiword (car hywiki--range) (cdr hywiki--range)) + wikiword) + (when range-flag + '(nil nil nil))))))) + +(defun hywiki-word-at (&optional range-flag) + "Return potential HyWikiWord and optional #section:Lnum:Cnum at point or nil. +If the HyWikiWord is delimited, point must be within the delimiters. +This works regardless of whether the HyWikiWord has been highlighted +or not. + +With optional RANGE-FLAG, return a list of (HyWikiWord start-position +end-position); the positions include the entire +HyWikiWord#section:Lnum:Cnum string but exclude any delimiters. + +This does not test whether a referent exists for the HyWiki word; call +`hywiki-referent-exists-p' without an argument for that. + +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 ;; 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 @@ -2880,56 +2990,183 @@ or this will return nil." (wikiword (nth 0 wikiword-start-end)) (start (nth 1 wikiword-start-end)) (end (nth 2 wikiword-start-end))) - (when (if 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)))) - (progn - (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 a non-delimited HyWiki word 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: "([\"'`'" - (let ((case-fold-search nil)) - (skip-chars-backward "-_*#:[:alnum:]") - (when (hywiki-maybe-at-wikiword-beginning) - (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)))) - ((looking-at (concat hywiki-word-with-optional-suffix-regexp "\\'")) + (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)))))))) - (if range-flag - (list wikiword start end) - wikiword))))))) + (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) + ;; 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 + (buffer-substring-no-properties start end))))))))) + ;; If `wikiword' has a #section, ensure there are + ;; no invalid chars + (if (and (stringp wikiword) (string-match "#" wikiword)) + (string-match "#[^][#()<>{}\"\n\r\f]+\\'" wikiword) + t)) + (if range-flag + (list wikiword start end) + wikiword) + (when range-flag + '(nil nil nil)))))) + (when range-flag + '(nil nil nil)))) (defun hywiki-word-at-point () "Return singular HyWikiWord at point with its suffix stripped or nil. -Point should be on the HyWikiWord itself." +Point should be on the HyWikiWord itself. Suffix is anything after +the # symbol. + +This does not test whether a referent exists for the HyWiki word; call +`hywiki-referent-exists-p' without an argument for that. + +A call to `hywiki-active-in-current-buffer-p' at point must return non-nil +or this will return nil." (hywiki-get-singular-wikiword (hywiki-word-strip-suffix (hywiki-word-at)))) +(defun hywiki-delimited-p (&optional pos) + "Return non-nil if optional POS or point is surrounded by matching delimiters. +The delimited range must be two lines or less. + +Use `hywiki-word-at', which calls this, to determine whether there is +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)))))))) + (defun hywiki-word-face-at-p () "Non-nil if but at point has `hywiki-word-face' property." (hproperty:but-get (point) 'face hywiki-word-face)) @@ -2962,12 +3199,16 @@ Return nil if WORD is a prefixed, typed hy:HyWikiWord, since these are handled by the Org mode link handler." (and (stringp word) (not (string-empty-p word)) (let (case-fold-search) - (or (string-match hywiki-word-with-optional-suffix-exact-regexp word) - ;; For now this next version allows spaces and tabs in - ;; the suffix part - (eq 0 (string-match - hywiki-word-with-optional-spaces-suffix-exact-regexp - word)))))) + (and (or (string-match hywiki-word-with-optional-suffix-exact-regexp word) + ;; For now this next version allows spaces and tabs in + ;; the suffix part + (eq 0 (string-match + hywiki-word-with-optional-suffix-exact-regexp + word))) + ;; If has a #section, ensure there are no invalid chars + (if (string-match "#" word) + (string-match "#[^][#()<>{}\"\n\r\f]+\\'" word) + t))))) (defun hywiki-word-read (&optional prompt) "Prompt with completion for and return an existing HyWikiWord. @@ -3036,6 +3277,13 @@ auto-highlighting." ;;; Private functions ;;; ************************************************************************ +(defun hywiki--buttonized-region-p () + "Return non-nil when hywiki--buttonize-start/end point to the current buffer." + (and (marker-position hywiki--buttonize-start) + (eq (marker-buffer hywiki--buttonize-start) (current-buffer)) + (marker-position hywiki--buttonize-end) + (eq (marker-buffer hywiki--buttonize-end) (current-buffer)))) + (defun hywiki--add-suffix-to-referent (suffix referent) "Add SUFFIX to REFERENT's value and return REFERENT. SUFFIX includes its type prefix, e.g. #. Return nil if any input is @@ -3114,7 +3362,8 @@ or balanced pair delimiters." (defun hywiki--get-delimited-range-backward () "Return a list of (start end) if not between/after end ]] or >>. -Otherwise, return nil." +Delimiters are included in the range. Point must be on or after the +closing delimiter. Otherwise, return nil." (save-excursion (unless (or (eq (char-before) (char-before (1- (point)))) (and (char-after) @@ -3124,7 +3373,8 @@ Otherwise, return nil." (defun hywiki--get-delimited-range-forward () "Return a list of (start end) if not between/before opening [[ or <<. -Otherwise, return nil." +Delimiters are included in the range. Point must be on or after the +opening delimiter. Otherwise, return nil." (save-excursion (unless (or (eq (char-after) (char-after (1+ (point)))) (and (char-before) @@ -3174,21 +3424,66 @@ delimited grouping." ;; single delimiters - highlight (funcall func 1)))) -(defun hywiki--maybe-de/highlight-sexp (func direction-number) - "De/highlight HyWikiWord with FUNC on a single square/angle bracket. +(defun hywiki--maybe-de/highlight-sexp (func direction-number &optional sexp-start sexp-end) + "De/highlight HyWikiWord with FUNC on a single paired delimiter char. DIRECTION-NUMBER is 1 for forward scanning and -1 for backward scanning." - (let* ((sexp-start (point)) - (sexp-end (scan-sexps sexp-start direction-number))) - (when (and sexp-start sexp-end) - (cl-destructuring-bind (start end) - ;; Point may be at end of sexp, so start and end may - ;; need to be reversed. - (list (min sexp-start sexp-end) (max sexp-start sexp-end)) - ;; Increment sexp-start so regexp matching excludes the - ;; delimiter and starts with the page name. But include any - ;; trailing delimiter or regexp matching will not work. - (funcall func (1+ start) end) - (setq hywiki--highlighting-done-flag nil))))) + (setq sexp-start (or sexp-start (point)) + sexp-end (or sexp-end (scan-sexps sexp-start direction-number))) + (when (and sexp-start sexp-end) + (cl-destructuring-bind (start end) + ;; Point may be at end of sexp, so start and end may + ;; need to be reversed. + (list (min sexp-start sexp-end) (max sexp-start sexp-end)) + ;; Increment sexp-start so regexp matching excludes the + ;; delimiter and starts with the HyWikiWord. But include any + ;; trailing delimiter or regexp matching will not work. + (save-restriction + (narrow-to-region (1+ start) end) + (prog1 (funcall func (1+ start) end) + (setq hywiki--highlighting-done-flag nil)))))) + +(defun hywiki--maybe-dehighlight-at-point () + "Dehighlight any existing HyWikiWord when needed. +That is, only if the editing command has changed the word-only part of +the HyWikiWord reference." + (when (and hywiki--word-pre-command + (not (equal hywiki--word-pre-command + (hywiki-get-singular-wikiword + (or (car hywiki--range) + (when (hywiki--buttonized-region-p) + (buffer-substring hywiki--buttonize-start + hywiki--buttonize-end)) + (when (and (setq hywiki--range (hywiki-word-at :range)) + (nth 1 hywiki--range)) + (prog1 (nth 1 hywiki--range) + (setq hywiki--range nil))) +))))) + ;; Dehighlight if point is on or between a HyWikiWord + (hywiki-maybe-dehighlight-between-page-names))) + +(defun hywiki--maybe-rehighlight-at-point () + "Dehighlight any existing HyWikiWord when needed. +That is, only if the editing command has changed the word-only part of +the HyWikiWord reference. + +This must be called within a `save-excursion' or it may move point." + + (hywiki--maybe-dehighlight-at-point) + + ;; Highlight wikiwords around point as needed + (when hywiki--range + (hywiki-maybe-highlight-on-page-name)) + + (when (hywiki--buttonized-region-p) + (hywiki--maybe-de/highlight-sexp + #'hywiki-maybe-highlight-page-names 1 + hywiki--buttonize-start hywiki--buttonize-end)) + + (cond ((= (char-syntax (or (char-before) 0)) ?\ ) + (goto-char (1- (point))) + (hywiki-maybe-highlight-between-page-names)) + ((= (char-syntax (or (char-after) 0)) ?\ ) + (hywiki-maybe-highlight-between-page-names)))) ;;; ************************************************************************ ;;; Private Org export override functions @@ -3293,7 +3588,8 @@ matching DATUM before creating a new reference." num (if num (string-to-number num) 0) - ref (format "%s%s" parent (cl-incf num))))))) + num (1+ num) + ref (format "%s%s" parent num)))))) ref)) (defun hywiki--org-format-reference (title) @@ -3311,7 +3607,7 @@ matching DATUM before creating a new reference." ;; Must be set after `hywiki-get-buttonize-characters' is defined (unless hywiki--buttonize-characters (setq hywiki--buttonize-characters - (concat "[]()<>{} \t\r\n'" (hywiki-get-buttonize-characters)) + (concat "[]()<>{}\"' \t\r\n" (hywiki-get-buttonize-characters)) hywiki--buttonize-character-regexp (concat "\\([][" (regexp-quote (substring hywiki--buttonize-characters 2)) @@ -3339,6 +3635,10 @@ matching DATUM before creating a new reference." (hywiki-word-highlight-flag-changed 'hywiki-word-highlight-flag hywiki-word-highlight-flag 'set nil) +;; Ensures HyWiki referent lookup table is initialized as are HyWiki Org +;; Publish settings. +(hywiki-set-directory 'hywiki-directory hywiki-directory) + (provide 'hywiki) ;;; hywiki.el ends here diff --git a/man/hyperbole.texi b/man/hyperbole.texi index e66da842f1..438331af61 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: 19-Jan-25 at 18:06:31 by Bob Weiner +@c Last-Mod: 16-Mar-25 at 10:23:12 by Bob Weiner @c %**start of header (This is for running Texinfo on a region.) @setfilename hyperbole.info @@ -30,8 +30,8 @@ @set txicodequoteundirected @set txicodequotebacktick -@set UPDATED January, 2025 -@set UPDATED-MONTH January 2025 +@set UPDATED March, 2025 +@set UPDATED-MONTH March 2025 @set EDITION 9.0.2pre @set VERSION 9.0.2pre @@ -171,7 +171,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.</P> <PRE> Edition 9.0.2pre -Printed January 19, 2025. +Printed March 16, 2025. 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 -January 19, 2025 +March 16, 2025 Published by the Free Software Foundation, Inc. Author: Bob Weiner @@ -538,8 +538,8 @@ Smart Keyboard Keys @chapter Introduction This edition of the GNU Hyperbole Manual is for use with any version -9.0.2pre or greater of GNU Hyperbole. Hyperbole runs atop GNU Emacs 27.2 -or higher. It will trigger an error if your Emacs is older. +9.0.2pre or greater of GNU Hyperbole. Hyperbole runs atop GNU Emacs +28 or higher. It will trigger an error if your Emacs is older. This chapter summarizes the structure of the rest of the manual, describes Hyperbole, lists some of its potential applications, and @@ -8628,8 +8628,8 @@ the GNU Emacs Manual}). @group ;; Below are the lines to add: -(when (< emacs-major-version 27) - (error "Hyperbole requires Emacs 27 or above, not %d" +(when (< emacs-major-version 28) + (error "Hyperbole requires Emacs 28 or above, not %d" emacs-major-version)) (require 'package) (unless (package-installed-p 'hyperbole) @@ -8677,8 +8677,8 @@ the GNU Emacs Manual}). @group ;; Below are the lines to add: -(when (< emacs-major-version 27) - (error "Hyperbole requires Emacs 27 or above, not %d" +(when (< emacs-major-version 28) + (error "Hyperbole requires Emacs 28 or above, not %d" emacs-major-version)) (require 'package) (add-to-list 'package-archives @@ -8724,8 +8724,8 @@ the GNU Emacs Manual}). @group ;; Use this in your Emacs init file to install Straight (progn - (when (< emacs-major-version 27) - (error "Hyperbole requires Emacs 27 or above, not %d" + (when (< emacs-major-version 28) + (error "Hyperbole requires Emacs 28 or above, not %d" emacs-major-version)) (defvar bootstrap-version) (setq package-enable-at-startup nil) @@ -8785,8 +8785,8 @@ lines to your personal Emacs initialization file, @file{~/.emacs}: @lisp @group (unless (and (featurep 'hyperbole) hyperbole-mode) - (when (< emacs-major-version 27) - (error "Hyperbole requires Emacs 27 or above, not %d" + (when (< emacs-major-version 28) + (error "Hyperbole requires Emacs 28 or above, not %d" emacs-major-version)) (push "<directory-ending-with-hyperbole-where-you-unpacked>" load-path) diff --git a/test/MANIFEST b/test/MANIFEST index 00dbe43c1e..37356fdd7b 100644 --- a/test/MANIFEST +++ b/test/MANIFEST @@ -13,6 +13,7 @@ hpath-tests.el - unit tests for hpath hsettings-test.el - unit tests for hsettings hsys-org-tests.el - hsys-org tests hui-mini-tests.el - hui-mini tests +hui-mouse-tests.el - hui-mouse and hkey-alist Action Key tests hui-register-tests.el - test for hui-register hui-select-tests.el - hui-select tests hui-tests.el - tests for hui.el Hyperbole UI diff --git a/test/hsys-org-tests.el b/test/hsys-org-tests.el index 48117ee803..a7fa8ed9a0 100644 --- a/test/hsys-org-tests.el +++ b/test/hsys-org-tests.el @@ -3,7 +3,7 @@ ;; Author: Mats Lidell <ma...@gnu.org> ;; ;; Orig-Date: 23-Apr-21 at 20:55:00 -;; Last-Mod: 16-Nov-24 at 09:45:51 by Mats Lidell +;; Last-Mod: 13-Apr-25 at 03:23:46 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -139,9 +139,8 @@ This is independent of the setting of `hsys-org-enable-smart-keys'." (with-temp-buffer (insert "[[file:/tmp/abc][file]]\n") (goto-char 6) - (mocklet (((org-open-at-point-global) => t)) - (should (equal hsys-org-enable-smart-keys v)) ; Traceability - (should (action-key)))))))) + (should (equal hsys-org-enable-smart-keys v)) ; Traceability + (should (action-key))))))) (ert-deftest hsys-org--org-outside-org-mode-tmp-file () "Org links in a non `org-mode' file should work. diff --git a/test/hywiki-tests.el b/test/hywiki-tests.el index 1e7f54e2dd..efc1071d6a 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: 19-Mar-25 at 19:59:12 by Mats Lidell +;; Last-Mod: 12-Apr-25 at 17:00:40 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -234,51 +234,60 @@ line 2 (progn (hywiki-mode 1) - ;; Matches a WikiWord - (dolist (v '("WikiWord" "[WikiWord]" "[[WikiWord]]" "{WikiWord}" "(WikiWord)" - "<WikiWord>" "<<WikiWord>>" "{[[WikiWord]]}" "([[WikiWord]])" - "[WikiWord AnotherWord]" - )) - (with-temp-buffer - (org-mode) - (insert v) - (newline nil t) - (goto-char 6) - (should (string= "WikiWord" (hywiki-word-at))))) - - ;; Does not match as a WikiWord - (dolist (v '("WikiWord#")) - (with-temp-buffer - (org-mode) - (insert v) - (newline nil t) - (goto-char 6) - (should-not (string= "WikiWord" (hywiki-word-at))))) - - ;; Identifies as org link (Note: Not checked if target - ;; exists.) AND matches WikiWord - (dolist (v '("[[hy:WikiWord]]" "[[hy:WikiWord\\]]]")) - (with-temp-buffer - (org-mode) - (insert v) - (newline nil t) - (goto-char 6) - (font-lock-ensure) - (should (hsys-org-face-at-p 'org-link)) - (should (string= "WikiWord" (hywiki-word-at))))) - - ;; Identifies as org link (Note: Not checked if target - ;; exists.) AND DOES NOT match WikiWord - (dolist (v '("[[WikiWord AnotherWord]]")) - (with-temp-buffer - (org-mode) - (insert v) - (newline nil t) - (goto-char 6) - (font-lock-ensure) - (should (hsys-org-face-at-p 'org-link)) - (should-not (string= "WikiWord" (hywiki-word-at)))))) - (hy-delete-dir-and-buffer hywiki-directory))))) + ;; Matches a WikiWord + (dolist (v '("WikiWord" "[WikiWord]" "[[WikiWord]]" "{WikiWord}" "(WikiWord)" + "<WikiWord>" "<<WikiWord>>" "{[[WikiWord]]}" "([[WikiWord]])" + "[WikiWord AnotherWord WikiWord WikiWord]" + )) + (with-temp-buffer + (org-mode) + (insert v) + (newline nil t) + (goto-char 6) + (if (string= "WikiWord" (hywiki-word-at)) + (should t) + (should-not v)))) + + ;; Does not match as a WikiWord + (dolist (v '("WikiWord#")) + (with-temp-buffer + (org-mode) + (insert v) + (newline nil t) + (goto-char 6) + (if (string= "WikiWord" (hywiki-word-at)) + (should-not v) + (should t)))) + + ;; Identifies as org link (Note: Not checked if target + ;; exists.) AND matches WikiWord + (dolist (v '("[[hy:WikiWord]]" "[[hy:WikiWord\\]]]")) + (with-temp-buffer + (org-mode) + (insert v) + (newline nil t) + (goto-char 6) + (font-lock-ensure) + (should (hsys-org-face-at-p 'org-link)) + (if (string= "WikiWord" (hywiki-word-at)) + (should t) + (should-not v)))) + + ;; Identifies as org link (Note: Not checked if target + ;; exists.) AND DOES NOT match WikiWord + (dolist (v '("[[WikiWord AnotherWord]]")) + (with-temp-buffer + (org-mode) + (insert v) + (newline nil t) + (goto-char 6) + (font-lock-ensure) + (should (hsys-org-face-at-p 'org-link)) + (if (string= "WikiWord" (hywiki-word-at)) + (should-not v) + (should t))))) + (hywiki-mode 0) + (hy-delete-dir-and-buffer hywiki-directory))))) (ert-deftest hywiki-tests--at-wikiword-finds-word-and-section () "Verify `hywiki-word-at' finds WikiWord and section if available." @@ -287,7 +296,7 @@ line 2 (words '("WikiWord" "WikiWord:L1" "WikiWord:L1:C2" "WikiWord#section" "WikiWord#section:L1" "WikiWord#section:L1:C2" "WikiWord#section-subsection" "WikiWord#section-subsection:L1" "WikiWord#section-subsection:L1:C2" - ;; FIXME: Uncomment when implemented. + ;; !! FIXME: Uncomment when implemented. ;; ("(WikiWord#section with spaces)" . "WikiWord#section with spaces") ;; ("(WikiWord#section)" . "WikiWord#section") ))) @@ -321,7 +330,7 @@ line 2 (with-temp-buffer (insert "WikiWord#\"section-within-quotes\"") (goto-char 4) - (should (string= "WikiWord#\"section-within-quotes\"" (hywiki-word-at))))) + (should-not (string= "WikiWord#\"section-within-quotes\"" (hywiki-word-at))))) (hy-delete-dir-and-buffer hywiki-directory))))) (ert-deftest hywiki-tests--word-is-p () @@ -986,7 +995,8 @@ Note special meaning of `hywiki-allow-plurals-flag'." (let* ((hywiki-directory (make-temp-file "hywiki" t)) (wikiword (hy-make-random-wikiword))) (unwind-protect - (mocklet (((hypb:require-package 'org-roam) => t) + (mocklet ((cl-struct-org-roam-node-tags => nil) + ((hypb:require-package 'org-roam) => t) ((org-roam-node-read) => "node") (org-roam-node-title => "node-title")) (hywiki-add-org-roam-node wikiword) @@ -1064,6 +1074,7 @@ up the test." (should (hact 'kbd-key "C-u C-h hhck{ABC} RET")) (hy-test-helpers:consume-input-events))) +;; !! TODO: Have to make this work or remove it. ;; Expanded for easier debugging ;; (ert-deftest hywiki-tests--save-referent-keyseries-use--menu-expanded () ;; "Verify saving and loading a referent works when using Hyperbole's menu." @@ -1281,6 +1292,7 @@ up the test." (save-excursion (unwind-protect (progn + ;; (should (hact 'kbd-key "C-u C-h hhc MyWiki RET n (emacs) RET")) (should (hact 'kbd-key "C-u C-h hhcn (emacs) RET")) (hy-test-helpers:consume-input-events)) (kill-buffer "*info*"))))) @@ -1336,7 +1348,6 @@ up the test." (ert-deftest hywiki-tests--delete-parenthesised-char () "Verify removing a char between parentheses only removes the char. See gh#rswgnu/hyperbole/669." - :expected-result :failed (with-temp-buffer (insert "(a)") (goto-char 2) @@ -1430,8 +1441,8 @@ the function is called." (("HiHo#s " . t) (" n")) ;; With delimiters (("(HiHo#s" . "HiHo#s") (" " . "HiHo#s")) - (("(HiHo#s" . "HiHo#s") (")" . "HiHo#s)")) ; Delimiter part of WikiWord. See below too. - (("(HiHo#s" . "HiHo#s") ("-" . "HiHo#s-") ("n" . "HiHo#s-n") (")" . "HiHo#s-n)")) + (("(HiHo#s" . "HiHo#s") (")" . "HiHo#s")) ; Delimiter part of WikiWord. See below too. + (("(HiHo#s" . "HiHo#s") ("-" . "HiHo#s-") ("n" . "HiHo#s-n") (")" . "HiHo#s-n")) ;; Insert and delete between WikiWords (("HiHo" . t) (p3 . t) (" " . "Hi") (p4 . "Ho") (-1 . "HiHo")) (("Hiho" . t) (p3 . t) (" " . "Hi") (p4) (-1 . "Hiho")) @@ -1489,6 +1500,51 @@ Insert test in the middle of other text." (p1 . t) (p4) (-1 . "Hiho"))))) (hy-delete-dir-and-buffer hywiki-directory))))) +(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) + (hywiki-directory (make-temp-file "hywiki" t))) + (unwind-protect + (progn + (hywiki-mode 1) + + ;; Matches a WikiWord + (dolist (v '("WikiWord" "[WikiWord]" "[[WikiWord]]" "{WikiWord}" "(WikiWord)" + "<WikiWord>" "<<WikiWord>>" "{[[WikiWord]]}" "([[WikiWord]])" + "[WikiWord AnotherWord]" + )) + (with-temp-buffer + (emacs-lisp-mode) + (insert (format ";; %s" v)) + (hywiki-tests--command-execute #'newline 1 'interactive) + (goto-char 9) + (should (string= "WikiWord" (hywiki-word-at)))) + + (with-temp-buffer + (emacs-lisp-mode) + (insert (format "(setq var \"%s\")" v)) + (hywiki-tests--command-execute #'newline 1 'interactive) + (goto-char 16) + (should (string= "WikiWord" (hywiki-word-at))))) + + ;; Does not match as a WikiWord + (dolist (v '("WikiWord#")) + (with-temp-buffer + (emacs-lisp-mode) + (insert (format ";; %s" v)) + (hywiki-tests--command-execute #'newline 1 'interactive) + (goto-char 9) + (should-not (hywiki-word-at))) + + (with-temp-buffer + (emacs-lisp-mode) + (insert (format "(setq var \"%s\")" v)) + (hywiki-tests--command-execute #'newline 1 'interactive) + (goto-char 16) + (should-not (hywiki-word-at))))) + (hy-delete-dir-and-buffer hywiki-directory))))) + (provide 'hywiki-tests) ;; This file can't be byte-compiled without the `el-mock' package diff --git a/test/kcell-tests.el b/test/kcell-tests.el index 5d4ddeba58..9fed2b5f6c 100644 --- a/test/kcell-tests.el +++ b/test/kcell-tests.el @@ -7,14 +7,14 @@ ;; e-mail: ma...@gnu.org ;; ;; orig-date: 16-Feb-22 at 23:28:49 -;; last-mod: 23-Aug-24 at 13:27:11 by Bob Weiner +;; last-mod: 16-Mar-25 at 10:15:23 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; ;; Copyright (C) 2021-2024 Free Software Foundation, Inc. ;; Licensed under the GNU General Public License, version 3. ;; -;; This file is not part of Emacs. It requires Emacs 27.2 or above. +;; This file is not part of Emacs. It requires Emacs 28 or above. ;; This file is part of Hyperbole. ;; ;;; Commentary: