فهرست منبع

add improvements for ivy backend to support user defined actions that access the
URLs of links and for fancier display with ivy rich. Thanks to Q. Hong who
extended xxwp-follow-link to store urls for candidates.

Boris Glavic 5 سال پیش
والد
کامیت
99670ec37e
4فایلهای تغییر یافته به همراه92 افزوده شده و 31 حذف شده
  1. 14 16
      test/xwwp-follow-link-test.el
  2. 1 1
      xwwp-follow-link-helm.el
  3. 56 1
      xwwp-follow-link-ivy.el
  4. 21 13
      xwwp-follow-link.el

+ 14 - 16
test/xwwp-follow-link-test.el

@@ -108,20 +108,20 @@ return '' + window.location;
        ,@body)))
        ,@body)))
 
 
 (ert-deftest test-xwwp-follow-link-prepare-links ()
 (ert-deftest test-xwwp-follow-link-prepare-links ()
-  (let ((links '(("3" . "Functions")
-                 ("1" . "Function Cells")
-                 ("12" . "Structures")
-                 ("2" . "Anonymous Functions")
-                 ("9" . "Declare Form"))))
-    (should (equal '(("Function Cells" . 1)
-	             ("Anonymous Functions" . 2)
-	             ("Functions" . 3)
-	             ("Declare Form" . 9)
-	             ("Structures" . 12))
+  (let ((links '(("3" . ["Functions" "http://www.this.is.a.test.de/functions"])
+                 ("1" . ["Function Cells" "http://www.this.is.a.test.de/function-cells"])
+                 ("12" . ["Structures" "http://www.this.is.a.test.de/structures"])
+                 ("2" . ["Anonymous Functions" "http://www.this.is.a.test.de/anon"])
+                 ("9" . ["Declare Form" "http://www.this.is.a.test.de/declare-form"]))))
+    (should (equal '(("Function Cells" . (1 "http://www.this.is.a.test.de/function-cells"))
+	             ("Anonymous Functions" . (2 "http://www.this.is.a.test.de/anon"))
+	             ("Functions" . (3 "http://www.this.is.a.test.de/functions"))
+	             ("Declare Form" . (9 "http://www.this.is.a.test.de/declare-form"))
+	             ("Structures" . (12 "http://www.this.is.a.test.de/structures")))
             (xwwp-follow-link-prepare-links links)))))
             (xwwp-follow-link-prepare-links links)))))
 
 
 (ert-deftest test-xwwp-follow-link-highlight ()
 (ert-deftest test-xwwp-follow-link-highlight ()
-  (with-test-backend-browse '(0 0 1) 0 "links.html"
+  (with-test-backend-browse '((0 "http://") (0 "http://") (1 "http://")) '(0 "http://") "links.html"
     (xwwp-follow-link)
     (xwwp-follow-link)
     (xwwp-event-loop)
     (xwwp-event-loop)
     (let ((backend xwwp-follow-link-completion-backend-instance))
     (let ((backend xwwp-follow-link-completion-backend-instance))
@@ -131,7 +131,7 @@ return '' + window.location;
       (should (string= "test-1.html" (file-name-nondirectory (oref backend location))))
       (should (string= "test-1.html" (file-name-nondirectory (oref backend location))))
       (should (equal (backend-test-link-classes backend "test-1") '["xwwp-follow-link-selected"]))
       (should (equal (backend-test-link-classes backend "test-1") '["xwwp-follow-link-selected"]))
       (should (equal (backend-test-link-classes backend "test-2") '["xwwp-follow-link-candidate"]))))
       (should (equal (backend-test-link-classes backend "test-2") '["xwwp-follow-link-candidate"]))))
-  (with-test-backend-browse '(1 0 1) 1 "links.html"
+  (with-test-backend-browse '((1 "http://") (0 "http://") (1 "http://")) '(1 "http://") "links.html"
     (xwwp-follow-link)
     (xwwp-follow-link)
     (xwwp-event-loop)
     (xwwp-event-loop)
     (let ((backend xwwp-follow-link-completion-backend-instance))
     (let ((backend xwwp-follow-link-completion-backend-instance))
@@ -140,10 +140,8 @@ return '' + window.location;
       (xwwp-event-dispatch)
       (xwwp-event-dispatch)
       (should (string= "test-2.html" (file-name-nondirectory (oref backend location))))
       (should (string= "test-2.html" (file-name-nondirectory (oref backend location))))
       (should (equal (backend-test-link-classes backend "test-1") '["xwwp-follow-link-candidate"]))
       (should (equal (backend-test-link-classes backend "test-1") '["xwwp-follow-link-candidate"]))
-      (should (equal (backend-test-link-classes backend "test-2") '["xwwp-follow-link-selected"])))))
-
-(ert-deftest test-xwwp-follow-link-highlight-no-candidates ()
-  (with-test-backend-browse '(1) 1 "links.html"
+      (should (equal (backend-test-link-classes backend "test-2") '["xwwp-follow-link-selected"]))))
+  (with-test-backend-browse '((1 "http://")) '(1 "http://") "links.html"
     (xwwp-follow-link)
     (xwwp-follow-link)
     (xwwp-event-loop)
     (xwwp-event-loop)
     (let ((backend xwwp-follow-link-completion-backend-instance))
     (let ((backend xwwp-follow-link-completion-backend-instance))

+ 1 - 1
xwwp-follow-link-helm.el

@@ -7,7 +7,7 @@
 ;; Version: 0.1
 ;; Version: 0.1
 ;; Package-Requires: ((emacs "26.1") (xwwp "0.1"))
 ;; Package-Requires: ((emacs "26.1") (xwwp "0.1"))
 
 
-;; Copyright (C) 2020 Damien Merenne <dam@cosinux.org>
+;; Copyright (C) 2020 Q. Hong <qhong@mit.edu>, Damien Merenne <dam@cosinux.org>
 
 
 ;; This file is NOT part of GNU Emacs.
 ;; This file is NOT part of GNU Emacs.
 
 

+ 56 - 1
xwwp-follow-link-ivy.el

@@ -36,9 +36,14 @@
 (require 'xwwp-follow-link)
 (require 'xwwp-follow-link)
 (require 'ivy)
 (require 'ivy)
 
 
+(defvar xwwp-follow-link-ivy-history
+  nil
+  "Stores history for links selected with command `xwwp-follow-link-completion-backend-ivy'.")
+
 (defclass xwwp-follow-link-completion-backend-ivy (xwwp-follow-link-completion-backend) ())
 (defclass xwwp-follow-link-completion-backend-ivy (xwwp-follow-link-completion-backend) ())
 
 
 (cl-defmethod xwwp-follow-link-candidates ((_ xwwp-follow-link-completion-backend-ivy))
 (cl-defmethod xwwp-follow-link-candidates ((_ xwwp-follow-link-completion-backend-ivy))
+  "Follow a link selected with ivy."
   (with-current-buffer (ivy-state-buffer ivy-last)
   (with-current-buffer (ivy-state-buffer ivy-last)
     (let* ((collection (ivy-state-collection ivy-last))
     (let* ((collection (ivy-state-collection ivy-last))
            (current (ivy-state-current ivy-last))
            (current (ivy-state-current ivy-last))
@@ -47,7 +52,57 @@
       (seq-map (lambda (c) (cdr (nth (get-text-property 0 'idx c) collection))) result))))
       (seq-map (lambda (c) (cdr (nth (get-text-property 0 'idx c) collection))) result))))
 
 
 (cl-defmethod xwwp-follow-link-read ((_ xwwp-follow-link-completion-backend-ivy) prompt collection action update-fn)
 (cl-defmethod xwwp-follow-link-read ((_ xwwp-follow-link-completion-backend-ivy) prompt collection action update-fn)
-  (ivy-read prompt collection :require-match t :action (lambda (v) (funcall action (cdr v))) :update-fn update-fn))
+  "Select a link from COLLECTION a with ivy showing PROMPT using UPDATE-FN to highlight the link in the xwidget session and execute ACTION once a link has been select."
+  (ivy-read prompt collection
+            :require-match t
+            :action (lambda (v) (funcall action (cdr v)))
+            :update-fn update-fn
+            :caller 'xwwp-follow-link-read
+            :history 'xwwp-follow-link-ivy-history))
+
+(defmacro xwwp-follow-link-ivy-ivify-action (v &rest body)
+  "Wraps BODY as an action for the `xwwp-follow-link-read' `ivy' version.
+The text and url of the link is made available through variables `linktext' and
+`linkurl' extracted from the selection candidate V.  Before the action is
+executed, the link highlights are removed from the HTML document shown in
+xwidgets."
+  `(let ((xwidget (xwidget-webkit-current-session))
+         (linktext (car ,v))
+         (linkurl (caddr ,v)))
+    (xwwp-follow-link-cleanup xwidget)
+    ,body))
+
+(defun xwwp-follow-link-ivy-copy-url-action (v)
+  "Copy the selected url from candidate V to the `kill-ring'."
+  (xwwp-follow-link-ivy-ivify-action v
+   (ignore 'linktext)
+   (kill-new linkurl)))
+
+(defun xwwp-follow-link-ivy-browse-external-action (v)
+  "Open the selected url from candidate V in the default browser."
+  (xwwp-follow-link-ivy-ivify-action v
+   (ignore 'linktext)
+   (browse-url linkurl)))
+
+(defun xwwp-follow-link-ivy-get-full-candidate (c)
+  "Return the full candidate for a text candidate C from xwwp-follow-link-ivy."
+  (with-current-buffer (ivy-state-buffer ivy-last)
+    (let* ((collection (ivy-state-collection ivy-last)))
+      (nth (get-text-property 0 'idx c) collection))))
+
+(defun xwwp-follow-link-ivy-get-candidate-text (v)
+  "Return the link text for completion candiate V for displaying candidates in `ivy-rich'."
+  (substring-no-properties (car (xwwp-follow-link-ivy-get-full-candidate v))))
+
+(defun xwwp-follow-link-ivy-get-candidate-url (v)
+  "Return the url for for a complettion candidate V for displaying candidates in `ivy-rich'."
+  (caddr (xwwp-follow-link-ivy-get-full-candidate v)))
+
+;; add ivy actions
+(ivy-add-actions
+ 'xwwp-follow-link-read
+ '(("w" xwwp-follow-link-ivy-copy-url-action "Copy URL")
+   ("e" xwwp-follow-link-ivy-browse-external-action "Open in default browser")))
 
 
 (provide 'xwwp-follow-link-ivy)
 (provide 'xwwp-follow-link-ivy)
 ;;; xwwp-follow-link-ivy.el ends here
 ;;; xwwp-follow-link-ivy.el ends here

+ 21 - 13
xwwp-follow-link.el

@@ -1,6 +1,6 @@
 ;;; xwwp-follow-link.el --- Link navigation in `xwidget-webkit' sessions -*- lexical-binding: t; -*-
 ;;; xwwp-follow-link.el --- Link navigation in `xwidget-webkit' sessions -*- lexical-binding: t; -*-
 
 
-;; Copyright (C) 2020 Damien Merenne <dam@cosinux.org>
+;; Copyright (C) 2020 Damien Merenne <dam@cosinux.org>, Q. Hong <qhong@mit.edu>
 
 
 ;; This file is NOT part of GNU Emacs.
 ;; This file is NOT part of GNU Emacs.
 
 
@@ -69,14 +69,15 @@ returns an instance of an eieio class extending
 
 
 (xwwp-js-def follow-link cleanup ()
 (xwwp-js-def follow-link cleanup ()
   "Remove all custom class from links.""
   "Remove all custom class from links.""
-document.querySelectorAll('a').forEach(a => {
+window.__xwidget_plus_follow_link_candidates.forEach(a => {
     a.classList.remove('xwwp-follow-link-candidate', 'xwwp-follow-link-selected');
     a.classList.remove('xwwp-follow-link-candidate', 'xwwp-follow-link-selected');
 });
 });
+window.__xwidget_plus_follow_link_candidates = null;
 ")
 ")
 
 
 (xwwp-js-def follow-link highlight (ids selected)
 (xwwp-js-def follow-link highlight (ids selected)
   "Highlight IDS as candidate and SELECTED as selected.""
   "Highlight IDS as candidate and SELECTED as selected.""
-document.querySelectorAll('a').forEach((a, id) => {
+window.__xwidget_plus_follow_link_candidates.forEach((a, id) => {
     a.classList.remove('xwwp-follow-link-candidate', 'xwwp-follow-link-selected');
     a.classList.remove('xwwp-follow-link-candidate', 'xwwp-follow-link-selected');
     if (selected == id) {
     if (selected == id) {
         a.classList.add('xwwp-follow-link-selected');
         a.classList.add('xwwp-follow-link-selected');
@@ -89,17 +90,19 @@ document.querySelectorAll('a').forEach((a, id) => {
 
 
 (xwwp-js-def follow-link action (link-id)
 (xwwp-js-def follow-link action (link-id)
   "Click on the link identified by LINK-ID""
   "Click on the link identified by LINK-ID""
+let selected = window.__xwidget_plus_follow_link_candidates[link_id];
 __xwidget_plus_follow_link_cleanup();
 __xwidget_plus_follow_link_cleanup();
-document.querySelectorAll('a')[link_id].click();
+selected.click();
 ")
 ")
 
 
 (xwwp-js-def follow-link fetch-links ()
 (xwwp-js-def follow-link fetch-links ()
   "Fetch all visible, non empty links from the current page.""
   "Fetch all visible, non empty links from the current page.""
 var r = {};
 var r = {};
-document.querySelectorAll('a').forEach((a, i) => {
+window.__xwidget_plus_follow_link_candidates = Array.from(document.querySelectorAll('a'));
+window.__xwidget_plus_follow_link_candidates.forEach((a, i) => {
     if (a.offsetWidth || a.offsetHeight || a.getClientRects().length) {
     if (a.offsetWidth || a.offsetHeight || a.getClientRects().length) {
         if (a.innerText.match(/\\\\S/))
         if (a.innerText.match(/\\\\S/))
-            r[i] = a.innerText;
+            r[i] = [a.innerText, a.href];
     }
     }
 });
 });
 return r;
 return r;
@@ -188,16 +191,18 @@ browser."
 (defvar xwwp-follow-link-completion-backend-instance '())
 (defvar xwwp-follow-link-completion-backend-instance '())
 
 
 (defun xwwp-follow-link-update (xwidget)
 (defun xwwp-follow-link-update (xwidget)
-  "Highligh LINKS in XWIDGET buffer when updating candidates."
+  "Highlight LINKS in XWIDGET buffer when updating candidates."
   (let ((links (xwwp-follow-link-candidates xwwp-follow-link-completion-backend-instance)))
   (let ((links (xwwp-follow-link-candidates xwwp-follow-link-completion-backend-instance)))
     (when links
     (when links
       (let* ((selected (car links))
       (let* ((selected (car links))
              (candidates (cdr links)))
              (candidates (cdr links)))
-        (xwwp-follow-link-highlight xwidget candidates selected)))))
+        (xwwp-follow-link-highlight xwidget (mapcar 'car candidates) (car selected))))))
 
 
 (defun xwwp-follow-link-trigger-action (xwidget selected)
 (defun xwwp-follow-link-trigger-action (xwidget selected)
-  "Activate link matching SELECTED in XWIDGET LINKS."
-  (xwwp-follow-link-action xwidget selected))
+  "Activate link matching SELECTED in XWIDGET LINKS.
+The SELECTED value is the cdr of an assoc in the collection passed to
+completion back end, which is of the form (numerical-id link-url)"
+  (xwwp-follow-link-action xwidget (car selected)))
 
 
 (defun xwwp-follow-link-format-link (str)
 (defun xwwp-follow-link-format-link (str)
   "Format link title STR."
   "Format link title STR."
@@ -208,12 +213,15 @@ browser."
 
 
 (defun xwwp-follow-link-prepare-links (links)
 (defun xwwp-follow-link-prepare-links (links)
   "Prepare the alist of LINKS."
   "Prepare the alist of LINKS."
-  (seq-sort-by (lambda (v) (cdr v)) #'<
-               (seq-map (lambda (v) (cons (xwwp-follow-link-format-link (cdr v)) (string-to-number (car v))))
+  (seq-sort-by (lambda (v) (cadr v)) #'<
+               (seq-map (lambda (v) (list (xwwp-follow-link-format-link (aref (cdr v) 0))
+                                          (string-to-number (car v))
+                                          (aref (cdr v) 1)))
                         links)))
                         links)))
 
 
 (defun xwwp-follow-link-callback (links)
 (defun xwwp-follow-link-callback (links)
-  "Ask for a link belonging to the alist LINKS."
+  "Ask for a link belonging to the alist LINKS.
+LINKS maps a numerical ID to an array of form [link-text, link-uri]"
   (let* ((xwidget (xwidget-webkit-current-session))
   (let* ((xwidget (xwidget-webkit-current-session))
          (links (xwwp-follow-link-prepare-links links)))
          (links (xwwp-follow-link-prepare-links links)))
     (oset xwwp-follow-link-completion-backend-instance collection links)
     (oset xwwp-follow-link-completion-backend-instance collection links)