branch: elpa/haskell-tng-mode commit 26c6828ccbbbaf20d4b4626d9be7453dbb065809 Author: Tseen She <ts33n....@gmail.com> Commit: Tseen She <ts33n....@gmail.com>
fqn imports --- README.md | 1 + haskell-tng-compile.el | 5 ----- haskell-tng-extra-company.el | 4 ++-- haskell-tng-extra-projectile.el | 5 +++++ haskell-tng-extra-stack.el | 12 ++++++++++-- haskell-tng-extra.el | 2 ++ haskell-tng-hsinspect.el | 36 ++++++++++++++++++++++-------------- haskell-tng-util.el | 34 ++++++++++++++++++++-------------- 8 files changed, 62 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 34a2cf1..207ae63 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ A full installation may look like the following (require 'haskell-tng-extra-projectile) (require 'haskell-tng-extra-smartparens) (require 'haskell-tng-extra-yasnippet) + ;; (require 'haskell-tng-extra-stack) :bind (:map diff --git a/haskell-tng-compile.el b/haskell-tng-compile.el index 322ff84..1faf187 100644 --- a/haskell-tng-compile.el +++ b/haskell-tng-compile.el @@ -67,9 +67,6 @@ (defvar-local haskell-tng--compile-command nil) (defvar-local haskell-tng--compile-alt "cabal v2-clean") -(defvar haskell-tng--compile-dominating-project - ;; TODO move stack.yaml to extra-stack - (rx (| "cabal.project" "cabal.project.local" "cabal.project.freeze" "stack.yaml"))) (defvar haskell-tng--compile-dominating-package (rx (| (: (+ any) ".cabal") "package.yaml"))) @@ -108,8 +105,6 @@ will cause the subsequent call to prompt." (or (haskell-tng--util-locate-dominating-file haskell-tng--compile-dominating-package) - (haskell-tng--util-locate-dominating-file - haskell-tng--compile-dominating-project) default-directory))) (compilation-start command diff --git a/haskell-tng-extra-company.el b/haskell-tng-extra-company.el index f5ad1c1..2c8cf62 100644 --- a/haskell-tng-extra-company.el +++ b/haskell-tng-extra-company.el @@ -21,8 +21,8 @@ (require 'haskell-tng-hsinspect) (require 'haskell-tng-rx) -;; TODO completions for imports (needs to know project wide module list) -;; TODO completions for symbols (can this be made more contextual) +;; TODO completions for modules in imports +;; TODO completions for explicits in imports (defcustom haskell-tng-company-backends '(company-files diff --git a/haskell-tng-extra-projectile.el b/haskell-tng-extra-projectile.el index b5fd9af..7b59b55 100644 --- a/haskell-tng-extra-projectile.el +++ b/haskell-tng-extra-projectile.el @@ -12,6 +12,11 @@ ;; TODO fix the haskell-stack detection to also include cabal ;; TODO populate the projectile compile/run/test commands +;; ;; Not guaranteed to exist for cabal-install, always fall back to +;; ;; dominating-package when searching for the dominating project. +;; (defvar haskell-tng--compile-dominating-project +;; (rx (| "cabal.project" "cabal.project.local" "cabal.project.freeze"))) + (add-hook 'haskell-tng-mode-hook (lambda () diff --git a/haskell-tng-extra-stack.el b/haskell-tng-extra-stack.el index 1d67fd0..bce8f0a 100644 --- a/haskell-tng-extra-stack.el +++ b/haskell-tng-extra-stack.el @@ -9,8 +9,16 @@ ;; ;;; Code: -;; TODO document -;; TODO implement +(require 'haskell-tng-compile) +(require 'haskell-tng-hsinspect) + +(setq + haskell-tng--hsinspect-which-hsinspect "stack exec --silent which -- hsinspect" + haskell-tng--compile-history '("stack build --fast --no-run-tests --ghc-options=\"-j\"" + "stack build --fast --ghc-options=\"-j\"")) + +(setq-default + haskell-tng--compile-alt "stack clean") (provide 'haskell-tng-extra-stack) ;;; haskell-tng-extra-stack.el ends here diff --git a/haskell-tng-extra.el b/haskell-tng-extra.el index c019552..58774ea 100644 --- a/haskell-tng-extra.el +++ b/haskell-tng-extra.el @@ -103,7 +103,9 @@ When in a comment and called with a prefix, the comment will be completed." ;;;###autoload (defun haskell-tng-goto-imports () "Hack to jump to imports" + ;; TODO is this useful? (interactive) + ;; TODO comment / text resilience (goto-char (point-min)) (re-search-forward (rx line-start "import" word-end)) (forward-line 0)) diff --git a/haskell-tng-hsinspect.el b/haskell-tng-hsinspect.el index 0a9caf7..87daf28 100644 --- a/haskell-tng-hsinspect.el +++ b/haskell-tng-hsinspect.el @@ -11,10 +11,13 @@ ;; ;;; Code: +;; TODO this file needs tests, if not testing calling hsinspect then at least +;; with pre-canned data. + (require 'subr-x) (require 'popup) -;; TODO remove the dependency on third party "popup". Unfortunately this is +;; FIXME remove the dependency on third party "popup". Unfortunately this is ;; blocked on Emacs shipping with a usable menu and tooltip library. ;; `tooltip-show' and `popup-menu' are mouse centric whereas we need `point' ;; centric. https://emacs.stackexchange.com/questions/53373 @@ -41,33 +44,38 @@ name of the symbol at point in the minibuffer." ;;;###autoload (defun haskell-tng-import-symbol-at-point () "Import the symbol at point" - ;; TODO import FQN as qualified module ;; TODO prefix + FQN should mean use unqualified `as' import ;; TODO prefix + unqualified should mean to import entire module ;; TODO shortlist for FQN imports (no need to calc the index) (interactive) + ;; TODO update the hsinspect-imports cache (when-let* ((index (haskell-tng--hsinspect-index)) (sym (haskell-tng--hsinspect-symbol-at-point))) (message "Seaching for '%s' in %s modules" sym (length index)) - (when-let ((hits (haskell-tng--hsinspect-import-candidates index sym))) - ;; TODO special case one hit - (when-let* ((entries (mapcar 'car hits)) ;; TODO include function name - (selected (popup-menu* entries)) - (hit (seq-find (lambda (el) (equal (car el) selected)) hits))) - ;; TODO update the hsinspect-imports cache + (if (string-match (rx bos (group (+ anything)) "." (group (+ (not (any ".")))) eos) sym) + (let* ((fqn (match-string 1 sym)) + (sym (match-string 2 sym))) + (when-let (hit (haskell-tng--hsinspect-import-popup index sym)) + (haskell-tng--import-symbol (car hit) fqn))) + (when-let (hit (haskell-tng--hsinspect-import-popup index sym)) ;; TODO add parens around operators ;; TODO add the type around data constructors (requires hsinspect changes) (haskell-tng--import-symbol (car hit) nil (cdr hit)))))) +(defun haskell-tng--hsinspect-import-popup (index sym) + (when-let ((hits (haskell-tng--hsinspect-import-candidates index sym))) + ;; TODO special case one hit + (when-let* ((entries (mapcar 'car hits)) ;; TODO include function name + (selected (popup-menu* entries))) + (seq-find (lambda (el) (equal (car el) selected)) hits)))) + (defun haskell-tng--hsinspect-import-candidates (index sym) "Return a list of (module . symbol)" ;; TODO threading/do syntax ;; TODO alist variable binding like RecordWildcards (seq-mapcat (lambda (pkg-entry) - (let ((modules (alist-get 'modules pkg-entry)) - (unitid (alist-get 'unitid pkg-entry))) - ;; (message "UNITID= %s" unitid) + (let ((modules (alist-get 'modules pkg-entry))) (seq-mapcat (lambda (module-entry) (let ((module (alist-get 'module module-entry)) @@ -108,7 +116,7 @@ name of the symbol at point in the minibuffer." (user-error "could not find `.ghc.flags'."))) ;; TODO invalidate cache when imports section has changed -;; TODO cache per file (timestamp based, for optimal browsing) +;; FIXME cache per file (timestamp based, for optimal browsing) (defvar-local haskell-tng--hsinspect-imports nil "Cache for the last `imports' call for this buffer. t means the process failed.") @@ -120,7 +128,7 @@ t means the process failed.") (setq haskell-tng--hsinspect-imports (haskell-tng--hsinspect "imports" buffer-file-name)))) -;; TODO this can be more efficiently cached alongside the .ghc.flags file, not per source file +;; FIXME this can be more efficiently cached alongside the .ghc.flags file, not per source file ;; (it's also fast to load so maybe persist it in a cache dir and check timestamps) (defvar-local haskell-tng--hsinspect-index nil) (defun haskell-tng--hsinspect-index (&optional lookup-only) @@ -131,7 +139,7 @@ t means the process failed.") (setq haskell-tng--hsinspect-index (haskell-tng--hsinspect "index")))) -;; TODO cache per project +;; FIXME cache per project (or package at least) (defvar-local haskell-tng--hsinspect-exe nil) (defvar haskell-tng--hsinspect-which-hsinspect "cabal exec -v0 which -- hsinspect") diff --git a/haskell-tng-util.el b/haskell-tng-util.el index fb9a367..96bd3f0 100644 --- a/haskell-tng-util.el +++ b/haskell-tng-util.el @@ -70,23 +70,29 @@ and taking a regexp." (while (not (setq ,res ,test)) ,@body) ,res))) -(defun haskell-tng--import-symbol (module as sym) +(defun haskell-tng--import-symbol (module as &optional sym) "Adds an import for MODULE." ;; TODO outsource to `hsimport' when it does de-duping and formatting. (save-excursion - (haskell-tng-goto-imports) - (insert - "import " - (cond - ((and (null as) (null sym)) - module) - ((null as) - (concat module " (" sym ")")) - ((eq t as) - (concat "qualified " module)) - (t - (concat "qualified " module " as " as))) - "\n"))) + (goto-char (point-min)) + ;; TODO comment / text resilience + (if (re-search-forward (rx line-start "import" word-end) nil t) + (forward-line 0) + (re-search-forward (rx line-start "module" word-end)) + (forward-line 1) + (insert "\n")) + (insert + "import " + (cond + ((and (null as) (null sym)) + module) + ((null as) + (concat module " (" sym ")")) + ((eq t as) + (concat "qualified " module)) + (t + (concat "qualified " module " as " as))) + "\n"))) (provide 'haskell-tng-util) ;;; haskell-tng-util.el ends here