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)
 ![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
 ## 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
 ## How to install
 
 
-Sorry, no melpa as of now.
+Sorry, no melpa as of now. Should be added quite soon.
 
 
 ```
 ```
 (use-package xwidget-plus
 (use-package xwidget-plus

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

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

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

@@ -138,68 +138,92 @@ browser."
 
 
 
 
 ;; Ido backend using ido-completing-read
 ;; 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
 ;; 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
 ;; 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)
 (defun xwidget-plus-follow-link-update (xwidget)
   "Highligh LINKS in XWIDGET buffer when updating candidates."
   "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
     (when links
       (let* ((selected (car links))
       (let* ((selected (car links))
              (candidates (cdr links)))
              (candidates (cdr links)))
@@ -227,20 +251,21 @@ browser."
   (let* ((xwidget (xwidget-webkit-current-session))
   (let* ((xwidget (xwidget-webkit-current-session))
          (links (xwidget-plus-follow-link-prepare-links links))
          (links (xwidget-plus-follow-link-prepare-links links))
          link)
          link)
-    (oset xwidget-plus-completion-backend-instance collection links)
+    (oset xwidget-plus-follow-link-completion-backend-instance collection links)
     (unwind-protect
     (unwind-protect
         (condition-case nil
         (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
                                            "Link: " links
                                            (apply-partially #'xwidget-plus-follow-link-trigger-action xwidget)
                                            (apply-partially #'xwidget-plus-follow-link-trigger-action xwidget)
                                            (apply-partially #'xwidget-plus-follow-link-update xwidget))
                                            (apply-partially #'xwidget-plus-follow-link-update xwidget))
           (quit (xwidget-plus-follow-link-cleanupxwidget))))
           (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
 ;;;###autoload
 (defun xwidget-plus-follow-link (&optional xwidget)
 (defun xwidget-plus-follow-link (&optional xwidget)
   "Ask for a link in the XWIDGET session or the current one and follow it."
   "Ask for a link in the XWIDGET session or the current one and follow it."
   (interactive)
   (interactive)
+  (setq xwidget-plus-follow-link-completion-backend-instance (funcall (xwidget-plus-follow-link-make-backend)))
   (let ((xwidget (or xwidget (xwidget-webkit-current-session))))
   (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-inject-style xwidget "__xwidget_plus_follow_link_style" (xwidget-plus-follow-link-style-definition))
     (xwidget-plus-js-inject xwidget 'follow-link)
     (xwidget-plus-js-inject xwidget 'follow-link)

+ 17 - 4
xwidget-plus.el

@@ -43,10 +43,23 @@
 (require 'xwidget-plus-follow-link)
 (require 'xwidget-plus-follow-link)
 
 
 ;; Bring the window to front when summoning browse
 ;; 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:
 ;; Local Variables:
 ;; eval: (mmm-mode)
 ;; eval: (mmm-mode)