From feeb964645084dd4ffc1e024dce64c513d1841cf Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Tue, 14 Oct 2014 23:51:44 +0200 Subject: [PATCH 1/8] Add hayoo backend for suggesting imports. --- haskell-process.el | 93 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 24 deletions(-) diff --git a/haskell-process.el b/haskell-process.el index 570fc2f96..5899b365a 100644 --- a/haskell-process.el +++ b/haskell-process.el @@ -139,6 +139,18 @@ See `haskell-process-do-cabal' for more details." :type 'boolean :group 'haskell-interactive) +(defcustom haskell-process-suggest-hayoo-imports + nil + "Suggest to add import statements using Hayoo as a backend." + :type 'boolean + :group 'haskell-interactive) + +(defcustom haskell-process-hayoo-query-url + "http://hayoo.fh-wedel.de/json/?query=%s" + "Query url for json hayoo results." + :type 'string + :group 'haskell-interactive) + (defcustom haskell-process-suggest-haskell-docs-imports nil "Suggest to add import statements using haskell-docs as a backend." @@ -839,6 +851,8 @@ from `module-buffer'." ((string-match "^Not in scope: .*[‘`‛]\\(.+\\)['’]$" msg) (when haskell-process-suggest-hoogle-imports (haskell-process-suggest-hoogle-imports session msg file)) + (when haskell-process-suggest-hayoo-imports + (haskell-process-suggest-hayoo-imports session msg file)) (when haskell-process-suggest-haskell-docs-imports (haskell-process-suggest-haskell-docs-imports session msg file))) ((string-match "^[ ]+It is a member of the hidden package [‘`‛]\\(.+\\)['’].$" msg) @@ -859,6 +873,30 @@ from `module-buffer'." cabal-file)) (haskell-cabal-add-dependency package-name version nil t)))) +(defun haskell-process-suggest-imports (imports ident) + "Given a list of IMPORTS, suggest adding them to the import section." + (let ((module (cond ((> (length modules) 1) + (when (y-or-n-p (format "Identifier `%s' not in scope, choose module to import?" + ident)) + (haskell-complete-module-read "Module: " modules))) + ((= (length modules) 1) + (let ((module (car modules))) + (unless (member module suggested-already) + (haskell-process-set-suggested-imports process (cons module suggested-already)) + (when (y-or-n-p (format "Identifier `%s' not in scope, import `%s'?" + ident + module)) + module))))))) + (when module + (haskell-process-find-file session file) + (save-excursion + (goto-char (point-max)) + (haskell-navigate-imports) + (insert (read-from-minibuffer "Import line: " (concat "import " module)) + "\n") + (haskell-sort-imports) + (haskell-align-imports))))) + (defun haskell-process-suggest-hoogle-imports (session msg file) "Given an out of scope identifier, Hoogle for that identifier, and if a result comes back, suggest to import that identifier @@ -870,30 +908,22 @@ now." (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" i) (match-string 1 i) i))) - (modules (haskell-process-hoogle-ident ident)) - (module - (cond - ((> (length modules) 1) - (when (y-or-n-p (format "Identifier `%s' not in scope, choose module to import?" - ident)) - (haskell-complete-module-read "Module: " modules))) - ((= (length modules) 1) - (let ((module (car modules))) - (unless (member module suggested-already) - (haskell-process-set-suggested-imports process (cons module suggested-already)) - (when (y-or-n-p (format "Identifier `%s' not in scope, import `%s'?" - ident - module)) - module))))))) - (when module - (haskell-process-find-file session file) - (save-excursion - (goto-char (point-max)) - (haskell-navigate-imports) - (insert (read-from-minibuffer "Import line: " (concat "import " module)) - "\n") - (haskell-sort-imports) - (haskell-align-imports))))) + (modules (haskell-process-hoogle-ident ident))) + (haskell-process-suggest-imports modules ident))) + +(defun haskell-process-suggest-hayoo-imports (session msg file) + "Given an out of scope identifier, Hayoo for that identifier, +and if a result comes back, suggest to import that identifier +now." + (let* ((process (haskell-session-process session)) + (suggested-already (haskell-process-suggested-imports process)) + (ident (let ((i (match-string 1 msg))) + ;; Skip qualification. + (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" i) + (match-string 1 i) + i)))) + (haskell-process-hayoo-ident ident #'haskell-process-suggest-imports))) + (defun haskell-process-suggest-haskell-docs-imports (session msg file) "Given an out of scope identifier, haskell-docs search for that identifier, @@ -951,6 +981,21 @@ now." (split-string (buffer-string) "\n")))))) +(defun haskell-process-hayoo-ident (ident callback) + "Hayoo for IDENT, returns a list of modules asyncronously through CALLBACK." + (setq haskell-process-hayoo-ident--ident ident + haskell-process-hayoo-ident--callback callback) + (url-retrieve + (format haskell-process-hayoo-query-url ident) + (lambda (status) + (re-search-forward "\r?\n\r?\n") + (let* ((res (json-read-object)) + (results (assoc-default 'result res)) + (modules-l (cl-mapcar (lambda (r) (cl-mapcar 'identity (assoc-default 'resultModules r))) results)) + (modules (apply #'append modules-l) )) + (funcall haskell-process-hayoo-ident--callback modules + haskell-process-hayoo-ident--ident))))) + (defun haskell-process-suggest-remove-import (session file import line) "Suggest removing or commenting out IMPORT on LINE." (let ((continue t) From fa7762c20071a3dad2925ffacf724bda2f757e00 Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Wed, 15 Oct 2014 00:45:12 +0200 Subject: [PATCH 2/8] Fixed typo and moved suggested-already. --- haskell-process.el | 60 +++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/haskell-process.el b/haskell-process.el index 5899b365a..493f0249c 100644 --- a/haskell-process.el +++ b/haskell-process.el @@ -873,20 +873,25 @@ from `module-buffer'." cabal-file)) (haskell-cabal-add-dependency package-name version nil t)))) -(defun haskell-process-suggest-imports (imports ident) - "Given a list of IMPORTS, suggest adding them to the import section." - (let ((module (cond ((> (length modules) 1) - (when (y-or-n-p (format "Identifier `%s' not in scope, choose module to import?" - ident)) - (haskell-complete-module-read "Module: " modules))) - ((= (length modules) 1) - (let ((module (car modules))) - (unless (member module suggested-already) - (haskell-process-set-suggested-imports process (cons module suggested-already)) - (when (y-or-n-p (format "Identifier `%s' not in scope, import `%s'?" - ident - module)) - module))))))) +(defun haskell-process-suggest-imports (session file modules ident) + "Given a list of MODULES, suggest adding them to the import section." + (cl-assert session) + (cl-assert file) + (cl-assert ident) + (let* ((process (haskell-session-process session)) + (suggested-already (haskell-process-suggested-imports process)) + (module (cond ((> (length modules) 1) + (when (y-or-n-p (format "Identifier `%s' not in scope, choose module to import?" + ident)) + (haskell-complete-module-read "Module: " modules))) + ((= (length modules) 1) + (let ((module (car modules))) + (unless (member module suggested-already) + (haskell-process-set-suggested-imports process (cons module suggested-already)) + (when (y-or-n-p (format "Identifier `%s' not in scope, import `%s'?" + ident + module)) + module))))))) (when module (haskell-process-find-file session file) (save-excursion @@ -901,28 +906,24 @@ from `module-buffer'." "Given an out of scope identifier, Hoogle for that identifier, and if a result comes back, suggest to import that identifier now." - (let* ((process (haskell-session-process session)) - (suggested-already (haskell-process-suggested-imports process)) - (ident (let ((i (match-string 1 msg))) + (let* ((ident (let ((i (match-string 1 msg))) ;; Skip qualification. (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" i) (match-string 1 i) i))) (modules (haskell-process-hoogle-ident ident))) - (haskell-process-suggest-imports modules ident))) + (haskell-process-suggest-imports session file modules ident))) (defun haskell-process-suggest-hayoo-imports (session msg file) "Given an out of scope identifier, Hayoo for that identifier, and if a result comes back, suggest to import that identifier now." - (let* ((process (haskell-session-process session)) - (suggested-already (haskell-process-suggested-imports process)) - (ident (let ((i (match-string 1 msg))) + (let* ((ident (let ((i (match-string 1 msg))) ;; Skip qualification. (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" i) (match-string 1 i) i)))) - (haskell-process-hayoo-ident ident #'haskell-process-suggest-imports))) + (haskell-process-hayoo-ident session file ident #'haskell-process-suggest-imports))) (defun haskell-process-suggest-haskell-docs-imports (session msg file) @@ -981,9 +982,12 @@ now." (split-string (buffer-string) "\n")))))) -(defun haskell-process-hayoo-ident (ident callback) +(defun haskell-process-hayoo-ident (session file ident callback) "Hayoo for IDENT, returns a list of modules asyncronously through CALLBACK." - (setq haskell-process-hayoo-ident--ident ident + ;; This is a bit mysterious, but otherwise these are all unset + (setq haskell-process-hayoo-ident--session session + haskell-process-hayoo-ident--file file + haskell-process-hayoo-ident--ident ident haskell-process-hayoo-ident--callback callback) (url-retrieve (format haskell-process-hayoo-query-url ident) @@ -991,9 +995,15 @@ now." (re-search-forward "\r?\n\r?\n") (let* ((res (json-read-object)) (results (assoc-default 'result res)) + ;; TODO: gather packages as well, and when we choose a + ;; given import, check that we have the package in the + ;; cabal file as well. (modules-l (cl-mapcar (lambda (r) (cl-mapcar 'identity (assoc-default 'resultModules r))) results)) (modules (apply #'append modules-l) )) - (funcall haskell-process-hayoo-ident--callback modules + (funcall haskell-process-hayoo-ident--callback + haskell-process-hayoo-ident--session + haskell-process-hayoo-ident--file + modules haskell-process-hayoo-ident--ident))))) (defun haskell-process-suggest-remove-import (session file import line) From 070cb97e6f73ee32b41cd9335cbd7b3f9af3a743 Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Wed, 15 Oct 2014 01:43:41 +0200 Subject: [PATCH 3/8] Add dummy defvars. --- haskell-process.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/haskell-process.el b/haskell-process.el index 493f0249c..d5ee3deb4 100644 --- a/haskell-process.el +++ b/haskell-process.el @@ -982,6 +982,10 @@ now." (split-string (buffer-string) "\n")))))) +(defvar haskell-process-hayoo-ident--session) +(defvar haskell-process-hayoo-ident--file) +(defvar haskell-process-hayoo-ident--ident) +(defvar haskell-process-hayoo-ident--callback) (defun haskell-process-hayoo-ident (session file ident callback) "Hayoo for IDENT, returns a list of modules asyncronously through CALLBACK." ;; This is a bit mysterious, but otherwise these are all unset From c69ab3652eece1572606cd562ffa2184601f4069 Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Wed, 15 Oct 2014 01:53:35 +0200 Subject: [PATCH 4/8] require json --- haskell-process.el | 1 + 1 file changed, 1 insertion(+) diff --git a/haskell-process.el b/haskell-process.el index d5ee3deb4..e95c1a6c1 100644 --- a/haskell-process.el +++ b/haskell-process.el @@ -28,6 +28,7 @@ ;;; Code: (require 'cl-lib) +(require 'json) (require 'haskell-complete-module) (require 'haskell-mode) (require 'haskell-session) From 40b42c216c5d736def332170eacbccaa1c1923f5 Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Wed, 15 Oct 2014 12:05:38 +0200 Subject: [PATCH 5/8] Simplify and fixes. --- haskell-process.el | 113 +++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 81 deletions(-) diff --git a/haskell-process.el b/haskell-process.el index e95c1a6c1..d529acdc3 100644 --- a/haskell-process.el +++ b/haskell-process.el @@ -29,6 +29,7 @@ (require 'cl-lib) (require 'json) +(require 'url-util) (require 'haskell-complete-module) (require 'haskell-mode) (require 'haskell-session) @@ -850,12 +851,19 @@ from `module-buffer'." (when haskell-process-suggest-overloaded-strings (haskell-process-suggest-pragma session "LANGUAGE" "OverloadedStrings" file))) ((string-match "^Not in scope: .*[‘`‛]\\(.+\\)['’]$" msg) - (when haskell-process-suggest-hoogle-imports - (haskell-process-suggest-hoogle-imports session msg file)) - (when haskell-process-suggest-hayoo-imports - (haskell-process-suggest-hayoo-imports session msg file)) - (when haskell-process-suggest-haskell-docs-imports - (haskell-process-suggest-haskell-docs-imports session msg file))) + (let* ((match1 (match-string 1 msg)) + (ident (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" match1) + ;; Skip qualification. + (match-string 1 match1) + match1))) + (when haskell-process-suggest-hoogle-imports + (let ((modules (haskell-process-hoogle-ident ident))) + (haskell-process-suggest-imports session file modules ident))) + (when haskell-process-suggest-hayoo-imports + (haskell-process-hayoo-ident session file ident #'haskell-process-suggest-imports)) + (when haskell-process-suggest-haskell-docs-imports + (let ((modules (haskell-process-haskell-docs-ident ident))) + (haskell-process-suggest-imports session file modules ident))))) ((string-match "^[ ]+It is a member of the hidden package [‘`‛]\\(.+\\)['’].$" msg) (when haskell-process-suggest-add-package (haskell-process-suggest-add-package session msg))))) @@ -903,66 +911,6 @@ from `module-buffer'." (haskell-sort-imports) (haskell-align-imports))))) -(defun haskell-process-suggest-hoogle-imports (session msg file) - "Given an out of scope identifier, Hoogle for that identifier, -and if a result comes back, suggest to import that identifier -now." - (let* ((ident (let ((i (match-string 1 msg))) - ;; Skip qualification. - (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" i) - (match-string 1 i) - i))) - (modules (haskell-process-hoogle-ident ident))) - (haskell-process-suggest-imports session file modules ident))) - -(defun haskell-process-suggest-hayoo-imports (session msg file) - "Given an out of scope identifier, Hayoo for that identifier, -and if a result comes back, suggest to import that identifier -now." - (let* ((ident (let ((i (match-string 1 msg))) - ;; Skip qualification. - (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" i) - (match-string 1 i) - i)))) - (haskell-process-hayoo-ident session file ident #'haskell-process-suggest-imports))) - - -(defun haskell-process-suggest-haskell-docs-imports (session msg file) - "Given an out of scope identifier, haskell-docs search for that identifier, -and if a result comes back, suggest to import that identifier -now." - (let* ((process (haskell-session-process session)) - (suggested-already (haskell-process-suggested-imports process)) - (ident (let ((i (match-string 1 msg))) - ;; Skip qualification. - (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" i) - (match-string 1 i) - i))) - (modules (haskell-process-haskell-docs-ident ident)) - (module - (cond - ((> (length modules) 1) - (when (y-or-n-p (format "Identifier `%s' not in scope, choose module to import?" - ident)) - (haskell-complete-module-read "Module: " modules))) - ((= (length modules) 1) - (let ((module (car modules))) - (unless (member module suggested-already) - (haskell-process-set-suggested-imports process (cons module suggested-already)) - (when (y-or-n-p (format "Identifier `%s' not in scope, import `%s'?" - ident - module)) - module))))))) - (when module - (haskell-process-find-file session file) - (save-excursion - (goto-char (point-max)) - (haskell-navigate-imports) - (insert (read-from-minibuffer "Import line: " (concat "import " module)) - "\n") - (haskell-sort-imports) - (haskell-align-imports))))) - (defun haskell-process-haskell-docs-ident (ident) "Search with haskell-docs for IDENT, returns a list of modules." (cl-remove-if-not (lambda (a) (string-match "^[A-Z][A-Za-b0-9_'.]+$" a)) @@ -995,21 +943,24 @@ now." haskell-process-hayoo-ident--ident ident haskell-process-hayoo-ident--callback callback) (url-retrieve - (format haskell-process-hayoo-query-url ident) - (lambda (status) - (re-search-forward "\r?\n\r?\n") - (let* ((res (json-read-object)) - (results (assoc-default 'result res)) - ;; TODO: gather packages as well, and when we choose a - ;; given import, check that we have the package in the - ;; cabal file as well. - (modules-l (cl-mapcar (lambda (r) (cl-mapcar 'identity (assoc-default 'resultModules r))) results)) - (modules (apply #'append modules-l) )) - (funcall haskell-process-hayoo-ident--callback - haskell-process-hayoo-ident--session - haskell-process-hayoo-ident--file - modules - haskell-process-hayoo-ident--ident))))) + (format haskell-process-hayoo-query-url (url-hexify-string ident)) + (lambda (status) + (message "Hayoo server returned a result") + (re-search-forward "\r?\n\r?\n") + (let* ((res (setq asdf0 (json-read-object))) + (results (setq asdf (assoc-default 'result res))) + ;; TODO: gather packages as well, and when we choose a + ;; given import, check that we have the package in the + ;; cabal file as well. + (modules (cl-mapcan (lambda (r) + ;; append converts from vector -> list + (append (assoc-default 'resultModules r) nil)) + results))) + (funcall haskell-process-hayoo-ident--callback + haskell-process-hayoo-ident--session + haskell-process-hayoo-ident--file + modules + haskell-process-hayoo-ident--ident))))) (defun haskell-process-suggest-remove-import (session file import line) "Suggest removing or commenting out IMPORT on LINE." From 602401850ed40e7b3a846e32a6615216de590f56 Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Wed, 15 Oct 2014 15:41:23 +0200 Subject: [PATCH 6/8] Removed debug setqs. --- haskell-process.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/haskell-process.el b/haskell-process.el index d529acdc3..f2df0fda3 100644 --- a/haskell-process.el +++ b/haskell-process.el @@ -947,8 +947,8 @@ from `module-buffer'." (lambda (status) (message "Hayoo server returned a result") (re-search-forward "\r?\n\r?\n") - (let* ((res (setq asdf0 (json-read-object))) - (results (setq asdf (assoc-default 'result res))) + (let* ((res (json-read-object)) + (results (assoc-default 'result res)) ;; TODO: gather packages as well, and when we choose a ;; given import, check that we have the package in the ;; cabal file as well. From 06ba36575e7806c22aca567e7e9dd96ad738bd29 Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Wed, 15 Oct 2014 23:45:49 +0200 Subject: [PATCH 7/8] Use lexical-let. --- haskell-process.el | 47 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/haskell-process.el b/haskell-process.el index f2df0fda3..a8f40e15a 100644 --- a/haskell-process.el +++ b/haskell-process.el @@ -931,36 +931,29 @@ from `module-buffer'." (split-string (buffer-string) "\n")))))) -(defvar haskell-process-hayoo-ident--session) -(defvar haskell-process-hayoo-ident--file) -(defvar haskell-process-hayoo-ident--ident) -(defvar haskell-process-hayoo-ident--callback) (defun haskell-process-hayoo-ident (session file ident callback) "Hayoo for IDENT, returns a list of modules asyncronously through CALLBACK." ;; This is a bit mysterious, but otherwise these are all unset - (setq haskell-process-hayoo-ident--session session - haskell-process-hayoo-ident--file file - haskell-process-hayoo-ident--ident ident - haskell-process-hayoo-ident--callback callback) - (url-retrieve - (format haskell-process-hayoo-query-url (url-hexify-string ident)) - (lambda (status) - (message "Hayoo server returned a result") - (re-search-forward "\r?\n\r?\n") - (let* ((res (json-read-object)) - (results (assoc-default 'result res)) - ;; TODO: gather packages as well, and when we choose a - ;; given import, check that we have the package in the - ;; cabal file as well. - (modules (cl-mapcan (lambda (r) - ;; append converts from vector -> list - (append (assoc-default 'resultModules r) nil)) - results))) - (funcall haskell-process-hayoo-ident--callback - haskell-process-hayoo-ident--session - haskell-process-hayoo-ident--file - modules - haskell-process-hayoo-ident--ident))))) + + (lexical-let ((session session) + (file file) + (ident ident) + (callback callback)) + (url-retrieve + (format haskell-process-hayoo-query-url (url-hexify-string ident)) + (lambda (status) + (message "Hayoo server returned a result") + (re-search-forward "\r?\n\r?\n") + (let* ((res (json-read-object)) + (results (assoc-default 'result res)) + ;; TODO: gather packages as well, and when we choose a + ;; given import, check that we have the package in the + ;; cabal file as well. + (modules (cl-mapcan (lambda (r) + ;; append converts from vector -> list + (append (assoc-default 'resultModules r) nil)) + results))) + (funcall callback session file modules ident)))))) (defun haskell-process-suggest-remove-import (session file import line) "Suggest removing or commenting out IMPORT on LINE." From f08177d14c815aaa12a7e69fe55b4c0af5b6db85 Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Thu, 16 Oct 2014 12:27:14 +0200 Subject: [PATCH 8/8] Require 'cl during compilation for lexical-let. --- haskell-process.el | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/haskell-process.el b/haskell-process.el index a8f40e15a..a5f77c5c8 100644 --- a/haskell-process.el +++ b/haskell-process.el @@ -28,6 +28,8 @@ ;;; Code: (require 'cl-lib) +;; For lexical-let. TODO: Remove when converting to lexical bindings +(eval-when-compile (require 'cl)) (require 'json) (require 'url-util) (require 'haskell-complete-module) @@ -933,27 +935,30 @@ from `module-buffer'." (defun haskell-process-hayoo-ident (session file ident callback) "Hayoo for IDENT, returns a list of modules asyncronously through CALLBACK." - ;; This is a bit mysterious, but otherwise these are all unset - + ;; We need a real/simulated closure, because otherwise these + ;; variables will be unbound when the url-retrieve callback is + ;; called. + ;; TODO: Remove when this code is converted to lexical bindings by + ;; default (Emacs 24.1+) (lexical-let ((session session) (file file) (ident ident) (callback callback)) - (url-retrieve - (format haskell-process-hayoo-query-url (url-hexify-string ident)) - (lambda (status) - (message "Hayoo server returned a result") - (re-search-forward "\r?\n\r?\n") - (let* ((res (json-read-object)) - (results (assoc-default 'result res)) - ;; TODO: gather packages as well, and when we choose a - ;; given import, check that we have the package in the - ;; cabal file as well. - (modules (cl-mapcan (lambda (r) - ;; append converts from vector -> list - (append (assoc-default 'resultModules r) nil)) - results))) - (funcall callback session file modules ident)))))) + (url-retrieve + (format haskell-process-hayoo-query-url (url-hexify-string ident)) + (lambda (status) + (message "Hayoo server returned a result") + (re-search-forward "\r?\n\r?\n") + (let* ((res (json-read-object)) + (results (assoc-default 'result res)) + ;; TODO: gather packages as well, and when we choose a + ;; given import, check that we have the package in the + ;; cabal file as well. + (modules (cl-mapcan (lambda (r) + ;; append converts from vector -> list + (append (assoc-default 'resultModules r) nil)) + results))) + (funcall callback session file modules ident)))))) (defun haskell-process-suggest-remove-import (session file import line) "Suggest removing or commenting out IMPORT on LINE."