jyp / dante

389 stars 52 forks source link

Jump to definition and Info unreliable. Configuration of test targets hard to get right #147

Closed freckletonj closed 1 year ago

freckletonj commented 4 years ago

This is my 2nd time trying this out, emmigrating from Intero. It seems to install nicely, no errors, but key features aren't working, and no debug information seems to be logged anywhere.

Weirdly, flycheck, C-c , and C-c " work just fine. (EDIT: sometimes)

This looks related to: https://github.com/jyp/dante/issues/137 and https://github.com/jyp/dante/issues/78#issuecomment-413336831, but neither solves it.

Features broken:

M-.  ::  No definitions found for: "/tmp/danteTqJJvj.hs" ...
M-?  ::  1.) in helm: "Find references of (default "/tmp/dante..." 12 12 12 12 functionName) 
         2. ) No references found for: "/tmp/dante..." ...
C-c .  ::  Couldn't guess that module name. Does it exist?

Details:

# Dante Version
dante              20200131.1211 installed             Development mode for Haskell

# OS
Debian Buster

# Emacs version
26.1

# Stack version
2.1.3, rev 636e3a75...
# dante-diagnose

default-directory "<correct project root>"
dante-command-line ("stack" "repl")
dante-state (loaded "Prelude Domain.Analysis")
dante-queue nil
dante-loaded-file "<project root>/Domain/Analysis.hs"
dante-load-message nil
lcr-process-callback nil
; init.el

