Browse Source

Allow choosing backend.

Damien Merenne 5 years ago
parent
commit
1f8d7a5dbc
5 changed files with 161 additions and 77 deletions
  1. 9 8
      README.md
  2. 45 13
      test/xwidget-plus-follow-link-test.el
  3. 13 0
      xwidget-plus-common.el
  4. 77 52
      xwidget-plus-follow-link.el
  5. 17 4
      xwidget-plus.el

+ 9 - 8
README.md

@@ -11,20 +11,21 @@ page. It also highlight the candidates on the web page.
 
 ![Imgur](https://i.imgur.com/1KO70FE.gif)
 
-It currently uses `ivy` for completion because it provides an easy way to hook
-into the candidate selection process (`update-fn`). I'm willing to add support
-for other completion backend, open an issue with your backend of choice and I'll
-have a look at what can be done. Or better, fork and create a pull request, most
-of the needed code is already there, it just needs to be hooked.
+It currently supports `completing-read`, `ido`, `ivy` and `helm` completion
+backend. I'm willing to add support for other completion backend, open an issue
+with your backend of choice and I'll have a look at what can be done. Or better,
+fork and create a pull request, most of the needed code is already there, it
+just needs to be hooked.
 
 ## Switch to xwidget on browse
 
-When opening a url using `xwidget-webkit` in an already opened session that is
-not visible, the session is brought to the front.
+This package provides the `xwiget-plus-browse-url` function. Unlike
+`xwidget-webkit-browse-url`, when reusing an existing session, it will bring it
+to the front.
 
 ## How to install
 
-Sorry, no melpa as of now.
+Sorry, no melpa as of now. Should be added quite soon.
 
 ```
 (use-package xwidget-plus

+ 45 - 13
test/xwidget-plus-follow-link-test.el

@@ -23,12 +23,8 @@
 
 (require 'test-helper)
 (require 'with-simulated-input)
-(require 'ivy)
-(require 'helm)
 (require 'xwidget-plus-follow-link)
 
-;; Disable helm and ivy, otherwise they hijack completing-read and break the
-;; default completion backend test.
 (setq completing-read-function #'completing-read-default)
 
 
@@ -41,7 +37,7 @@
 
 (defun xwidget-plus-update-fn-callback (result)
   "Called after updating candidates with the css classes in RESULT."
-  (let ((backend xwidget-plus-completion-backend-instance))
+  (let ((backend xwidget-plus-follow-link-completion-backend-instance))
     ;; Store the results.
     (oset backend classes result)
     ;; Trigger the action function with the mocked selected link
@@ -79,15 +75,15 @@ r
   "Run BODY with the specified BACKEND."
   (declare (indent 1))
   `(let* ((backend (,(intern (concat "xwidget-plus-completion-backend-" (symbol-name backend)))))
-          (xwidget-plus-completion-backend-instance backend))
+          (xwidget-plus-follow-link-completion-backend-instance backend))
      ,@body))
 
+
 (defmacro with-test-backend-browse (candidates selected url &rest body)
   "Run BODY with the specified BACKEND mocking CANDIDATES and SELECTED while browsing URL."
   (declare (indent 3))
-  `(let* ((backend (xwidget-plus-completion-backend-test :candidates-mock ,candidates
-                                                         :selected-mock ,selected))
-          (xwidget-plus-completion-backend-instance backend))
+  `(let* ((xwidget-plus-completion-system (lambda () (xwidget-plus-completion-backend-test :candidates-mock ,candidates
+                                                                                           :selected-mock ,selected))))
      (with-browse ,url
        ,@body)))
 
@@ -109,16 +105,18 @@ r
     (xwidget-plus-follow-link)
       (xwidget-plus-event-dispatch)
       (should (string= "test-1.html" (file-name-nondirectory (xwidget-webkit-current-url))))
