branch: externals/hyperbole commit b4cc99649b56e29706940904362fca77c460a128 Merge: c3ff71d9ce 4bb81f423b Author: Robert Weiner <r...@gnu.org> Commit: GitHub <nore...@github.com>
Merge pull request #668 from rswgnu/rsw Fix yanking and deleting one paired delimiter properly; fix double-quoted string selection --- ChangeLog | 16 ++++++++++ hui-select.el | 101 ++++++++++++++++++++++++++++++++++------------------------ hywiki.el | 73 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 140 insertions(+), 50 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5c0ed49ae6..51e731c4f8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,22 @@ * hywiki.el (hywiki-make-referent-hasht): Return hasht when rebuilding from load data. +2025-02-07 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki--extend-yanked-region): Add and call in 'hywiki-highlight-on-yank' + to extend a yanked region to include the rest of a delimited pair or string. + +* hui-select.el (hui-select-string-p): Fix to make multi-line string selection + work properly whether on the opening or closing double quote mark, using + 'scan-sexps'. Update doc string. + +2025-02-06 Bob Weiner <r...@gnu.org> + +* hywiki.el (hywiki-debuttonize-non-character-commands, + hywiki-buttonize-non-character-commands): Add support for 'kill' + commands and fix that removing a trailing delimiter did not rehighlight + HyWikiWords properly. + 2025-02-05 Mats Lidell <ma...@gnu.org> * hywiki.el (hywiki-add-org-id): Suppress byte compile warnings for calling org-id-get with diff --git a/hui-select.el b/hui-select.el index a67d40a8aa..fd949b42f8 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: 26-Jan-25 at 17:04:55 by Bob Weiner +;; Last-Mod: 7-Feb-25 at 00:15:47 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -108,6 +108,7 @@ ;;; ************************************************************************ (require 'hvar) +(require 'hypb) ;; for hypb:in-string-p (eval-when-compile (require 'mhtml-mode) ;; for MHTML and HTML modes (require 'sgml-mode) ;; for SGML mode @@ -1036,49 +1037,65 @@ Return the updated cons cell." nil hui-select-region)) + (defun hui-select-string-p (&optional start-delim end-delim) - "Return (start . end) of string whose first line point is in or directly before. -Positions include delimiters. String is delimited by double quotes unless -optional START-DELIM and END-DELIM (strings) are given. -Returns nil if not within a string." - (let ((opoint (point)) - (count 0) - bol start delim-regexp start-regexp end-regexp) - (or start-delim (setq start-delim "\"")) - (or end-delim (setq end-delim "\"")) - ;; Special case for the empty string. - (if (looking-at (concat (regexp-quote start-delim) - (regexp-quote end-delim))) - (hui-select-set-region (point) (match-end 0)) - (setq start-regexp (concat "\\(^\\|[^\\]\\)\\(" - (regexp-quote start-delim) "\\)") - end-regexp (concat "[^\\]\\(" (regexp-quote end-delim) "\\)") - delim-regexp (concat start-regexp "\\|" end-regexp)) - (save-excursion - (beginning-of-line) - (setq bol (point)) - (while (re-search-forward delim-regexp opoint t) - (setq count (1+ count)) - ;; This is so we don't miss the closing delimiter of an empty - ;; string. - (if (and (= (point) (1+ bol)) - (looking-at (regexp-quote end-delim))) + "Return (start . end) of a string. +Works when on a delim or on the first line with point in the +string or directly before it. Positions include delimiters. +String is delimited by double quotes unless optional START-DELIM +and END-DELIM (strings) are given. Returns nil if not within a +string." + (unless start-delim (setq start-delim "\"")) + (unless end-delim (setq end-delim "\"")) + (or (and (equal start-delim "\"") (equal end-delim "\"") + (ignore-errors + (cond ((and (= (char-after) ?\") + (/= (char-before) ?\\)) + (if (hypb:in-string-p) + (hui-select-set-region (scan-sexps (1+ (point)) -1) + (1+ (point))) + (hui-select-set-region (point) (scan-sexps (point) 1)))) + ((and (= (char-before) ?\") + (/= (char-before (1- (point))) ?\\)) + (if (hypb:in-string-p) + (hui-select-set-region (1- (point)) (scan-sexps (1- (point)) 1)) + (hui-select-set-region (scan-sexps (1- (point)) -1) + (point))))))) + (let ((opoint (point)) + (count 0) + bol start delim-regexp start-regexp end-regexp) + ;; Special case for the empty string. + (if (looking-at (concat (regexp-quote start-delim) + (regexp-quote end-delim))) + (hui-select-set-region (point) (match-end 0)) + (setq start-regexp (concat "\\(^\\|[^\\]\\)\\(" + (regexp-quote start-delim) "\\)") + end-regexp (concat "[^\\]\\(" (regexp-quote end-delim) "\\)") + delim-regexp (concat start-regexp "\\|" end-regexp)) + (save-excursion + (beginning-of-line) + (setq bol (point)) + (while (re-search-forward delim-regexp opoint t) (setq count (1+ count)) - (unless (bobp) - (backward-char 1)))) - (goto-char opoint) - ;; If found an even # of starting and ending delimiters before - ;; opoint, then opoint is at the start of a string, where we want it. - (if (zerop (mod count 2)) - (unless (bobp) - (backward-char 1)) - (re-search-backward start-regexp nil t)) - ;; Point is now before the start of the string. - (when (re-search-forward start-regexp nil t) - (setq start (match-beginning 2)) - (when (re-search-forward end-regexp nil t) - (hui-select-set-region start (point)))))))) - + ;; This is so we don't miss the closing delimiter of an empty + ;; string. + (if (and (= (point) (1+ bol)) + (looking-at (regexp-quote end-delim))) + (setq count (1+ count)) + (unless (bobp) + (backward-char 1)))) + (goto-char opoint) + ;; If found an even # of starting and ending delimiters before + ;; opoint, then opoint is at the start of a string, where we want it. + (if (zerop (mod count 2)) + (unless (bobp) + (backward-char 1)) + (re-search-backward start-regexp nil t)) + ;; Point is now before the start of the string. + (when (re-search-forward start-regexp nil t) + (setq start (match-beginning 2)) + (when (re-search-forward end-regexp nil t) + (hui-select-set-region start (point))))))))) ;;; ;;; Code selections ;;; diff --git a/hywiki.el b/hywiki.el index 04190ac5fc..ddd716e0e2 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: 8-Feb-25 at 22:56:32 by Mats Lidell +;; Last-Mod: 9-Feb-25 at 10:10:14 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -564,16 +564,18 @@ deletion commands and those in `hywiki-non-character-commands'." (active-minibuffer-window))) (when (or (memq this-command hywiki-non-character-commands) (and (symbolp this-command) - (string-match-p "^\\(org-\\)?delete-\\|-delete-\\|insert\\(-\\|$\\)" (symbol-name this-command)))) + (string-match-p "^\\(org-\\)?\\(delete-\\|kill-\\)\\|\\(-delete\\|-kill\\|insert\\)\\(-\\|$\\)" (symbol-name this-command)))) (if (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) - (insert " ")) + (delete-char 1)) (goto-char hywiki--buttonize-end) (setq closing-char (char-before)) (when (memq closing-char '(?\) ?\")) @@ -582,7 +584,6 @@ deletion commands and those in `hywiki-non-character-commands'." (goto-char hywiki--buttonize-start) (hywiki-maybe-highlight-between-page-names) (when (memq opening-char '(?\( ?\")) - (delete-char 1) (insert opening-char)) (when (memq closing-char '(?\) ?\")) (goto-char (1+ hywiki--buttonize-end)) @@ -599,7 +600,7 @@ deletion commands and those in `hywiki-non-character-commands'." (set-marker hywiki--buttonize-end nil)) (when (or (memq this-command hywiki-non-character-commands) (and (symbolp this-command) - (string-match-p "\\`\\(org-\\)?delete-\\|-delete-" + (string-match-p "\\`\\(org-\\)?\\(delete-\\|kill-\\)\\|-delete-\\|-kill-" (symbol-name this-command)))) (cl-destructuring-bind (start end) (hywiki-get-delimited-range) ;; includes delimiters @@ -1543,10 +1544,16 @@ After successfully finding any kind of referent, run Have to add one character to the length of the yanked text so that any needed word-separator after the last character is included to induce highlighting any last HyWikiWord." - (hywiki-maybe-highlight-page-names start (min (1+ end) (point-max)))) + ;; When yank only part of a delimited pair, expand the range to + ;; include the whole delimited pair before re-highlighting + ;; HyWikiWords therein, so that the whole delimited expression is + ;; included. + (cl-destructuring-bind (start end) + (hywiki--extend-yanked-region start end) + (hywiki-maybe-highlight-page-names start (min (1+ end) (point-max))))) (defun hywiki-map-words (func) - "Apply FUNC across all HyWikiWords in the current buffer and return nil. + "Apply FUNC across highlighted HyWikiWords in the current buffer and return nil. FUNC takes 1 argument, the Emacs overlay spanning the start and end buffer positions of each HyWikiWord and its optional #section." (save-excursion @@ -2934,6 +2941,56 @@ invalid. Appended only if the referent-type supports suffixes." (cons referent-type referent-value)) referent)))))) +(defun hywiki--extend-yanked-region (start end) + "Return a list of (START END) with the specified range extended to include any delimited regions. +Typically used to extend a yanked region to fully include any strings or balanced pair delimiters." + (let ((delim-distance 0) + (result (list start end)) + opoint) + + ;; Skip past all delimited ranges and extend `end' as needed + (save-excursion + (goto-char start) + (while (and (<= (point) end) + (not (zerop (setq delim-distance (skip-syntax-forward "^\(" end))))) + (condition-case nil + (progn (goto-char (+ (point) delim-distance)) + (setq opoint (point)) + (setq end (max end (goto-char (scan-sexps (point) 1))) + result (list start end))) + (error (goto-char (min (1+ opoint) end)))))) + + ;; Skip past all double-quoted ranges and extend `start' and `end' as needed + (save-excursion + (goto-char start) + (while (and (<= (point) end) + (not (zerop (setq delim-distance (skip-syntax-forward "^\"" end))))) + (condition-case nil + (progn (goto-char (+ (point) delim-distance)) + (setq opoint (point)) + (if (hypb:in-string-p) + (progn (goto-char (1+ (point))) + (setq start (min start (goto-char (scan-sexps (1+ (point)) -1)))) + (goto-char (min (1+ opoint) end))) + ;; before a string + (setq end (max end (goto-char (scan-sexps (point) 1))))) + (setq result (list start end))) + (error (goto-char (min (1+ opoint) end)))))) + + ;; Skip past closing delimiter and extend `start' if needed + (save-excursion + (goto-char start) + (while (and (<= (point) end) + (not (zerop (setq delim-distance (skip-syntax-forward "^\)" end))))) + (condition-case nil + (progn (goto-char (+ (point) delim-distance)) + (setq opoint (point)) + (setq start (min start (goto-char (scan-sexps (1+ (point)) -1))) + result (list start end)) + (goto-char (min (1+ opoint) end))) + (error (goto-char (min (1+ opoint) end)))))) + result)) + (defun hywiki--get-delimited-range-backward () "Return a list of (start end) if not between/after end ]] or >>. Otherwise, return nil."