unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
From: Drew Adams <drew.adams@oracle.com>
To: Michael Heerdegen <michael_heerdegen@web.de>,
	Stefan Monnier <monnier@iro.umontreal.ca>
Cc: help-gnu-emacs@gnu.org
Subject: RE: Bookmarks in EWW
Date: Wed, 25 Mar 2020 14:48:34 -0700 (PDT)	[thread overview]
Message-ID: <6d3b94ad-c804-47aa-acb0-3ec64159ee50@default> (raw)
In-Reply-To: <87tv2d3zii.fsf@web.de>

[-- Attachment #1: Type: text/plain, Size: 1277 bytes --]

> Ok, I've thrown something together (draft!) and tested quickly:

Attached is something I threw together, by removing some
of the Bookmark+ features for EWW and adapting the code
to what vanilla bookmark.el offers.  Completely untested;
as is.  Perhaps you can make some use of it.

Features:

* An auto-bookmarking mode, for those who might want to
  automatically update an existing bookmark, or create
  a new bookmark, whenever they visit a URL with EWW.

* A user option for whether that mode can create new
  bookmarks or just update existing ones.

* A toggle command for that user option.

* A command to set (create or update) a bookmark for
  the URL currently visited by EWW.

* An option for whether and how an EWW buffer gets
  renamed (the default is to use vanilla EWW name,
  `*eww*').  Note: this acts beyond just bookmarking.

* An option for whether to generate a new buffer when
  you jump to an EWW bookmark (the default is to reuse
  an existing buffer for that URL).

* An option for whether to replace corresponding EWW
  "bookmarking" commands with standard bookmarking
  commands, in EWW key bindings (`eww-mode-map').

* A command to create regular Emacs bookmarks from a
  file of existing EWW "bookmarks".

HTH.

[-- Attachment #2: bmk-eww.el --]
[-- Type: application/octet-stream, Size: 12361 bytes --]

;; Quiet the byte-compiler.
(defvar bookmark-alist)                 ; In `bookmark.el'
(defvar bookmark-default-file)          ; In `bookmark.el'
(defvar bookmark-make-record-function)  ; In `bookmark.el'
(defvar eww-data)                       ; In `eww.el'
(defvar eww-local-regex)                ; In `eww.el'
(defvar eww-mode-map)                   ; In `eww.el'
(defvar eww-search-prefix)              ; In `eww.el'

(require 'bookmark)
(require 'cl-seq)                       ; `cl-remove-if-not'

(defcustom bmkp-eww-auto-type 'update-only
  "How `bmkp-eww-auto-bookmark-mode' behaves when enabled.
You can toggle this option using `\\[bmkp-toggle-eww-auto-type]'."
  :type '(choice
          (const :tag "Create EWW bookmark or update existing EWW bookmark"
                 create-or-update)
          (const :tag "Update existing EWW bookmark (only)"
                 update-only))
  :group 'bookmark)

(defcustom bmkp-eww-buffer-renaming nil
  "Whether and how an EWW buffer is renamed.
Non-nil values affect EWW behavior even when bookmarks are not used.

* nil:    Do not rename buffer - use `*eww*' (vanilla EWW behavior).
* `url':  Rename buffer to web-page title plus last 20 chars of URL.
* `page': Rename buffer to web-page title (only)."
  :type '(choice
          (const :tag "Do not rename buffer (use name `*eww*')"
                 nil)
          (const :tag "Rename buffer to web-page title plus last 20 chars of URL"
                 url)
          ;; Any symbol other than `page' and nil is treated the same as `page'.
          (const :tag "Rename buffer to web-page title"
                 page))
  :group 'bookmark)

(defcustom bmkp-eww-generate-buffer-flag nil
  "Whether to generate a new buffer when jumping to an EWW bookmark.
* nil means reuse an existing buffer for the bookmarked URL.
* Non-nil means use a new buffer."
  :type 'boolean :group 'bookmark)

(defcustom bmkp-eww-replace-keys-flag t
  "Non-nil means replace EWW bookmark keys with Emacs bookmark ones.
If you change the value of this option then you must restart Emacs for
it to take effect."
  :type 'boolean :group 'bookmark)

;; This is set by `bmkp-eww-rename-buffer',
;; and used in `bmkp-jump-eww' for local var `after-render-function'.
(defvar bmkp-eww-new-buf-name nil
  "If non-nil, the name of the EWW buffer to jump to.")
  (make-variable-buffer-local 'bmkp-eww-new-buf-name)

;; This is set by `bmkp-jump-eww',
;; and used in `bmkp-eww-rename-buffer'.
(defvar bmkp-eww-jumping-p nil
  "Non-nil only if we are currently jumping to an EWW bookmark.")
(make-variable-buffer-local 'bmkp-eww-jumping-p)

(defun bmkp-eww-bookmark-p (bookmark)
  "Return non-nil if BOOKMARK is an EWW bookmark.
BOOKMARK is a bookmark name or a bookmark record.
If it is a record then it need not belong to `bookmark-alist'."
  (eq (bookmark-get-handler bookmark) 'bmkp-jump-eww))

(defun bmkp-eww-alist-only ()
  "`bookmark-alist', filtered to retain only EWW bookmarks.
A new list is returned (no side effects)."
  (bookmark-maybe-load-default-file)
  (cl-remove-if-not #'bmkp-eww-bookmark-p bookmark-alist))

(defun bmkp-eww-title ()
  "Return the web-page title of the current `eww-mode' buffer."
  (plist-get eww-data :title))

(defun bmkp-eww-url ()
  "Return the URL of the current `eww-mode' buffer."
  (eww-current-url))

(defun bmkp-make-eww-record ()
  "Make a record for an EWW buffer."
  `(,(bmkp-eww-title)
    (buffer-name . ,(bmkp-eww-new-buffer-name))
    ,@(bookmark-make-record-default 'NO-FILE)
    (location . ,(bmkp-eww-url))
    (handler . bmkp-jump-eww)))

(defun bmkp-eww-new-buffer-name ()
  "Return a new buffer name for the current `eww-mode' buffer.
The name format is determined by option `bmkp-eww-buffer-renaming'."
  (concat 
   "*eww*" 
   (and bmkp-eww-buffer-renaming
        (concat 
         "-"
         (bmkp-eww-title)
         (let ((url  (bmkp-eww-url)))
           (and (eq 'url bmkp-eww-buffer-renaming)
                (concat " " (if (>= (length url) 20) (substring url -20) url))))))))

(defun bmkp-jump-eww (bookmark)
  "Handler function for record returned by `bmkp-make-eww-record'.
BOOKMARK is a bookmark name or a bookmark record."
  (require 'eww)
  (let ((buffer
         (or (and (not bmkp-eww-generate-buffer-flag)
                  (get-buffer (bookmark-prop-get bookmark 'buffer-name)))
             (generate-new-buffer "*eww*"))) ; Might get renamed later.
        ;; `bmkp-eww-new-buf-name' is free here, bound in `bmkp-eww-rename-buffer'.
        (after-render-function
         `(lambda ()
            (when (and bmkp-eww-buffer-renaming  bmkp-eww-new-buf-name)
              (kill-buffer)
              (set-buffer bmkp-eww-new-buf-name))
            (bookmark-default-handler
             `("" (buffer . ,(current-buffer))
               . ,(bookmark-get-bookmark-record ,(car bookmark))))
            (dolist (var  '(bmkp-eww-jumping-p eww-after-render-hook))
              (kill-local-variable var)))))
    (setq bmkp-eww-jumping-p  t)
    (with-current-buffer buffer
      (eww-mode)
      (save-window-excursion ; Because `eww' calls `pop-to-buffer-same-window'.
        (eww (bookmark-location bookmark)))
      (add-hook 'eww-after-render-hook after-render-function 'APPEND 'LOCAL))))

(defun bmkp-eww-rename-buffer (&rest _ignored)
  "Rename current buffer according to option `bmkp-eww-buffer-renaming'.
When jumping to EWW bookmark with nil `bmkp-eww-generate-buffer-flag',
if the buffer has already been visited, set `bmkp-eww-new-buf-name' so
that `bmkp-jump-eww' will use the already visited buffer."
  (when bmkp-eww-buffer-renaming
    (let ((new-bname  (bmkp-eww-new-buffer-name)))
      (condition-case nil
          (rename-buffer new-bname)
        ;; `bmkp-eww-jumping-p' is free here, set in `bmkp-jump-eww'.
        (error (if (and bmkp-eww-jumping-p  (not bmkp-eww-generate-buffer-flag))
                   ;; Save buf name for `after-render-function' in `bmkp-jump-eww'.
                   (setq bmkp-eww-new-buf-name  new-bname)
                 (rename-buffer (generate-new-buffer-name new-bname))))))))

(define-minor-mode bmkp-eww-auto-bookmark-mode
  "Toggle automatically setting a bookmark when you visit a URL with EWW.
The bookmark name is the title of the web page.

If option `bmkp-eww-auto-type' is `create-or-update' then such a
bookmark is created for the URL if none exists.  If the option value
is `update-only' then no new bookmark is created automatically, but an
existing bookmark is updated.  (Updating a bookmark increments the
recorded number of visits.)  You can toggle the option using
`\\[bmkp-toggle-eww-auto-type]'."
  :init-value nil :global t :group 'bookmark
  (cond (bmkp-eww-auto-bookmark-mode
         (add-hook   'eww-after-render-hook      'bmkp-set-eww-bookmark-here)
         (advice-add 'eww-restore-history :after 'bmkp-set-eww-bookmark-here))
        (t
         (remove-hook   'eww-after-render-hook   'bmkp-set-eww-bookmark-here)
         (advice-remove 'eww-restore-history     'bmkp-set-eww-bookmark-here)))
  (when (called-interactively-p 'any)
    (message "Automatic EWW bookmarking is now %s"
             (if bmkp-eww-auto-bookmark-mode
                 (if (eq bmkp-eww-auto-type 'update-only)
                     "ON, updating only"
                   "ON, creating or UPDATING")
               "OFF"))))

(defun bmkp-set-eww-bookmark-here (&optional nomsg)
  "Set an EWW bookmark for the URL of the current EWW buffer.
The current buffer is assumed to be in `eww-mode' and visiting a URL."
  (interactive)
  (let* ((bname   (bmkp-eww-title))
         (url     (bmkp-eww-url))
         (bmk     (bookmark-get-bookmark bname 'NO-ERROR))
         (visits  (and bmk  (bookmark-prop-get bmk 'visits))))
    (when bname
      (cond ((and (not visits)  (eq bmkp-eww-auto-type 'create-or-update))
             (bookmark-set bname)
             (unless nomsg (message "Created EWW bookmark `%s'" bname)))
            (visits
             (bookmark-prop-set bmk 'visits (1+ visits))
             (bookmark-prop-set bmk 'time   (current-time))
             (bookmark-bmenu-surreptitiously-rebuild-list)
             (unless nomsg (message "Updated EWW bookmark `%s'" bname)))))))

(defun bmkp-toggle-eww-auto-type (&optional msgp)
  "Toggle the value of option `bmkp-eww-auto-type'."
  (interactive "p")
  (setq bmkp-eww-auto-type  (if (eq bmkp-eww-auto-type 'create-or-update)
                                'update-only
                              'create-or-update))
  (when msgp (message "`bmkp-eww-auto-bookmark-mode' now %s"
                      (if (eq bmkp-eww-auto-type 'create-or-update)
                          "CREATES, as well as updates, EWW bookmarks"
                        "only UPDATES EXISTING EWW bookmarks"))))


;; You can use this to convert existing EWW bookmarks to Emacs bookmarks.
(defun bmkp-convert-eww-bookmarks (eww-file bmk-file &optional msgp)
  "Add bookmarks from EWW-FILE to BMK-FILE.
EWW-FILE is a file of \"bookmarks\" created by EWW, which are not
 compatible with Emacs bookmarks.  EWW-FILE is not modified.
BMK-FILE is an Emacs bookmarks file.

If BMK-FILE exists then it is updated to include the converted
bookmarks.  If it does not exist then it is created."
  (interactive
   (let ((default  bookmark-default-file))
     (list
      (expand-file-name
       (read-file-name "EWW \"bookmarks\" file: " nil
                       (expand-file-name "eww-bookmarks" user-emacs-directory)
                       t))
      (expand-file-name
       (read-file-name "Emacs bookmark file: "
                       (or (file-name-directory default)  "~/")
                       bookmark-default-file))
      t)))
  (when (file-directory-p eww-file)
    (error "`%s' is a directory, not a file" eww-file))
  (unless (file-readable-p eww-file)
    (error "Cannot read bookmark file `%s'" eww-file))
  (when (file-directory-p bmk-file)
    (error "`%s' is a directory, not a file" bmk-file))
  (unless (file-readable-p bmk-file)    ; Create empty bookmark file.
    (setq bmk-file  (expand-file-name bmk-file))
    (bookmark-maybe-load-default-file)
    (let ((bookmark-alist  ())) (bookmark-write-file bmk-file)))
  (with-temp-buffer
    (insert-file-contents eww-file)
    (let ((bookmark-alist  ())
          url title created bmk)
      (dolist (ebmk  (read (current-buffer)))
        (setq url      (plist-get ebmk :url)
              title    (plist-get ebmk :title)
              created  (date-to-time (plist-get ebmk :time))
              bmk      `(,title
                         ,@(bookmark-make-record-default 'NO-FILE)
                         (location . ,url)
                         (handler . bmkp-jump-eww)))
        (let ((buf-cell  (assq 'buffer-name bmk))) (setcdr buf-cell "*eww*"))
        (let ((created-cell  (assq 'created bmk))) (setcdr created-cell created))
        (push bmk bookmark-alist))
      (bookmark-write-file bmk-file)))
  (when msgp (message "Wrote Bookmark file `%s'" bmk-file)))

(defun bmkp-eww-jump (bookmark-name)
  "Jump to an EWW bookmark."
  (interactive (let ((bookmark-alist  (bmkp-eww-alist-only)))
                 (list (bookmark-completing-read "Jump to EWW bookmark: "))))
  (bookmark--jump-via bookmark-name 'pop-to-buffer-same-window))

(defun bmkp-eww-jump-other-window (bookmark-name)
  "Jump to an EWW bookmark in another window."
  (interactive (let ((bookmark-alist  (bmkp-eww-alist-only)))
                 (list (bookmark-completing-read "Jump to EWW bookmark: "))))
  (bookmark--jump-via bookmark-name (lambda (buf) (pop-to-buffer buf t))))

;; Non-nil `bmkp-eww-buffer-renaming' means EWW buffer is renamed on each visit.
(eval-after-load "eww"
  '(progn
     (add-hook   'eww-after-render-hook      'bmkp-eww-rename-buffer)
     (advice-add 'eww-restore-history :after 'bmkp-eww-rename-buffer)
     (add-hook   'eww-mode-hook (lambda ()
                                  (setq-local bookmark-make-record-function
                                              'bmkp-make-eww-record)
                                  (unless (lookup-key eww-mode-map "j")
                                    (define-key eww-mode-map "j" 'bmkp-eww-jump))))
     (when bmkp-eww-replace-keys-flag
       (define-key eww-mode-map [remap eww-add-bookmark]   'bookmark-set)
       (define-key eww-mode-map [remap eww-list-bookmarks] 'bookmark-bmenu-list))))

  parent reply	other threads:[~2020-03-25 21:48 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-23 11:26 Bookmarks in EWW Marcin Borkowski
2020-03-23 14:56 ` Drew Adams
2020-03-25 11:48   ` Marcin Borkowski
2020-03-23 20:41 ` Michael Heerdegen
2020-03-24 14:46   ` Stefan Monnier
2020-03-25  3:05     ` Michael Heerdegen
2020-03-25  3:45       ` `declare-function' docu (was: Re: Bookmarks in EWW) Emanuel Berg via Users list for the GNU Emacs text editor
2020-03-28 17:59         ` `declare-function' docu Bruno Félix Rezende Ribeiro
2020-03-28 18:38           ` Drew Adams
2020-04-09 12:30             ` Bruno Félix Rezende Ribeiro
2020-04-09 15:47               ` Drew Adams
2020-03-28 21:38           ` Emanuel Berg via Users list for the GNU Emacs text editor
2020-03-25 14:06       ` Bookmarks in EWW Stefan Monnier
2020-03-26  1:09         ` Michael Heerdegen
2020-03-26  4:07           ` Stefan Monnier
2020-03-27  2:07             ` Michael Heerdegen
2020-03-27  3:44               ` Stefan Monnier
2020-03-28  2:31                 ` Michael Heerdegen
2020-03-28  2:55                   ` Stefan Monnier
2020-04-19  3:42                     ` Michael Heerdegen
2020-04-19 13:18                       ` Stefan Monnier
2020-04-20  3:17                         ` Michael Heerdegen
2020-04-20 13:24                           ` Stefan Monnier
2020-04-21  0:59                             ` Michael Heerdegen
2020-04-29 22:53                             ` Michael Heerdegen
2020-04-30  1:25                               ` Stefan Monnier
2020-04-30  2:08                                 ` Michael Heerdegen
2020-04-30  3:08                                   ` Stefan Monnier
2020-04-30 20:09                                     ` Michael Heerdegen
2020-04-27  2:43                         ` Michael Heerdegen
2020-03-27  4:32         ` buffer-localness (was: Re: Bookmarks in EWW) Emanuel Berg via Users list for the GNU Emacs text editor
2020-03-25 21:48       ` Drew Adams [this message]
2020-03-26  2:29         ` Bookmarks in EWW Michael Heerdegen
2020-03-26  3:21           ` Drew Adams
2020-03-26  3:53             ` Michael Heerdegen
2020-03-26 14:02               ` Drew Adams
2020-03-26 22:33                 ` Michael Heerdegen
2020-03-26  8:41           ` Marcin Borkowski
2020-03-25 11:49   ` Marcin Borkowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6d3b94ad-c804-47aa-acb0-3ec64159ee50@default \
    --to=drew.adams@oracle.com \
    --cc=help-gnu-emacs@gnu.org \
    --cc=michael_heerdegen@web.de \
    --cc=monnier@iro.umontreal.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).