# eww-unmangle.el -rw-r--r-- 3.7 KiB View raw
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
;;; eww-unmangle.el --- Make websites more redable in EWW  -*- lexical-binding: t; -*-

;; Copyright (C) 2021, 2022  Philip Kaludercic

;; Author: Philip Kaludercic <philipk@posteo.net>

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Manually fix broken and difficult to use sites in Emacs' EWW
;; browser.
;;
;; Add `eww-unmangle' to `eww-after-render-hook' like so
;;
;;   (add-hook 'eww-after-render-hook #'eww-unmangle)
;;
;; to your configuration to enable the unmangling.

;;; Code:

(eval-when-compile (require 'subr-x))
(require 'eww)

(defvar eww-ummangle-alist '()
  "An alist of unmanglers for different domains.
Each entry has the form (DOMAIN-RE . UNMANGLER), where DOMAIN-RE
is string containing a a regular expression that should match a
domain name, and UNMANGLER is a function that takes no arguments
and is called in the EWW buffer.")

(defun eww-unmangle ()
  "Unmangler for EWW, to be added to `eww-after-render-hook'."
  (save-excursion
    (when-let* ((url (plist-get eww-data :url))
		(dom (url-domain url))
		(unm (alist-get dom eww-ummangle-alist nil nil
				#'string-match-p)))
      (funcall unm))))


;;; 

(defun eww-unmangle--site (domain unmangler)
  "Register function UNMANGLER for a DOMAIN name.
See `eww-ummangle-alist' on what UNMANGLER has to look like."
  (declare (indent 1))
  (push (cons (regexp-quote domain) unmangler)
	eww-ummangle-alist))

(defmacro eww-unmangle--cond (&rest body)
  "Dispatch handler by path.
BODY consists of `cond'-like forms (REGEXP . BODY), where REGEXP
attempts to match the result of `url-path-and-query' (bound to
the variable `url').  When successful, the final element in BODY
is executed (should be a function)."
  (declare (indent 0))
  (let (conds)
    (while body
      (let ((case (pop body)))
	(push `((string-match ,(car case) url)
		,@(cdr case))
	      conds)))
    `(lambda ()
       (let ((url (url-path-and-query (plist-get eww-data :url))))
	 (ignore url)			;silence the byte compiler
	 (funcall (cond ,@(nreverse conds) (#'ignore)))))))

(defmacro eww-unmangle--bind (varlist &rest body)
  "Bind matches to variables in VARLIST in BODY.
Each element of VARLIST should be a symbol, that is bound to the
matching `match-string' in order of its occurrence (starting with
1)."
  (declare (indent 1))
  (let (bound-varlist)
    (dotimes (i (length varlist))
      (push (list (nth i varlist) `(match-string ,(1+ i) url))
	    bound-varlist))
    `(let ,(nreverse bound-varlist) ,@body)))

(defmacro eww-unmangle--dom (var &rest body)
  "Transform the document object model.
BODY should use the S-Expression representation of the DOM bound
to VAR and return a modified version, that is then converted back
to HTML."
  (declare (indent 1))
  `(let* ((,var (libxml-parse-html-region (point-min) (point-max)))
	  (new (progn ,@body)))
     (ignore ,var)
     (delete-region (point-min) (point-max))
     (dom-print new)))


;;; Unmanglers

(add-to-list
 'eww-ummangle-alist
 (cons "github\\.com"
       (eww-unmangle--cond
	("\\`/\\([[:alnum:]]+\\)/\\([[:alnum:]-.]+\\)"
	 (eww-unmangle--dom dom
			    )))))

(provide 'eww-unmangle)
;;; eww-unmangle.el ends here