mirror of
https://github.com/doomemacs/doomemacs
synced 2025-08-01 12:17:25 -05:00
feat(eww): Add jump-to-heading in EWW
1. Add jump-to-heading functionality for EWW buffers 2. Bind within imenu (replaces +eww/jump-to-url-on-page; bound to `:localleader l`) The new functions are based on existing `eww--capture-url-on-page` and `+eww/jump-to-url-on-page`. However, I took a different approach and used alists to hide the position/coordinates from the `completing-read`. Also, unlike `+eww/jump-to-url-on-page`, we don't give the user an option of limiting the scope to a region or visible portion of the buffer. `+eww/jump-to-heading-on-page` always prompts based on the entire buffer. Examples: 1. `<h1>H1</h1>` - "H1" 2. `<h1>H1</h1><h2>H2</h2>` - "H1" - "H1/h2" 3. `<h1>H1</h1><h2>H2</h2><h3>H3</h3>` - "H1" - "H1/H2" - "H1/H2/H3" 4. `<h1>H1-1</h1><h2>H2</h2><h1>H1-2</h1>` - "H1-1" - "H1-1/H2" - "H1-2"  Gaps in the hierarchy (for example a `<h2>` followed by an `<h5>`) are not represented in the labels presented to the user. Take the Wikipedia entry for Emacs (above) as an example. The `<h2>` "Content" is the first heading on the page, there's no preceeding `<h1>`, so it's shown to the user as "Content" without any prefix. Examples: 1. `<h2>H2</h2>` - "H2" 2. `<h2>H2</h2><h4>H4</h4>` - "H2" - "H2/H4" 3. `<h2>H2</h2><h4>H4</h4><h5>H5</h5>` - "H2" - "H2/H4" - "H2/H4/H5" 4. `<h2>H2</h2><h1>H1</h1><h5>H5</h5>` - "H2" - "H1" - "H1/H5" - modules/emacs/eww/autoload.el - (eww--capture-url-on-page): Rename to `eww--capture-urls-on-page` - (eww--capture-headings-on-page): Add; based on existing `eww--capture-urls-on-page` - (+eww/jump-to-heading-on-page): Add; based on existing `+eww/jump-to-url-on-page` - modules/emacs/eww/config.el - (keybind) Bind `+eww/jump-to-heading-on-page` to `<:localleader.>`; based on existing org-mode jump-to-heading keybind (`consult-org-heading`)
This commit is contained in:
committed by
Henrik Lissner
parent
d60d639efe
commit
ed860b2b06
@ -4,17 +4,15 @@
|
||||
;; dotfiles. See https://protesilaos.com/codelog/2021-03-25-emacs-eww
|
||||
|
||||
;; Adapted from `prot-eww-jump-to-url-on-page'
|
||||
(defun eww--capture-url-on-page (&optional position)
|
||||
(defun eww--capture-urls-on-page (&optional position)
|
||||
"Capture all the links on the current web page.
|
||||
|
||||
Return a list of strings. Strings are in the form LABEL @ URL.
|
||||
Return a list of strings. Strings are in the form \"LABEL @ URL\".
|
||||
When optional argument POSITION is non-nil, include position info in the strings
|
||||
too, so strings take the form: LABEL @ URL ~ POSITION."
|
||||
too, so strings take the form: \"POSITION ~ LABEL @ URL\"."
|
||||
(let (links match)
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
;; NOTE 2021-07-25: The first clause in the `or' is meant to address a bug
|
||||
;; where if a URL is in `point-min' it does not get captured.
|
||||
(while (setq match (text-property-search-backward 'shr-url))
|
||||
(let* ((raw-url (prop-match-value match))
|
||||
(start-point-prop (prop-match-beginning match))
|
||||
@ -38,6 +36,48 @@ too, so strings take the form: LABEL @ URL ~ POSITION."
|
||||
links)))))
|
||||
links))
|
||||
|
||||
(defun eww--capture-headings-on-page ()
|
||||
"Return an alist in the form \"LABEL . POINT\" for the current buffer."
|
||||
(let ((heading-stack '())
|
||||
headings match)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (setq match (text-property-search-forward 'outline-level))
|
||||
(let* ((level (prop-match-value match))
|
||||
(start-point-prop (prop-match-beginning match))
|
||||
(end-point-prop (prop-match-end match))
|
||||
(text (replace-regexp-in-string
|
||||
"\n" " " ; NOTE 2021-07-25: newlines break completion
|
||||
(buffer-substring-no-properties
|
||||
start-point-prop end-point-prop))))
|
||||
(cond
|
||||
((= level (length heading-stack))
|
||||
(pop heading-stack)
|
||||
(push text heading-stack))
|
||||
((< level (length heading-stack))
|
||||
;; There's an upward gap between headings (for example: h5, then h2)
|
||||
(dotimes (_ (1+ (- (length heading-stack) level)))
|
||||
(pop heading-stack))
|
||||
(push text heading-stack))
|
||||
((> level (length heading-stack))
|
||||
;; There's a downward gap between headings (for example: h2, then h5)
|
||||
(dotimes (_ (1- (- level (length heading-stack))))
|
||||
(push nil heading-stack))
|
||||
(push text heading-stack)))
|
||||
(push (cons
|
||||
(concat
|
||||
(let ((preceeding-heading-stack (remove nil (cdr heading-stack))))
|
||||
(when preceeding-heading-stack
|
||||
(propertize
|
||||
(concat
|
||||
(string-join (reverse preceeding-heading-stack) "/")
|
||||
"/")
|
||||
'face 'shadow)))
|
||||
(car heading-stack))
|
||||
start-point-prop)
|
||||
headings))))
|
||||
headings))
|
||||
|
||||
;; Adapted from `prot-eww--rename-buffer'
|
||||
(defun +eww-page-title-or-url (&rest _)
|
||||
(let ((prop (if (string-empty-p (plist-get eww-data :title)) :url :title)))
|
||||
@ -75,12 +115,12 @@ consider whole buffer."
|
||||
(user-error "Not in an eww buffer!"))
|
||||
(let* ((links
|
||||
(if arg
|
||||
(eww--capture-url-on-page t)
|
||||
(eww--capture-urls-on-page t)
|
||||
(save-restriction
|
||||
(if (use-region-p)
|
||||
(narrow-to-region (region-beginning) (region-end))
|
||||
(narrow-to-region (window-start) (window-end)))
|
||||
(eww--capture-url-on-page t))))
|
||||
(eww--capture-urls-on-page t))))
|
||||
(prompt-scope (if arg
|
||||
(propertize "URL on the page" 'face 'warning)
|
||||
"visible URL"))
|
||||
@ -91,6 +131,18 @@ consider whole buffer."
|
||||
(goto-char point)
|
||||
(recenter)))
|
||||
|
||||
;; Adapted from `prot-eww-jump-to-url-on-page'
|
||||
;;;###autoload
|
||||
(defun +eww/jump-to-heading-on-page ()
|
||||
"Jump to heading position on the page (whole buffer) using completion."
|
||||
(interactive nil 'eww-mode)
|
||||
(unless (derived-mode-p 'eww-mode)
|
||||
(user-error "Not in an eww buffer!"))
|
||||
(let* ((headings (eww--capture-headings-on-page))
|
||||
(selection (completing-read "Jump to heading: " headings nil t)))
|
||||
(goto-char (alist-get selection headings nil nil #'string=))
|
||||
(recenter)))
|
||||
|
||||
;; Adapted from `prot-eww-open-in-other-window'
|
||||
;;;###autoload
|
||||
(defun +eww/open-in-other-window ()
|
||||
|
@ -6,7 +6,7 @@
|
||||
(map! :map eww-mode-map
|
||||
[remap text-scale-increase] #'+eww/increase-font-size
|
||||
[remap text-scale-decrease] #'+eww/decrease-font-size
|
||||
[remap imenu] #'+eww/jump-to-url-on-page
|
||||
[remap imenu] #'+eww/jump-to-heading-on-page
|
||||
[remap quit-window] #'+eww/quit
|
||||
:ni [C-return] #'+eww/open-in-other-window
|
||||
:n "yy" #'+eww/copy-current-url
|
||||
@ -16,6 +16,7 @@
|
||||
(:localleader
|
||||
:desc "external browser" "e" #'eww-browse-with-external-browser
|
||||
:desc "buffers" "b" #'eww-switch-to-buffer
|
||||
:desc "jump to link" "l" #'+eww/jump-to-url-on-page
|
||||
|
||||
(:prefix ("t" . "toggle")
|
||||
:desc "readable" "r" #'eww-readable
|
||||
|
Reference in New Issue
Block a user