diff --git a/haskell-completions.el b/haskell-completions.el index 5405f2ff4..54e8327cd 100644 --- a/haskell-completions.el +++ b/haskell-completions.el @@ -1,6 +1,6 @@ ;;; haskell-completions.el --- Haskell Completion package -*- lexical-binding: t -*- -;; Copyright © 2015 Athur Fayzrakhmanov. All rights reserved. +;; Copyright © 2015-2016 Athur Fayzrakhmanov. All rights reserved. ;; This file is part of haskell-mode package. ;; You can contact with authors using GitHub issue tracker: @@ -40,7 +40,7 @@ (require 'haskell-process) (require 'haskell-interactive-mode) -(defvar haskell-completions-pragma-names +(defvar haskell-completions--pragma-names (list "DEPRECATED" "INCLUDE" "INCOHERENT" @@ -63,8 +63,51 @@ "WARNING") "A list of supported pragmas. This list comes from GHC documentation (URL -`https://downloads.haskell.org/~ghc/7.10.1/docs/html/users_guide/pragmas.html'. -") +`https://downloads.haskell.org/~ghc/7.10.1/docs/html/users_guide/pragmas.html'.") + +(defvar haskell-completions--keywords + (list + "as" + "case" + "class" + "data family" + "data instance" + "data" + "default" + "deriving instance" + "deriving" + "do" + "else" + "family" + "forall" + "foreign import" + "foreign" + "hiding" + "if" + "import qualified" + "import" + "in" + "infix" + "infixl" + "infixr" + "instance" + "let" + "mdo" + "module" + "newtype" + "of" + "proc" + "qualified" + "rec" + "then" + "type family" + "type instance" + "type" + "where") + "A list of Haskell's keywords (URL `https://wiki.haskell.org/Keywords'). +Single char keywords and operator like keywords are not included +in this list.") + (defun haskell-completions-can-grab-prefix () "Check if the case is appropriate for grabbing completion prefix. @@ -206,9 +249,20 @@ identifier at point depending on result of function Returns a list of form '(prefix-start-position prefix-end-position prefix-value prefix-type) depending on situation, e.g. is it needed to complete pragma, module name, -arbitrary identifier, etc. Returns nil in case it is +arbitrary identifier, etc. Returns nil in case it is impossible to grab prefix. +Possible prefix types are: + +* haskell-completions-pragma-name-prefix +* haskell-completions-ghc-option-prefix +* haskell-completions-language-extension-prefix +* haskell-completions-module-name-prefix +* haskell-completions-identifier-prefix +* haskell-completions-general-prefix + +the last type is used in cases when completing things inside comments. + If provided optional MINLEN parameter this function will return result only if prefix length is not less than MINLEN." (when (haskell-completions-can-grab-prefix) @@ -220,36 +274,77 @@ result only if prefix length is not less than MINLEN." prefix)) (prefix prefix))))) +(defun haskell-completions--simple-completions (prefix) + "Provide a list of completion candidates for given PREFIX. +This function is used internally in +`haskell-completions-completion-at-point' and +`haskell-completions-sync-repl-completion-at-point'. + +It provides completions for haskell keywords, language pragmas, +GHC's options, and language extensions. + +PREFIX should be a list such one returned by +`haskell-completions-grab-identifier-prefix'." + (cl-destructuring-bind (beg end _pfx typ) prefix + (let ((candidates + (cl-case typ + ('haskell-completions-pragma-name-prefix + haskell-completions--pragma-names) + ('haskell-completions-ghc-option-prefix + haskell-ghc-supported-options) + ('haskell-completions-language-extension-prefix + haskell-ghc-supported-extensions) + (otherwise + haskell-completions--keywords)))) + (list beg end candidates)))) + + +(defun haskell-completions-completion-at-point () + "Provide completion list for thing at point. +This function is used in non-interactive `haskell-mode'. It +provides completions for haskell keywords, language pragmas, +GHC's options, and language extensions, but not identifiers." + (let ((prefix (haskell-completions-grab-prefix))) + (haskell-completions--simple-completions prefix))) + +(defun haskell-completions-sync-repl-completion-at-point () + "A completion function used in `interactive-haskell-mode'. +Completion candidates are provided quering current haskell +process, that is sending `:complete repl' command. + +Completes all possible things: everything that can be completed +with non-interactive function +`haskell-completions-completion-at-point' plus identifier +completions. -(defun haskell-completions-sync-completions-at-point () - "A `completion-at-point' function using the current haskell process. Returns nil if no completions available." (let ((prefix-data (haskell-completions-grab-prefix))) (when prefix-data (cl-destructuring-bind (beg end pfx typ) prefix-data - (let ((imp (eql typ 'haskell-completions-module-name-prefix)) - lst) - (setq lst - (cl-case typ - ;; non-interactive completions first - ('haskell-completions-pragma-name-prefix - haskell-completions-pragma-names) - ('haskell-completions-ghc-option-prefix - haskell-ghc-supported-options) - ('haskell-completions-language-extension-prefix - haskell-ghc-supported-extensions) - (otherwise - (when (and - (not (eql typ 'haskell-completions-general-prefix)) - (haskell-session-maybe) - (not - (haskell-process-cmd (haskell-interactive-process)))) - ;; if REPL is available and not busy try to query it - ;; for completions list in case of module name or - ;; identifier prefixes - (haskell-completions-sync-complete-repl pfx imp))))) - (when lst - (list beg end lst))))))) + (when (not (eql typ 'haskell-completions-general-prefix)) + ;; do not complete things in comments + (if (cl-member + typ + '(haskell-completions-pragma-name-prefix + haskell-completions-ghc-option-prefix + haskell-completions-language-extension-prefix)) + ;; provide simple completions + (haskell-completions--simple-completions prefix-data) + ;; only two cases left: haskell-completions-module-name-prefix + ;; and haskell-completions-identifier-prefix + (let* ((is-import (eql typ 'haskell-completions-module-name-prefix)) + (candidates + (when (and (haskell-session-maybe) + (not (haskell-process-cmd + (haskell-interactive-process)))) + ;; if REPL is available and not busy try to query it for + ;; completions list in case of module name or identifier + ;; prefixes + (haskell-completions-sync-complete-repl pfx is-import)))) + ;; append candidates with keywords + (list beg end (append + candidates + haskell-completions--keywords))))))))) (defun haskell-completions-sync-complete-repl (prefix &optional import) "Return completion list for given PREFIX querying REPL synchronously. diff --git a/haskell-interactive-mode.el b/haskell-interactive-mode.el index 20b667a1c..5135f9ea2 100644 --- a/haskell-interactive-mode.el +++ b/haskell-interactive-mode.el @@ -1016,7 +1016,8 @@ don't care when the thing completes as long as it's soonish." (remove-overlays)))) (defun haskell-interactive-mode-completion-at-point-function () - "Offer completions for partial expression between prompt and point" + "Offer completions for partial expression between prompt and point. +This completion function is used in interactive REPL buffer itself." (when (haskell-interactive-at-prompt) (let* ((process (haskell-interactive-process)) (inp (haskell-interactive-mode-input-partial)) diff --git a/haskell-mode.el b/haskell-mode.el index e19a37369..c76fb7ca1 100644 --- a/haskell-mode.el +++ b/haskell-mode.el @@ -783,6 +783,11 @@ Minor modes that work well with `haskell-mode': (setq haskell-literate nil) (add-hook 'before-save-hook 'haskell-mode-before-save-handler nil t) (add-hook 'after-save-hook 'haskell-mode-after-save-handler nil t) + ;; provide non-interactive completion function + (add-hook 'completion-at-point-functions + #'haskell-completions-completion-at-point + nil + t) (haskell-indentation-mode)) (defun haskell-fill-paragraph (justify) diff --git a/haskell.el b/haskell.el index 0ffaa9643..f977fd206 100644 --- a/haskell.el +++ b/haskell.el @@ -67,12 +67,12 @@ :lighter " Interactive" :keymap interactive-haskell-mode-map (add-hook 'completion-at-point-functions - #'haskell-completions-sync-completions-at-point + #'haskell-completions-sync-repl-completion-at-point nil t)) (make-obsolete 'haskell-process-completions-at-point - 'haskell-completions-sync-completions-at-point + 'haskell-completions-sync-repl-completion-at-point "June 19, 2015") (defun haskell-process-completions-at-point () "A completion-at-point function using the current haskell process."