diff options
| author | Stefan Monnier | 2025-05-18 15:44:35 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2025-05-18 15:44:35 -0400 |
| commit | 168b67b1eeb2e2b79d2e5f3712d8ebfda31fd753 (patch) | |
| tree | 5fcdbb6e1e3bbd3b48712a237983bbc58a3eac15 | |
| parent | 48c66f26c1454c449ac40407651d22dc5cc57424 (diff) | |
| download | emacs-168b67b1eeb2e2b79d2e5f3712d8ebfda31fd753.tar.gz emacs-168b67b1eeb2e2b79d2e5f3712d8ebfda31fd753.zip | |
eww.el: Use `track-changes`
* lisp/net/eww.el: Require `track-changes`.
(eww-display-document): Don't `inhibit-modification-hooks` any more.
Use `track-changes-register` *at the end* instead.
(eww-mode): Don't use `after-change-functions` any more.
(eww--track-changes): New function.
(eww--process-text-input): Rename from `eww-process-text-input`.
Try and be more careful: don't presume `point` is near
the modification. Check for a form both at BEG and at END.
Don't rely on `:start/:end` pointing to the right places.
Use `:length` instead and shrink the field back to its original length
when possible.
(eww-size-text-inputs): Set `:length` rather than `:start/:end`.
| -rw-r--r-- | lisp/net/eww.el | 77 |
1 files changed, 49 insertions, 28 deletions
diff --git a/lisp/net/eww.el b/lisp/net/eww.el index 736e42e94fa..ad2355db3c6 100644 --- a/lisp/net/eww.el +++ b/lisp/net/eww.el | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | (require 'url-file) | 35 | (require 'url-file) |
| 36 | (require 'vtable) | 36 | (require 'vtable) |
| 37 | (require 'xdg) | 37 | (require 'xdg) |
| 38 | (require 'track-changes) | ||
| 38 | (eval-when-compile (require 'subr-x)) | 39 | (eval-when-compile (require 'subr-x)) |
| 39 | 40 | ||
| 40 | (defgroup eww nil | 41 | (defgroup eww nil |
| @@ -812,7 +813,6 @@ This replaces the region with the preprocessed HTML." | |||
| 812 | (setq bidi-paragraph-direction nil) | 813 | (setq bidi-paragraph-direction nil) |
| 813 | (plist-put eww-data :dom document) | 814 | (plist-put eww-data :dom document) |
| 814 | (let ((inhibit-read-only t) | 815 | (let ((inhibit-read-only t) |
| 815 | (inhibit-modification-hooks t) | ||
| 816 | ;; Possibly set by the caller, e.g., `eww-render' which | 816 | ;; Possibly set by the caller, e.g., `eww-render' which |
| 817 | ;; preserves the old URL #target before chasing redirects. | 817 | ;; preserves the old URL #target before chasing redirects. |
| 818 | (shr-target-id (or shr-target-id | 818 | (shr-target-id (or shr-target-id |
| @@ -848,6 +848,10 @@ This replaces the region with the preprocessed HTML." | |||
| 848 | (while (and (not (eobp)) | 848 | (while (and (not (eobp)) |
| 849 | (get-text-property (point) 'eww-form)) | 849 | (get-text-property (point) 'eww-form)) |
| 850 | (forward-line 1))))) | 850 | (forward-line 1))))) |
| 851 | ;; We used to enable this in `eww-mode', but it cause tracking | ||
| 852 | ;; of changes while we insert the document, whereas we only care about | ||
| 853 | ;; changes performed afterwards. | ||
| 854 | (track-changes-register #'eww--track-changes :nobefore t) | ||
| 851 | (eww-size-text-inputs)))) | 855 | (eww-size-text-inputs)))) |
| 852 | 856 | ||
| 853 | (defun eww-display-html (charset url &optional document point buffer) | 857 | (defun eww-display-html (charset url &optional document point buffer) |
| @@ -1350,14 +1354,11 @@ within text input fields." | |||
| 1350 | ;; Autoload cookie needed by desktop.el. | 1354 | ;; Autoload cookie needed by desktop.el. |
| 1351 | ;;;###autoload | 1355 | ;;;###autoload |
| 1352 | (define-derived-mode eww-mode special-mode "eww" | 1356 | (define-derived-mode eww-mode special-mode "eww" |
| 1353 | "Mode for browsing the web. | 1357 | "Mode for browsing the web." |
| 1354 | |||
| 1355 | \\{eww-mode-map}" | ||
| 1356 | :interactive nil | 1358 | :interactive nil |
| 1357 | (setq-local eww-data (list :title "")) | 1359 | (setq-local eww-data (list :title "")) |
| 1358 | (setq-local browse-url-browser-function #'eww-browse-url) | 1360 | (setq-local browse-url-browser-function #'eww-browse-url) |
| 1359 | (add-hook 'after-change-functions #'eww-process-text-input nil t) | 1361 | (add-hook 'context-menu-functions #'eww-context-menu 5 t) |
| 1360 | (add-hook 'context-menu-functions 'eww-context-menu 5 t) | ||
| 1361 | (setq-local eww-history nil) | 1362 | (setq-local eww-history nil) |
| 1362 | (setq-local eww-history-position 0) | 1363 | (setq-local eww-history-position 0) |
| 1363 | (when (boundp 'tool-bar-map) | 1364 | (when (boundp 'tool-bar-map) |
| @@ -1485,7 +1486,6 @@ instead of `browse-url-new-window-flag'." | |||
| 1485 | 1486 | ||
| 1486 | (defun eww-restore-history (elem) | 1487 | (defun eww-restore-history (elem) |
| 1487 | (let ((inhibit-read-only t) | 1488 | (let ((inhibit-read-only t) |
| 1488 | (inhibit-modification-hooks t) | ||
| 1489 | (text (plist-get elem :text))) | 1489 | (text (plist-get elem :text))) |
| 1490 | (setq eww-data elem) | 1490 | (setq eww-data elem) |
| 1491 | (if (null text) | 1491 | (if (null text) |
| @@ -1756,16 +1756,34 @@ Interactively, EVENT is the value of `last-nonmenu-event'." | |||
| 1756 | "List of input types which represent a text input. | 1756 | "List of input types which represent a text input. |
| 1757 | See URL `https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input'.") | 1757 | See URL `https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input'.") |
| 1758 | 1758 | ||
| 1759 | (defun eww-process-text-input (beg end replace-length) | 1759 | (defun eww--track-changes (tracker-id) |
| 1760 | (when-let* ((pos (field-beginning (point)))) | 1760 | (track-changes-fetch |
| 1761 | (let* ((form (get-text-property pos 'eww-form)) | 1761 | tracker-id |
| 1762 | (properties (text-properties-at pos)) | 1762 | (lambda (beg end len) |
| 1763 | (eww--process-text-input beg end len) | ||
| 1764 | ;; Disregard our own changes. | ||
| 1765 | (track-changes-fetch tracker-id #'ignore)))) | ||
| 1766 | |||
| 1767 | (defun eww--process-text-input (beg end replace-length) | ||
| 1768 | (when-let* ((_ (integerp replace-length)) | ||
| 1769 | (pos end) | ||
| 1770 | (form (or (get-text-property pos 'eww-form) | ||
| 1771 | (progn | ||
| 1772 | (setq pos (max (point-min) (1- beg))) | ||
| 1773 | (get-text-property pos 'eww-form))))) | ||
| 1774 | (let* ((properties (text-properties-at pos)) | ||
| 1763 | (buffer-undo-list t) | 1775 | (buffer-undo-list t) |
| 1764 | (inhibit-read-only t) | 1776 | (inhibit-read-only t) |
| 1765 | (length (- end beg replace-length)) | 1777 | (length (- end beg replace-length)) |
| 1766 | (type (plist-get form :type))) | 1778 | (type (plist-get form :type))) |
| 1767 | (when (and form | 1779 | (when (member type eww-text-input-types) |
| 1768 | (member type eww-text-input-types)) | 1780 | ;; Make sure the new text has the right properties, which also |
| 1781 | ;; integrates the new text into the "current field". | ||
| 1782 | (set-text-properties beg end properties) | ||
| 1783 | ;; FIXME: This tries to preserve the "length" of the input field, | ||
| 1784 | ;; but we should try to preserve the *width* instead. | ||
| 1785 | ;; FIXME: Maybe instead of inserting/deleting spaces, we should | ||
| 1786 | ;; have a single stretch-space character at the end. | ||
| 1769 | (cond | 1787 | (cond |
| 1770 | ((> length 0) | 1788 | ((> length 0) |
| 1771 | ;; Delete some space at the end. | 1789 | ;; Delete some space at the end. |
| @@ -1781,18 +1799,21 @@ See URL `https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input'.") | |||
| 1781 | ((< length 0) | 1799 | ((< length 0) |
| 1782 | ;; Add padding. | 1800 | ;; Add padding. |
| 1783 | (save-excursion | 1801 | (save-excursion |
| 1784 | (goto-char end) | 1802 | (goto-char pos) |
| 1785 | (goto-char | 1803 | (let* ((field-length (- (eww-end-of-field) |
| 1786 | (if (equal type "textarea") | 1804 | (eww-beginning-of-field))) |
| 1787 | (1- (line-end-position)) | 1805 | (ideal-length (cdr (assq :length form)))) |
| 1788 | (1+ (eww-end-of-field)))) | 1806 | ;; FIXME: This test isn't right for multiline fields. |
| 1789 | (let ((start (point))) | 1807 | (when (or (null ideal-length) (> ideal-length field-length)) |
| 1790 | (insert (make-string (abs length) ? )) | 1808 | (goto-char |
| 1791 | (set-text-properties start (point) properties)) | 1809 | (if (equal type "textarea") |
| 1792 | (goto-char (1- end))))) | 1810 | (1- (line-end-position)) |
| 1793 | (set-text-properties (cdr (assq :start form)) | 1811 | (1+ (eww-end-of-field)))) |
| 1794 | (cdr (assq :end form)) | 1812 | (let ((start (point))) |
| 1795 | properties) | 1813 | (insert (make-string (min (abs length) |
| 1814 | (- ideal-length field-length)) | ||
| 1815 | ? )) | ||
| 1816 | (set-text-properties start (point) properties))))))) | ||
| 1796 | (let ((value (buffer-substring-no-properties | 1817 | (let ((value (buffer-substring-no-properties |
| 1797 | (eww-beginning-of-field) | 1818 | (eww-beginning-of-field) |
| 1798 | (eww-end-of-field)))) | 1819 | (eww-end-of-field)))) |
| @@ -2014,11 +2035,11 @@ Interactively, EVENT is the value of `last-nonmenu-event'." | |||
| 2014 | (< start (point-max))) | 2035 | (< start (point-max))) |
| 2015 | (when (or (get-text-property start 'eww-form) | 2036 | (when (or (get-text-property start 'eww-form) |
| 2016 | (setq start (next-single-property-change start 'eww-form))) | 2037 | (setq start (next-single-property-change start 'eww-form))) |
| 2017 | (let ((props (get-text-property start 'eww-form))) | 2038 | (let ((props (get-text-property start 'eww-form)) |
| 2018 | (nconc props (list (cons :start start))) | 2039 | (beg start)) |
| 2019 | (setq start (next-single-property-change | 2040 | (setq start (next-single-property-change |
| 2020 | start 'eww-form nil (point-max))) | 2041 | start 'eww-form nil (point-max))) |
| 2021 | (nconc props (list (cons :end start)))))))) | 2042 | (nconc props (list (cons :length (- start beg))))))))) |
| 2022 | 2043 | ||
| 2023 | (defun eww-input-value (input) | 2044 | (defun eww-input-value (input) |
| 2024 | (let ((type (plist-get input :type)) | 2045 | (let ((type (plist-get input :type)) |