-      (should (equal (backend-test-link-classes backend "test-1") '["xwidget-plus-follow-link-selected"]))
-      (should (equal (backend-test-link-classes backend "test-2") '["xwidget-plus-follow-link-candidate"])))
+      (let ((backend xwidget-plus-follow-link-completion-backend-instance))
+        (should (equal (backend-test-link-classes backend "test-1") '["xwidget-plus-follow-link-selected"]))
+        (should (equal (backend-test-link-classes backend "test-2") '["xwidget-plus-follow-link-candidate"]))))
   (with-test-backend-browse '(1 0 1) 1 "links.html"
     (split-window-vertically)
     (save-excursion (switch-to-buffer-other-window "*Messages*"))
     (xwidget-plus-follow-link)
     (xwidget-plus-event-dispatch)
     (should (string= "test-2.html" (file-name-nondirectory (xwidget-webkit-current-url))))
-    (should (equal (backend-test-link-classes backend "test-1") '["xwidget-plus-follow-link-candidate"]))
-    (should (equal (backend-test-link-classes backend "test-2") '["xwidget-plus-follow-link-selected"]))))
+    (let ((backend xwidget-plus-follow-link-completion-backend-instance))
+      (should (equal (backend-test-link-classes backend "test-1") '["xwidget-plus-follow-link-candidate"]))
+      (should (equal (backend-test-link-classes backend "test-2") '["xwidget-plus-follow-link-selected"])))))
 
 (defmacro with-read-fixtures (backend &rest body)
   (declare (indent 1))
@@ -137,23 +135,57 @@ r
       (should (= 1 link)))))
 
 (ert-deftest test-xwidget-plus-follow-link-read-ido ()
+  (require 'ido)
   (with-read-fixtures ido
     (with-simulated-input "2 RET"
       (xwidget-plus-follow-link-read backend "Test: " links action update)
       (should (= 1 link)))))
 
 (ert-deftest test-xwidget-plus-follow-link-read-ivy ()
+  (require 'ivy)
   (with-read-fixtures ivy
     (with-simulated-input "2 RET"
       (xwidget-plus-follow-link-read backend "Test: " links action update)
       (should (= 1 link)))))
 
 (ert-deftest test-xwidget-plus-follow-link-read-helm ()
+  (require 'helm)
   (with-read-fixtures helm
     (with-simulated-input '("2" (wsi-simulate-idle-time 0.1) "RET")
       (xwidget-plus-follow-link-read backend "Test: " links action update)
       (should (= 1 link)))))
 
+(defmacro with-feature (feature &rest body)
+  (declare (indent 1))
+  `(progn (when (featurep 'ido) (unload-feature 'ido t))
+          (when (featurep 'ivy) (unload-feature 'ivy t))
+          (when (featurep 'helm) (unload-feature 'helm t))
+          (when ,feature (require ,feature))
+          ,@body))
+
+(ert-deftest test-xwidget-plus-follow-link-make-backend-use-feature ()
+  (with-feature nil
+    (should (eq #'xwidget-plus-completion-backend-default (xwidget-plus-follow-link-make-backend))))
+  (with-feature 'ido
+    (should (eq #'xwidget-plus-completion-backend-ido (xwidget-plus-follow-link-make-backend))))
+  (with-feature 'ivy
+    (should (eq #'xwidget-plus-completion-backend-ivy (xwidget-plus-follow-link-make-backend))))
+  (with-feature 'helm
+    (should (eq #'xwidget-plus-completion-backend-helm (xwidget-plus-follow-link-make-backend)))))
+
+(ert-deftest test-xwidget-plus-follow-link-make-backend-use-custom ()
+  (let ((xwidget-plus-completion-system 'default))
+    (with-feature nil
+      (should (eq #'xwidget-plus-completion-backend-default (xwidget-plus-follow-link-make-backend)))))
+  (let ((xwidget-plus-completion-system 'ido))
+    (should (eq #'xwidget-plus-completion-backend-ido (xwidget-plus-follow-link-make-backend))))
+  (let ((xwidget-plus-completion-system 'ivy))
+    (should (eq #'xwidget-plus-completion-backend-ivy (xwidget-plus-follow-link-make-backend))))
+  (let ((xwidget-plus-completion-system 'helm))
+    (should (eq #'xwidget-plus-completion-backend-helm (xwidget-plus-follow-link-make-backend))))
+  (let ((xwidget-plus-completion-system #'identity))
+    (should (eq #'identity (xwidget-plus-follow-link-make-backend)))))
+
 ;; Local Variables:
 ;; eval: (mmm-mode)
 ;; eval: (mmm-add-classes '((elisp-js :submode js-mode :face mmm-code-submode-face :delimiter-mode nil :front "--js \"" :back "\" js--")))

+ 13 - 0
xwidget-plus-common.el

@@ -25,6 +25,19 @@
   "Augment the xwidget webkit browser."
   :group 'convenience)
 
+(defcustom xwidget-plus-completion-system 'default
+  "The completion system to be used by xwidget plus.
+
+Custom function should be a function that takes no arguments and
+returns an instance of an eieio class extending
+`xwidget-plus-completion-backend'."
+  :group 'xwidget-plus
+  :type '(radio
+          (const :tag "Ido" ido)
+          (const :tag "Helm" helm)
+          (const :tag "Ivy" ivy)
+          (const :tag "Default" default)
+          (function :tag "Custom function")))
 
 (require 'xwidget)
 (require 'json)

+ 77 - 52
xwidget-plus-follow-link.el

@@ -138,68 +138,92 @@ browser."
 
 
 ;; Ido backend using ido-completing-read
-(defclass xwidget-plus-completion-backend-ido (xwidget-plus-completion-backend) ())
+(with-eval-after-load 'ido
+  (defclass xwidget-plus-completion-backend-ido (xwidget-plus-completion-backend) ())
 
-(cl-defmethod xwidget-plus-follow-link-candidates ((backend xwidget-plus-completion-backend-ido))
-  (let ((collection (oref backend collection)))
-    (when collection
-      (seq-map (lambda (i) (cdr (assoc i collection))) ido-matches))))
+  (cl-defmethod xwidget-plus-follow-link-candidates ((backend xwidget-plus-completion-backend-ido))
+    (let ((collection (oref backend collection)))
+      (when collection
+        (seq-map (lambda (i) (cdr (assoc i collection))) ido-matches))))
 
-(cl-defmethod xwidget-plus-follow-link-read ((backend xwidget-plus-completion-backend-ido) prompt collection action update-fn)
-  (let ((choices (seq-map #'car collection)))
-    (advice-add #'ido-set-matches :after update-fn)
-    (let ((link (cdr (assoc (ido-completing-read prompt choices nil t) collection))))
-      (oset backend collection nil)
-      (advice-remove #'ido-set-matches #'update-fn)
-      (funcall action link))))
+  (cl-defmethod xwidget-plus-follow-link-read ((backend xwidget-plus-completion-backend-ido) prompt collection action update-fn)
+    (let ((choices (seq-map #'car collection)))
+      (advice-add #'ido-set-matches :after update-fn)
+      (let ((link (cdr (assoc (ido-completing-read prompt choices nil t) collection))))
+        (oset backend collection nil)
+        (advice-remove #'ido-set-matches #'update-fn)
+        (funcall action link)))))
 
 
 ;; Ivy backend using completing read
-(defclass xwidget-plus-completion-backend-ivy (xwidget-plus-completion-backend) ())
+(with-eval-after-load 'ivy
+  (defclass xwidget-plus-completion-backend-ivy (xwidget-plus-completion-backend) ())
 
-(cl-defmethod xwidget-plus-follow-link-candidates ((backend xwidget-plus-completion-backend-ivy))
-  (with-current-buffer (ivy-state-buffer ivy-last)
-    (let* ((collection (ivy-state-collection ivy-last))
-           (current (ivy-state-current ivy-last))
-           (candidates (ivy--filter ivy-text ivy--all-candidates))
-           (result (cons current candidates)))
-      (seq-map (lambda (c) (cdr (nth (get-text-property 0 'idx c) collection))) result))))
+  (cl-defmethod xwidget-plus-follow-link-candidates ((backend xwidget-plus-completion-backend-ivy))
+    (with-current-buffer (ivy-state-buffer ivy-last)
+      (let* ((collection (ivy-state-collection ivy-last))
+             (current (ivy-state-current ivy-last))
+             (candidates (ivy--filter ivy-text ivy--all-candidates))
+             (result (cons current candidates)))
+        (seq-map (lambda (c) (cdr (nth (get-text-property 0 'idx c) collection))) result))))
 
-(cl-defmethod xwidget-plus-follow-link-read ((backend xwidget-plus-completion-backend-ivy) prompt collection action update-fn)
-  (ivy-read "Link: " collection :require-match t :action (lambda (v) (funcall action (cdr v))) :update-fn update-fn))
+  (cl-defmethod xwidget-plus-follow-link-read ((backend xwidget-plus-completion-backend-ivy) prompt collection action update-fn)
+    (ivy-read "Link: " collection :require-match t :action (lambda (v) (funcall action (cdr v))) :update-fn update-fn)))
 
 
 ;; Helm backend
-(defclass xwidget-plus-completion-backend-helm (xwidget-plus-completion-backend) ((candidates)))
-
-(cl-defmethod xwidget-plus-follow-link-candidates ((backend xwidget-plus-completion-backend-helm))
-  (let* ((candidates (oref backend candidates))
-         (selection (helm-get-selection))
-         (selected (when selection (cdr (elt (oref backend collection) selection))))
-         (result (seq-map #'cdr candidates)))
-    (cons selected result)))
-
-(cl-defmethod xwidget-plus-follow-link-read ((backend xwidget-plus-completion-backend-helm) prompt collection action update-fn)
-  (add-hook 'helm-after-initialize-hook (lambda ()
-                                          (with-current-buffer "*helm-xwidget-plus*"
-                                            (add-hook 'helm-move-selection-after-hook update-fn nil t)))
-            nil t)
-  (helm :sources
-        (helm-make-source "Xwidget Plus" 'helm-source-sync
-          :candidates collection
-          :action action
-          :filtered-candidate-transformer (lambda (candidates _)
-                                            (oset backend candidates candidates)
-                                            (funcall update-fn)
-                                            candidates))
-        :prompt prompt
-        :buffer "*helm-xwidget-plus*"))
-
-(defvar xwidget-plus-completion-backend-instance (xwidget-plus-completion-backend-ivy))
+(with-eval-after-load 'helm
+  (defclass xwidget-plus-completion-backend-helm (xwidget-plus-completion-backend) ((candidates)))
+
+  (cl-defmethod xwidget-plus-follow-link-candidates ((backend xwidget-plus-completion-backend-helm))
+    (let* ((candidates (oref backend candidates))
+           (selection (helm-get-selection))
+           (selected (when selection (cdr (elt (oref backend collection) selection))))
+           (result (seq-map #'cdr candidates)))
+      (cons selected result)))
+
+  (cl-defmethod xwidget-plus-follow-link-read ((backend xwidget-plus-completion-backend-helm) prompt collection action update-fn)
+    (add-hook 'helm-after-initialize-hook (lambda ()
+                                            (with-current-buffer "*helm-xwidget-plus*"
+                                              (add-hook 'helm-move-selection-after-hook update-fn nil t)))
+              nil t)
+    (helm :sources
+          (helm-make-source "Xwidget Plus" 'helm-source-sync
+                            :candidates collection
+                            :action action
+                            :filtered-candidate-transformer (lambda (candidates _)
+                                                              (oset backend candidates candidates)
+                                                              (funcall update-fn)
+                                                              candidates))
+          :prompt prompt
+          :buffer "*helm-xwidget-plus*")))
+
+(defun xwidget-plus-follow-link-make-backend ()
+  "Instanciate a completion backend."
+  (cond ((eq xwidget-plus-completion-system 'default)
+         (cond ((featurep 'ivy)
+                #'xwidget-plus-completion-backend-ivy)
+               ((featurep 'helm)
+                #'xwidget-plus-completion-backend-helm)
+               ((featurep 'ido)
+                #'xwidget-plus-completion-backend-ido)
+               (t #'xwidget-plus-completion-backend-default)))
+        ((eq xwidget-plus-completion-system 'ivy)
+         #'xwidget-plus-completion-backend-ivy)
+        ((eq xwidget-plus-completion-system 'helm)
+         #'xwidget-plus-completion-backend-helm)
+        ((eq xwidget-plus-completion-system 'ido)
+         #'xwidget-plus-completion-backend-ido)
+        ((eq xwidget-plus-completion-system 'default)
+         #'xwidget-plus-completion-backend-default)
+        (t xwidget-plus-completion-system)))
+
+
+(defvar xwidget-plus-follow-link-completion-backend-instance '())
 
 (defun xwidget-plus-follow-link-update (xwidget)
   "Highligh LINKS in XWIDGET buffer when updating candidates."
-  (let ((links (xwidget-plus-follow-link-candidates xwidget-plus-completion-backend-instance)))
+  (let ((links (xwidget-plus-follow-link-candidates xwidget-plus-follow-link-completion-backend-instance)))
     (when links
       (let* ((selected (car links))
              (candidates (cdr links)))
@@ -227,20 +251,21 @@ browser."
   (let* ((xwidget (xwidget-webkit-current-session))
          (links (xwidget-plus-follow-link-prepare-links links))
          link)
-    (oset xwidget-plus-completion-backend-instance collection links)
+    (oset xwidget-plus-follow-link-completion-backend-instance collection links)
     (unwind-protect
         (condition-case nil
-            (xwidget-plus-follow-link-read xwidget-plus-completion-backend-instance
+            (xwidget-plus-follow-link-read xwidget-plus-follow-link-completion-backend-instance
                                            "Link: " links
                                            (apply-partially #'xwidget-plus-follow-link-trigger-action xwidget)
                                            (apply-partially #'xwidget-plus-follow-link-update xwidget))
           (quit (xwidget-plus-follow-link-cleanupxwidget))))
-    (oset xwidget-plus-completion-backend-instance collection nil)))
+    (oset xwidget-plus-follow-link-completion-backend-instance collection nil)))
 
 ;;;###autoload
 (defun xwidget-plus-follow-link (&optional xwidget)
   "Ask for a link in the XWIDGET session or the current one and follow it."
   (interactive)
+  (setq xwidget-plus-follow-link-completion-backend-instance (funcall (xwidget-plus-follow-link-make-backend)))
   (let ((xwidget (or xwidget (xwidget-webkit-current-session))))
     (xwidget-plus-inject-style xwidget "__xwidget_plus_follow_link_style" (xwidget-plus-follow-link-style-definition))
     (xwidget-plus-js-inject xwidget 'follow-link)

+ 17 - 4
xwidget-plus.el

@@ -43,10 +43,23 @@
 (require 'xwidget-plus-follow-link)
 
 ;; Bring the window to front when summoning browse
-(defun xwidget-plus-webkit-browse-url-advise (&rest _)
-    "Advice to add switch to window when calling `xwidget-webkit-browse-url'."
-    (switch-to-buffer-other-window (xwidget-buffer (xwidget-webkit-current-session))))
-(advice-add #'xwidget-webkit-browse-url :after #'xwidget-plus-webkit-browse-url-advise)
+;;;###autoload
+(defun xwidget-plus-browse-url (url &optional new-session)
+  "Ask xwidget-webkit to browse URL.
+NEW-SESSION specifies whether to create a new xwidget-webkit session.
+Interactively, URL defaults to the string looking like a url around point."
+  (interactive (progn
+                 (require 'browse-url)
+                 (browse-url-interactive-arg "xwidget-webkit URL: "
+                                             ;;(xwidget-webkit-current-url)
+                                             )))
+  (or (featurep 'xwidget-internal)
+      (user-error "Your Emacs was not compiled with xwidgets support"))
+  (when (stringp url)
+    (if new-session
+        (xwidget-webkit-new-session url)
+      (progn (xwidget-webkit-goto-url url)
+             (switch-to-buffer-other-window (xwidget-buffer (xwidget-webkit-current-session)))))))
 
 ;; Local Variables:
 ;; eval: (mmm-mode)