From f27a85ed354fa146a218fc2d26604317c9cc4723 Mon Sep 17 00:00:00 2001 From: Ag Ibragimov Date: Wed, 11 Sep 2024 17:18:01 -0400 Subject: [PATCH] module: add :emacs eww Close: #6866 Co-authored-by: hlissner --- modules/emacs/eww/README.org | 55 +++++++++++++++ modules/emacs/eww/autoload.el | 128 ++++++++++++++++++++++++++++++++++ modules/emacs/eww/config.el | 48 +++++++++++++ modules/emacs/eww/packages.el | 4 ++ templates/init.example.el | 3 +- 5 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 modules/emacs/eww/README.org create mode 100644 modules/emacs/eww/autoload.el create mode 100644 modules/emacs/eww/config.el create mode 100644 modules/emacs/eww/packages.el diff --git a/modules/emacs/eww/README.org b/modules/emacs/eww/README.org new file mode 100644 index 000000000..c09b07927 --- /dev/null +++ b/modules/emacs/eww/README.org @@ -0,0 +1,55 @@ +:PROPERTIES: +:ID: 4f6e0ee2-7837-4d15-853f-c8863d065f21 +:END: +#+title: :emacs eww +#+subtitle: The internet is gross +#+created: September 11, 2024 +#+since: 24.10 + +* Description :unfold: +This module augments eww (Emacs Web Wowser); Emacs' built-in web browser, with +some reasonable defaults and helper commands. + +** Maintainers +/This module has no dedicated maintainers./ [[doom-contrib-maintainer:][Become a maintainer?]] + +** Module flags +/This module has no flags./ + +** Packages +/This module doesn't install any packages./ + +** Hacks +- The buffer is renamed to match the current page's URL or title. + +** TODO Changelog +# This section will be machine generated. Don't edit it by hand. +/This module does not have a changelog yet./ + +* Installation +[[id:01cffea4-3329-45e2-a892-95a384ab2338][Enable this module in your ~doom!~ block.]] + +/This module has no external requirements./ + +* Usage +#+begin_quote +󱌣 /This module's usage documentation is incomplete./ [[doom-contrib-module:][Complete it?]] +#+end_quote + +Type ~M-x eww~ and enter an URL. + +* TODO Configuration +#+begin_quote +󱌣 This module has no configuration documentation yet. [[doom-contrib-module:][Write some?]] +#+end_quote + +* Troubleshooting +/There are no known problems with this module./ [[doom-report:][Report one?]] + +* Frequently asked questions +/This module has no FAQs yet./ [[doom-suggest-faq:][Ask one?]] + +* TODO Appendix +#+begin_quote +󱌣 This module has no appendix yet. [[doom-contrib-module:][Write one?]] +#+end_quote diff --git a/modules/emacs/eww/autoload.el b/modules/emacs/eww/autoload.el new file mode 100644 index 000000000..b83c551a0 --- /dev/null +++ b/modules/emacs/eww/autoload.el @@ -0,0 +1,128 @@ +;;; emacs/eww/autoload.el -*- lexical-binding: t; -*- + +;; NOTE: Many of these functions were adapted from Protesilaos Stavrou's +;; 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) + "Capture all the links on the current web page. + +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." + (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)) + (end-point-prop (prop-match-end match)) + (url (when (stringp raw-url) + (propertize raw-url 'face 'link))) + (label (replace-regexp-in-string + "\n" " " ; NOTE 2021-07-25: newlines break completion + (buffer-substring-no-properties + start-point-prop end-point-prop))) + (point start-point-prop) + (line (line-number-at-pos point t)) + (column (save-excursion (goto-char point) (current-column))) + (coordinates (propertize + (format "%d,%d (%d)" line column point) + 'face 'shadow))) + (when url + (push (if position + (format "%-15s ~ %s @ %s" coordinates label url) + (format "%s @ %s" label url)) + links))))) + links)) + +;; 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))) + (format "*%s # eww*" (plist-get eww-data prop)))) + + +;; +;;; Commands + +;; Adapted from `prot-eww-quit' +;;;###autoload +(defun +eww/quit () + "Quit eww and kill all its buffers." + (interactive nil 'eww-mode) + (when (yes-or-no-p "Are you sure you want to quit eww?") + (save-match-data + (cl-loop with case-fold-search = t + for buf in (doom-buffer-list) + if (with-current-buffer buf + (or (eq major-mode 'eww-mode) + (and (derived-mode-p 'special-mode) + (string-match "\\*.*eww.*\\*" (buffer-name))))) + do (kill-buffer buf))))) + +;; Adapted from `prot-eww-jump-to-url-on-page' +;;;###autoload +(defun +eww/jump-to-url-on-page (&optional arg) + "Jump to URL position on the page using completion. + +When called without ARG (\\[universal-argument]) get URLs only +from the visible portion of the buffer. But when ARG is provided +consider whole buffer." + (interactive "P" 'eww-mode) + (unless (derived-mode-p 'eww-mode) + (user-error "Not in an eww buffer!")) + (let* ((links + (if arg + (eww--capture-url-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)))) + (prompt-scope (if arg + (propertize "URL on the page" 'face 'warning) + "visible URL")) + (prompt (format "Jump to %s: " prompt-scope)) + (selection (completing-read prompt links nil t)) + (position (replace-regexp-in-string "^.*(\\([0-9]+\\))[\s\t]+~" "\\1" selection)) + (point (string-to-number position))) + (goto-char point) + (recenter))) + +;; Adapted from `prot-eww-open-in-other-window' +;;;###autoload +(defun +eww/open-in-other-window () + "Use `eww-open-in-new-buffer' in another window." + (interactive nil 'ewe-mode) + (other-window-prefix) + (eww-open-in-new-buffer)) + +;;;###autoload +(defun +eww/copy-current-url () + "Copy the open page's URL to the kill ring." + (interactive nil 'eww-mode) + (let ((url (eww-current-url))) + (kill-new url) + (message url))) + +;;;###autoload +(defun +eww/increase-font-size () + "Increase the font size in `eww-mode'." + (interactive nil 'eww-mode) + (if shr-use-fonts + (let* ((cur (face-attribute 'shr-text :height nil)) + (cur (if (floatp cur) cur 1.0))) + (set-face-attribute 'shr-text nil :height (+ cur 0.1))) + (text-scale-increase 0.5))) + +;;;###autoload +(defun +eww/decrease-font-size () + "Decrease the font size in `eww-mode'." + (interactive nil 'eww-mode) + (if shr-use-fonts + (let* ((cur (face-attribute 'shr-text :height nil)) + (cur (if (floatp cur) cur 1.0))) + (set-face-attribute 'shr-text nil :height (- cur 0.1))) + (text-scale-decrease 0.5))) diff --git a/modules/emacs/eww/config.el b/modules/emacs/eww/config.el new file mode 100644 index 000000000..37c168035 --- /dev/null +++ b/modules/emacs/eww/config.el @@ -0,0 +1,48 @@ +;;; emacs/eww/config.el -*- lexical-binding: t; -*- + +(use-package! eww + :defer t + :config + (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 quit-window] #'+eww/quit + :ni [C-return] #'+eww/open-in-other-window + :n "yy" #'+eww/copy-current-url + :n "zk" #'text-scale-increase + :n "zj" #'text-scale-decrease + + (:localleader + :desc "external browser" "e" #'eww-browse-with-external-browser + :desc "buffers" "b" #'eww-switch-to-buffer + + (:prefix ("t" . "toggle") + :desc "readable" "r" #'eww-readable + :desc "colors" "c" #'eww-toggle-colors + :desc "fonts" "f" #'eww-toggle-fonts + :desc "images" "i" #'eww-toggle-images) + + (:prefix ("y" . "copy") + :desc "copy url" "y" #'+eww/copy-current-url + :desc "copy for Org" "o" #'org-eww-copy-for-org-mode))) + + ;; HACK: There are packages that use eww to pop up html documentation; we want + ;; those to open in a popup, but if the user calls `eww' directly, it should + ;; open in the current window. + (defadvice! +eww-open-in-fullscreen-if-interactive-a (fn &rest args) + :around #'eww + (if (called-interactively-p 'any) + (apply fn args) + (let (display-buffer-alist) + (apply fn args)))) + + ;; HACK: Rename the eww buffer to match the open page's title or URL. + (if (boundp 'eww-auto-rename-buffer) + (setq eww-auto-rename-buffer #'+eww-page-title-or-url) ; for >=29.1 + ;; REVIEW: Remove when we drop 28 support + (add-hook! 'eww-after-render-hook + (defun +eww--rename-buffer-to-page-title-or-url-h (&rest _) + (rename-buffer (+eww-page-title-or-url)))) + (advice-add #'eww-back-url :after #'+eww--rename-buffer-to-page-title-or-url-h) + (advice-add #'eww-forward-url :after #'+eww--rename-buffer-to-page-title-or-url-h))) diff --git a/modules/emacs/eww/packages.el b/modules/emacs/eww/packages.el new file mode 100644 index 000000000..fd7bf1310 --- /dev/null +++ b/modules/emacs/eww/packages.el @@ -0,0 +1,4 @@ +;; -*- no-byte-compile: t; -*- +;;; emacs/eww/packages.el + +(package! eww :built-in t) diff --git a/templates/init.example.el b/templates/init.example.el index 40a5d4658..187cd99be 100644 --- a/templates/init.example.el +++ b/templates/init.example.el @@ -69,7 +69,8 @@ :emacs dired ; making dired pretty [functional] electric ; smarter, keyword-based electric-indent - ;;ibuffer ; interactive buffer management + ;;eww ; the internet is gross + ;;ibuffer ; interactive buffer management undo ; persistent, smarter undo for your inevitable mistakes vc ; version-control and Emacs, sitting in a tree