branch: elpa/haskell-tng-mode commit 599d4f0e9d1dbae34d58a7ea6d966cf796c3ea1e Author: Tseen She <ts33n....@gmail.com> Commit: Tseen She <ts33n....@gmail.com>
import symbol at point --- README.md | 2 +- haskell-tng-extra.el | 12 +++++++++-- haskell-tng-hsinspect.el | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ haskell-tng-util.el | 18 +++++++++++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a778405..6435488 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ You must install `hsinspect` for every version of `ghc` that you plan to use, e. ``` rm -f ~/.cabal/bin/hsinspect for V in 8.4.4 8.6.5 ; do - cabal v2-install hsinspect-0.0.6 -w ghc-$V -O2 && + cabal v2-install hsinspect-0.0.7 -w ghc-$V -O2 && mv -f ~/.cabal/bin/hsinspect ~/.cabal/bin/hsinspect-ghc-$V done ``` diff --git a/haskell-tng-extra.el b/haskell-tng-extra.el index 48fe0da..5f7d5ac 100644 --- a/haskell-tng-extra.el +++ b/haskell-tng-extra.el @@ -93,9 +93,17 @@ 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 imenu navigation will replace this (interactive) - (re-search-backward (rx line-start "import" word-end))) + (goto-char (point-min)) + (re-search-forward (rx line-start "import" word-end)) + (forward-line 0)) + +;;;###autoload +(defun haskell-tng-import-module (module) + "Adds an unqualified wildcard import." + ;; TODO autocomplete on available imports + (interactive "s") + (haskell-tng--import-symbol module nil nil)) ;;;###autoload (defun haskell-tng-current-module () diff --git a/haskell-tng-hsinspect.el b/haskell-tng-hsinspect.el index 202fd95..45332a2 100644 --- a/haskell-tng-hsinspect.el +++ b/haskell-tng-hsinspect.el @@ -20,6 +20,7 @@ ;; centric. (require 'haskell-tng-compile) +(require 'haskell-tng-util) ;;;###autoload (defun haskell-tng-fqn-at-point () @@ -37,6 +38,46 @@ name of the symbol at point in the minibuffer." (error "hsinspect is not available") (message "<not imported>")))) +;;;###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) + (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))) + (haskell-tng--import-symbol (car hit) nil (cdr hit)))))) + +(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) + (seq-mapcat + (lambda (module-entry) + (let ((module (alist-get 'module module-entry)) + (ids (alist-get 'ids module-entry))) + ;;(message "MODULE= %s" module) + (seq-mapcat + (lambda (entry) + (let ((name (alist-get 'name entry))) + (when (equal name sym) `(,(cons module name))))) + ids))) + modules))) + index)) + (defun haskell-tng--hsinspect-symbol-at-point () "A `symbol-at-point' that includes FQN parts." (buffer-substring-no-properties @@ -87,6 +128,17 @@ 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 +;; (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) + (if (or lookup-only haskell-tng--hsinspect-index) + (unless (eq t haskell-tng--hsinspect-index) + haskell-tng--hsinspect-index) + (setq haskell-tng--hsinspect-index t) ;; avoid races + (setq haskell-tng--hsinspect-index + (haskell-tng--hsinspect "index")))) + (defun haskell-tng--hsinspect (&rest params) (ignore-errors (kill-buffer "*hsinspect*")) (when-let ((ghcflags (haskell-tng--hsinspect-ghcflags)) diff --git a/haskell-tng-util.el b/haskell-tng-util.el index 041cec6..fb9a367 100644 --- a/haskell-tng-util.el +++ b/haskell-tng-util.el @@ -70,5 +70,23 @@ and taking a regexp." (while (not (setq ,res ,test)) ,@body) ,res))) +(defun haskell-tng--import-symbol (module as 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"))) + (provide 'haskell-tng-util) ;;; haskell-tng-util.el ends here