(use-package dante
  :ensure t
  :after haskell-mode
  :commands 'dante-mode
  :init

  ;; Dante can only check *saved* buffers, make sure flycheck gets
  ;; along. Disable checks on idle after change.
  (add-hook 'haskell-mode-hook 'flycheck-mode)
  (setq flymake-no-changes-timeout nil)
  (setq flymake-start-syntax-check-on-newline nil)
  (setq flycheck-check-syntax-automatically '(save mode-enabled))

  ;; HLint
  (add-hook 'dante-mode-hook
            '(lambda () (flycheck-add-next-checker 'haskell-dante
                                                   '(warning . haskell-hlint))))

  ;; Dante
  (add-hook 'haskell-mode-hook 'dante-mode)
  )
; .dir-locals.el

((nil . ((dante-methods . (stack)))))

EDIT0: These features do work in a stack script, but not in a full fledged stack project.

EDIT1: I was wondering if stack was running in the wrong place, so I tried C-c " on the following:

-- >>> :! ls
-- <LISTS MY ROOT>

Not sure how else to debug this, it looks like it has the right root.

EDIT2: Just to be sure I've got the latest, I git cloned the latest to use it locally instead of from MELPA. Same bugs apply on the latest master (4955bc7363e250d22579bc34b0b4ab6611c0766c).

EDIT3: If I turn off Dante all together, xref-find-references works on its own, maybe it's just haskell-mode that's doing that? It's not intero or anything else.

jyp commented 4 years ago

In my understanding, this is related to the fact that we save the current buffer as a temporary file. GHCi ends up with two "versions" of the module and it gets confused. Usually it works again after a reload.

freckletonj commented 4 years ago

@jyp after a dante-restart? hmm... that tends to not work. The features to print type at point, and jump to def never work, and eventually dante remains stopped eternally until I kill emacs...

Are there any debug logs that'd help me investigate?

jyp commented 4 years ago

Since evaluation works, it could be good to check if +c is activated by running :set. For example:

-- >>> :set
-- options currently set: +c
-- base language is: Haskell2010
-- with the following modifiers:
--   -XNoDatatypeContexts
--   -XNondecreasingIndentation
-- GHCi-specific dynamic flag settings:
-- other dynamic, non-language, flag settings:
--   -fdefer-type-errors
--   -fdefer-typed-holes
--   -fdefer-out-of-scope-variables
--   -fno-diagnostics-show-caret
--   -ferror-spans
--   -fexternal-dynamic-refs
--   -fignore-optim-changes
--   -fignore-hpc-changes
--   -fimplicit-import-qualified
-- warning settings:
--   -Wunbanged-strict-patterns
--   -Wdodgy-exports
--   -Wdodgy-imports
--   -Whi-shadowing
--   -Wincomplete-patterns
--   -Wmissing-signatures
--   -Wname-shadowing
--   -Worphans
--   -Wtrustworthy-safe
--   -Wtype-defaults
--   -Wunticked-promoted-constructors
--   -Wunused-do-bind
--   -Wunused-foralls
--   -Wunused-imports
--   -Wunused-local-binds
--   -Wunused-matches
--   -Wunused-pattern-binds
--   -Wunused-top-binds
--   -Wmissing-pattern-synonym-signatures

You can also configure dante-debug (see below) and read the contents of the dante buffer.

dante-debug is a variable defined in ‘dante.el’.
Its value is (inputs outputs responses command-line)
Original value was nil

  This variable is safe as a file local variable if its value
  satisfies the predicate ‘t’.

Documentation:
Show debug output.

You can customize this variable.
jyp commented 4 years ago

Also: if the module cannot be loaded then GHCi won't give any type. Deferring type errors will help there.

freckletonj commented 4 years ago

@jyp Thanks, those were good leads! Still not quite solved though, and totally not your duty to make it work, but I'll document what I'm going through for you and future ppl with the same problems.

For future guests, here are the docs for : :set +c

-- :set   -- and  C-c "
-- options currently set: none.

So I add a .ghci to my root dir with simply :set +c.

Now I see though, after (setq dante-debug t), the dante error buffer shows that ghci is frozen waiting for my input:

The main module to load is ambiguous. Candidates are: 
...
You can specify which one to pick by: 
 * Specifying targets to stack ghci e.g. stack ghci theproject:exe:devel
 * Specifying what the main is e.g. stack ghci --main-is theproject:exe:devel
 * Choosing from the candidate above [1..3]
* * * * * * * *

Specify main module to use (press enter to load none): 

I would think that having the following would have helped it figure out my desired target (PS this was also confusing to figure out how to get my test targets working, just a barb in adoption of Dante):

;; src/.dir-locals.el
((haskell-mode . ((dante-target . "theproject"))))

I'll report back if I figure this out!

freckletonj commented 4 years ago

Progress (though incomplete)! I can thread a variable in via a .dir-locals.el living at project root:

(("src" .
  ((nil . ((dante-target . "theproject:exe:theproject")))))

  ("test" .
  ((nil . ((dante-target . ":test"))))))

(If you've never had to play with this file: directory variables, associated lists. Lifelong emacser, non-elisper, ya it's the basics... :man_shrugging:. Oh, and you can revert-buffer if you want to check out what effect your changes to .dir-locals.el had)

But now I get weird errors that generally hint at - Wrong type argument: listp, t - but still tracking em down.

EDIT: the errors are somehow related to flycheck. When starting flycheck-mode:

Debugger entered--Lisp error: (wrong-type-argument listp t)
  signal(wrong-type-argument (listp t))
  flycheck-buffer()
  flycheck-buffer-automatically()
  flycheck-perform-deferred-syntax-check()

relatedly:

Error while checking syntax automatically: (wrong-type-argument listp t)
freckletonj commented 4 years ago

It's working, sorta. I don't know how I fixed it but after wrestling all day I:

A residual bug is M-i for find-definitiions doesn't work... except I found it can work if you visit the file that dante should be jumping to and re-save it.

I like that Dante is fast-light-simple, thanks and I hope it becomes my daily driver since other projects are either dead or still a little buggy. Thanks, and Stay healthy!

freckletonj commented 4 years ago

gahh, ok, the thing I spent a couple hours debugging, that whole listp t thing? That's because I set this in my init.el:

  (setq dante-debug t)
freckletonj commented 4 years ago

Apologies for closing and reopening, testing targets still don't work, if they depend on the main project library, which can't be found.

I can't find a way to enable multiple targets, and having a testing target is buggy.

If I use a (dante-target . "theproject:test:theproject-test") it can find the test target but not the library target, and so dante fails to boot up.

Digging through the commit history of dante, it looks like multiple targets were considered at some point, so I tried these, but they threw errors:

(dante-target . "theproject:exe:theproject theproject:test:theproject-test")
(dante-target . '("theproject:exe:theproject" "theproject:test:theproject-test"))
(dante-target . (quote ("theproject:exe:theproject" "theproject:test:theproject-test")))

If I use (dante-target . "--test") it... sorta works sometimes, but is still buggy, and it seems like the 2 instances of dante that load up for library+test suffer a ~20 second switching time if I'm going back and forth between them, which I don't quite understand.

This is sorta just an involved bug report, hopefully it helps someone in the future, and if anyone's "in the know" about what's going on, a little documentation would help a tooon.

jyp commented 4 years ago

You have really two issues here.

  1. The unreliability of jump to definition and info is something I could never fix, and seem related to a GHCi bug and/or the way we load temporary files into GHCi
  2. The test targets is something that should be fixable using the dante-target variable. If you still have this issue I think it could be fixable with a precise report. (ie. if I could try your project on my machine).
unhammer commented 3 years ago

For me, jump to definition / info started working once I added my targets into .dir-locals.el. Perhaps readme should say

==Troubleshooting==

If `dante-type-at` gives `Couldn't guess that module name. Does it
exist?` or `xref-find-definitions` gives `No definitions found for:
"/tmp/danteTqJJvj.hs" `, you may need to add your targets to
`.dir-locals.el`; see the Configuration section above.
jyp commented 3 years ago

@unhammer Good suggestion.

jyp commented 1 year ago

Doc updated