[PATCH (draft)] company model for external programs

Subject: [PATCH (draft)] company model for external programs

Date: Tue, 27 Oct 2015 09:05:28 +0000

To: notmuch@notmuchmail.org

Cc:

From: Mark Walters


---

This is an attempt to make company mode work for external address
completion programs. We need to be able to run the address completion
asynchronously.

The changes are three fold: separate out the internal completion code
into its own function, allow the external program to be called
asynchronously, and copy the relevant code from the emacs function
process-lines into our function as it doesn't appear to be available
as a separate function.

It seems to work in light testing but asynchronous emacs is always a
little fragile/tricky.

Best wishes

Mark



emacs/notmuch-address.el |  2 +-
 emacs/notmuch-company.el | 65 +++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 51 insertions(+), 16 deletions(-)

diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el
index 49e2402..65d04ce 100644
--- a/emacs/notmuch-address.el
+++ b/emacs/notmuch-address.el
@@ -81,7 +81,7 @@ (defcustom notmuch-address-use-company t
 
 (defun notmuch-address-setup ()
   (let* ((use-company (and notmuch-address-use-company
-			   (eq notmuch-address-command 'internal)
+		;;	   (eq notmuch-address-command 'internal)
 			   (require 'company nil t)))
 	 (pair (cons notmuch-address-completion-headers-regexp
 		     (if use-company
diff --git a/emacs/notmuch-company.el b/emacs/notmuch-company.el
index add3161..dc3d018 100644
--- a/emacs/notmuch-company.el
+++ b/emacs/notmuch-company.el
@@ -42,6 +42,7 @@ (defvar company-backends)
 (declare-function notmuch-address-matching "notmuch-address")
 (defvar notmuch-address-full-harvest-finished)
 (defvar notmuch-address-completion-headers-regexp)
+(defvar notmuch-address-command)
 
 ;;;###autoload
 (defun notmuch-company-setup ()
@@ -49,6 +50,54 @@ (defun notmuch-company-setup ()
   (make-local-variable 'company-backends)
   (setq company-backends '(notmuch-company)))
 
+(defun notmuch-company-address-internal (arg)
+  (cond
+   (notmuch-address-full-harvest-finished
+    ;; Update harvested addressed from time to time
+    (notmuch-address-harvest-trigger)
+    (notmuch-address-matching arg))
+   (t
+    (cons :async
+	  (lambda (callback)
+	    ;; First run quick asynchronous harvest based on what the user entered so far
+	    (notmuch-address-harvest
+	     (format "to:%s*" arg) nil
+	     (lambda (_proc _event)
+	       (funcall callback (notmuch-address-matching arg))
+	       ;; Then start the (potentially long-running) full asynchronous harvest if necessary
+	       (notmuch-address-harvest-trigger))))))))
+
+(defun notmuch-company-external-sentinel (callback proc _event)
+  (let (lines)
+    (with-current-buffer (process-buffer proc)
+      ;; Copied verbatim from the process-lines function in subr.el in
+      ;; the standard emacs distribution.
+      (goto-char (point-min))
+      (while (not (eobp))
+	(setq lines (cons (buffer-substring-no-properties
+			   (line-beginning-position)
+			   (line-end-position))
+			  lines))
+	(forward-line 1))
+      (message "lines %s" lines))
+    (kill-buffer (process-buffer proc))
+    (funcall callback (nreverse lines))))
+
+(defun notmuch-company-address-external (arg)
+  (cons :async
+	(lambda (callback)
+	  (let* ((buf (generate-new-buffer " *notmuch-external-address*"))
+		 (proc (start-process "notmuch-external-address" buf
+				      notmuch-address-command arg)))
+	    (set-process-sentinel proc (apply-partially
+					'notmuch-company-external-sentinel
+					callback))))))
+
+(defun notmuch-company-address (arg)
+  (if (eq notmuch-address-command 'internal)
+      (notmuch-company-address-internal arg)
+    (notmuch-company-address-external arg)))
+
 ;;;###autoload
 (defun notmuch-company (command &optional arg &rest _ignore)
   "`company-mode' completion back-end for `notmuch'."
@@ -62,21 +111,7 @@ (defun notmuch-company (command &optional arg &rest _ignore)
 		   (looking-back (concat notmuch-address-completion-headers-regexp ".*")
 				 (line-beginning-position))
 		   (setq notmuch-company-last-prefix (company-grab "[:,][ \t]*\\(.*\\)" 1 (point-at-bol)))))
-      (candidates (cond
-		   (notmuch-address-full-harvest-finished
-		    ;; Update harvested addressed from time to time
-		    (notmuch-address-harvest-trigger)
-		    (notmuch-address-matching arg))
-		   (t
-		    (cons :async
-			  (lambda (callback)
-			    ;; First run quick asynchronous harvest based on what the user entered so far
-			    (notmuch-address-harvest
-			     (format "to:%s*" arg) nil
-			     (lambda (_proc _event)
-			       (funcall callback (notmuch-address-matching arg))
-			       ;; Then start the (potentially long-running) full asynchronous harvest if necessary
-			       (notmuch-address-harvest-trigger))))))))
+      (candidates (notmuch-company-address arg))
       (match (if (string-match notmuch-company-last-prefix arg)
 		 (match-end 0)
 	       0))
-- 
2.1.4


Thread: