jyp / dante

389 stars 52 forks source link

Using Dante with stack script #165

Closed Martinsos closed 1 year ago

Martinsos commented 3 years ago

First of all, thanks for Dante, I love it! Makes my love so much easier, and doesn't need any configuration at all for most projects.

I have a simple Haskell script -> when I say script, I mean it is using stack script interpreter feature to run as a script:

In that script, I import Hspec. Dante doesn't recognize import Test.Hspec:

  error:
     Could not find module ‘Test.Hspec’
     Use -v (or `:set -v` in ghci) to see a list of the files searched for.

Here is minimal example of script that has this error:

#!/usr/bin/env stack
-- stack --resolver lts-17.9 script --package hspec

import Test.Hspec

main = putStrLn "test"

Is there a way to get Dante to recognize this package/module?

I am using Dante through Spacemacs, default configuration.

soupi commented 3 years ago

I think that if dante doesn't find a stack.yaml it will use the global stack environment, so if your global stack environment matches your script's resolver and the package is installed it should work.

stevladimir commented 3 years ago

Can you paste an output of the dante buffer? It is named like dante:<your-project>

Martinsos commented 3 years ago

@soupi, hmmm, but I don't think that global environment is supposed to match the script -> script should be standalone, reproducible thing, with it's own resolves and packages specified, as you can see above in my code example + their docs. So if that is how Dante works, it would mean it would not work for scripts. I imagine the best way would be to have Dante use the same command as script uses? Ideally I imagine it would read it from script and use it, and if not, that we would be able to provide it per script.

My project is organized like this:

  - ~/git/topcoder/srm-802/
    - BalloutCount.hs

and I see one Dante buffer present in my (spac)emacs: dante:::~/git/topccoder/srm-802/*. I opened it, however it is completely empty! I tried changing the hs script, calling dante-restart (which kills and then creates the dante buffer again), calling different dante commands, but buffer remains empty.

Output of dante diagnose:

default-directory "/home/martin/git/topcoder/srm-802/"
dante-command-line ("ghci")
dante-state (ghc-err (compiling "Main"))
dante-queue nil
dante-loaded-file "/home/martin/git/topcoder/srm-802/DisjointDiceValues1000.hs"
dante-load-message (("/tmp/dantebp6rq3.hs" "4:1-17" "error:" "    Could not find module ‘Test.Hspec’
    Use -v (or `:set -v` in ghci) to see a list of the files searched for.
"))
lcr-process-callback nil

Extra info:

Martinsos commented 3 years ago

Btw I am looking into https://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html right now. I can't provide comment on the first or second line because those are already used by stack script, but I could put comments at the bottom of the file, so I guess this could be a solution? I am not 100% what comment I would put there, but I guess I would somehow explain to dante which command to use to evalute the script?

What I don't like about this is that I have to put these comments directly in the file. If somebody else is not using dante, these comments are not relevant for them -> what would happen if everybody would start putting their own comments? I would rather have them in a separate file. But again I don't want to define stuff on the level of project, I want to define it per level of file/script, and that doesn't seem to be possible?

I imagine it would be ideal if Dante would pick up the stack script comment and use it in a smart way. I don't know though how hard is that or how ambiguous it is.

stevladimir commented 3 years ago

To me it looks like that it picks wrong dante-method. As it does not find any specific file like stack.yaml or shell.nix it just picks the default method, which is bare-ghci and hence dante-command-line == "ghci". You can explore (and customize) dante-methods-alist variable to get the list of available methods.

The solution is to use local file variables. You can place .dir-locals.el file in the directory with your script with the following content

((haskell-mode . (
  (dante-repl-command-line . ("stack" "repl" "DisjointDiceValues1000.hs"))
  ; It may be also necessary to set
  ; (dante-project-root  . "/home/martin/git/topcoder/srm-802")
  )))
Martinsos commented 3 years ago

@stevladimir thanks, something in this direction might be a solution! However, scripts have this special thing where they specify their own resolver and packages. I tried and stack repl doesn't pick that up, it just loads the .hs file while ignoring the comment in it that specifies other details.

One thing I do know is how to get ghcid working with the stack script: I would do it with ghcid -c "stack BallotCounting.hs", and that works. Is this something we can use for this?

Martinsos commented 3 years ago

I found this issue on stack repo: https://github.com/commercialhaskell/stack/issues/5354 -> it seems there is no way to run a stack script with ghci. And it make sense that this is supported from the side of stack, not dante. Still, I am interested to hear what others think (@jyp ?).

stevladimir commented 3 years ago

Unfortunately, the only option coming to my mind is to pass resolver and dependencies manually. Smth like below should work

; .dir-locals.el
((haskell-mode . (
  (dante-repl-command-line . ("stack" "repl" "--resolver" "lts-17.9" "--package" "hspec" "DisjointDiceValues1000.hs"))
  ; It may be also necessary to set
  ; (dante-project-root  . "/home/martin/git/topcoder/srm-802")
  )))
Martinsos commented 3 years ago

Thanks @stevladimir ! Makes sense, as I mentioned above, I think this should be taken care from the stack side.