jyp / dante

389 stars 52 forks source link

dante does not support multiple targets at once #83

Closed dmvianna closed 1 year ago

dmvianna commented 6 years ago
nix-shell --run "cabal repl --builddir=dist/dante"

is hardcoded into dante. However, it will not work in a project with multiple targets such as qfpl applied fp course. I find that (at least in my system)

nix-shell --run "cabal new-repl --builddir=dist/dante"

works fine in the shell.

Would it be possible to address this limitation in Dante by using new-repl in nix-shell? Dante works fine when I replace cabal repl with cabal new-repl in my system.

sboehler commented 6 years ago

I don't know much about the merits of new-repl vs repl, but instead of using new-repl, you can solve your issue by assigning a value to the dante-target variable. This can be done very conveniently using Emacs' native directory variable feature (https://www.gnu.org/software/emacs/manual/html_node/elisp/Directory-Local-Variables.html).

For an example how I use this, have a look at my project here: https://github.com/sboehler/beans. There is a .dir-locals.el file in each source directory (/app, /src, /test) which defines a target for the cabal repl command issued by dante. So whenever I open a Haskell file in /src, the associated dante process will use lib:beans as target.

In addition, I use a .dir-locals.el file at the project root, to customize general Haskell-mode settings.

Hope this helps!

dmvianna commented 6 years ago

Interesting.

cabal repl exe:level02-exe

works as expected.

;; .dir-locals.el
((haskell-mode . (
          (dante-target . "exe:level02-exe")
          )))

Does not help. Notice below that Dante does pick up the config from .dir-locals.el.

This is the buffer associated with the GHCi session. This buffer
is normally hidden, but the GHCi process ended.

EXTRA TROUBLESHOOTING INFO

Process state change: exited abnormally with code 1

default-directory "/Users/dmvianna/sandbox/applied-fp-course/"
dante-command-line ("nix-shell" "--run" "cabal repl exe:level02-exe --builddir=dist/dante")
dante-state dead
dante-queue (#[257 "r\302\301!\203\302\301!q\210\212\301b\210\300!*\207" [#[257 "\211\306\307\310\"\304\"\211\303\205\211\211\203\307\311\"\301!\262\202R\302\211\205)\312 ?\313 )\300\2036\314\2027\315\316\317\320\321\322\323\300\301\304%\324\"\325\326%\"\262\262\262\262\262\262\207" [nil #[257 "\303\304p!!\301\305\306\307\310\311\312\313\314\300
\"\315\"\316\317%\"\320\203I@\321!
>\2040\322\323\324D\"\210\211\325H\326=\204=\211B\262\210\211T\262A\262\202\266\211\237\262\"\207" [haskell-dante #[128 "\301\302\300#\207" [#s(flycheck-syntax-check #<buffer Core.hs> haskell-dante nil "/Users/dmvianna/sandbox/applied-fp-course/src/Level02/") apply flycheck-report-buffer-checker-status] 5 "

(fn &rest ARGS)"] cl-struct-flycheck-error-tags dante-local-name buffer-file-name finished nil mapcar make-byte-code 257 "\302\300p\301$\207" vconcat vector [dante-fly-message] 6 "

(fn IT)" 0 type-of signal wrong-type-argument flycheck-error 7 splice] 13 "

(fn MESSAGES)"] 171 nil "/Users/dmvianna/sandbox/applied-fp-course/src/Level02/Core.hs" dante-temp-epoch s-equals\? buffer-local-value dante-loaded-file dante-load-message flycheck-running-p save-buffer ":set -fbyte-code" ":set -fobject-code" dante-async-call make-byte-code 257 "p\211\303q\305\300\204\304\203\306\202\307\300\203\310\202\311\312\302!Q!\313\311\314\315\316\317\320\321\301\302#\322\"\323\324%#\262\262\262\207" vconcat vector [dante-async-write ":r" ":l " "*" "" dante-local-name dante-load-loop nil make-byte-code 257 "\211\211G\305U\203\211A\262\242\202\306\307\310GD\"\211A\262\242@\301\211\266\203\302q\300!\262\262\207" vconcat vector [dante-loaded-file dante-load-message 3 signal wrong-number-of-arguments nil] 7 "

(fn V14)"] 16 "

(fn V9)" flycheck-mode] 20 "

(fn V3)"] #<marker at 1183 in Core.hs> marker-buffer] 3 "

(fn BUFFER)"])
dante-loaded-file "<DANTE:NO-FILE-LOADED>"
dante-load-message nil
lcr-process-callback #[257 "\303\304\305\300#\210r\306\302!\203\306\302!q\210\212\302b\210\301!*\207" [#<buffer  dante:applied-fp-course:exe:level02-exe:/Users/dmvianna/sandbox/applied-fp-course/> #[257 "\302\303\"\210\300\304\305\306#!\210\307\301!\207" [#[257 "\300\242P\300\240\301\304\300\242\"\240\305\302\242!\262\262\207" [("error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I), at /Users/dmvianna/sandbox/applied-fp-course/default.nix:1:20
") (nil) (#[0 "\303\242?\211\203\305\306\307\310\311\312\302\303\304#\313\"\314\315%!\202,\316\302\242\317\320\224SO!\301q\300!\262\262\207" [#[257 "\300\301!\207" [dante-set-state running] 3 "

(fn START-MESSAGES)"] #<buffer Core.hs> ("error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I), at /Users/dmvianna/sandbox/applied-fp-course/default.nix:1:20
") (nil) #6 dante-async-read make-byte-code 257 "\300\242P\300\240\301\304\300\242\"\240\305\302\242!\262\262\207" vconcat vector [dante-ghci-prompt string-match lcr-yield] 7 "

(fn V60)" s-trim-right 0 1] 10]) dante-ghci-prompt string-match lcr-yield] 7 "

(fn V60)"] #<buffer  dante:applied-fp-course:exe:level02-exe:/Users/dmvianna/sandbox/applied-fp-course/> dante-debug inputs s-replace "
" "" dante-schedule-next] 6 "

(fn INPUT)"] #<marker at 1 in  dante:applied-fp-course:exe:level02-exe:/Users/dmvianna/sandbox/applied-fp-course/> lcr-set-local lcr-process-callback nil marker-buffer] 5 "

(fn INPUT)"]

WHAT TO DO NEXT

Verify that the GHCi REPL can be loaded manually, then try to
customize (probably file-locally or directory-locally)
`dante-project-root' and/or `dante-repl-command-line'.  If you
fixed the problem, just kill this buffer, Dante will make a fresh
one and attempt to restart GHCi automatically.

If you do not want Dante will not attempt to restart GHCi, just
leave this buffer around. You can always run `dante-restart' to
make it try again.
sboehler commented 6 years ago

How did you add dante to your emacs? I think I had the same issue with the recommended use-package setup from the README file (https://github.com/jyp/dante#installation). I am now using the code below and it seems to work, my recent key insight was that the flycheck-mode hook has to be enabled before the dante-mode hook:

(use-package dante
  :ensure t
  :after haskell-mode
  :commands 'dante-mode
  :init
  (add-hook 'haskell-mode-hook 'flycheck-mode)
  (add-hook 'haskell-mode-hook 'dante-mode)
  :config
  (flycheck-add-next-checker 'haskell-dante '(warning . haskell-hlint))
  )

Can you test this?

dmvianna commented 6 years ago

---

This is the buffer associated with the GHCi session. This buffer
is normally hidden, but the GHCi process ended.

EXTRA TROUBLESHOOTING INFO

Process state change: exited abnormally with code 1

default-directory "/Users/dmvianna/sandbox/applied-fp-course/"
dante-command-line ("nix-shell" "--run" "cabal repl exe:level02-exe --builddir=dist/dante")
dante-state dead
dante-queue (#[257 "r\302\301!\203\302\301!q\210\212\301b\210\300!*\207" [#[257 "\211\306\307\310\"\304\"\211\303\205\211\211\203\307\311\"\301!\262\202R\302\211\205)\312 ?\313 )\300\2036\314\2027\315\316\317\320\321\322\323\300\301\304%\324\"\325\326%\"\262\262\262\262\262\262\207" [nil #[257 "\303\304p!!\301\305\306\307\310\311\312\313\314\300
\"\315\"\316\317%\"\320\203I@\321!
>\2040\322\323\324D\"\210\211\325H\326=\204=\211B\262\210\211T\262A\262\202\266\211\237\262\"\207" [haskell-dante #[128 "\301\302\300#\207" [#s(flycheck-syntax-check #<buffer Core.hs> haskell-dante nil "/Users/dmvianna/sandbox/applied-fp-course/src/Level02/") apply flycheck-report-buffer-checker-status] 5 "

(fn &rest ARGS)"] cl-struct-flycheck-error-tags dante-local-name buffer-file-name finished nil mapcar make-byte-code 257 "\302\300p\301$\207" vconcat vector [dante-fly-message] 6 "

(fn IT)" 0 type-of signal wrong-type-argument flycheck-error 7 splice] 13 "

(fn MESSAGES)"] 3 nil "/Users/dmvianna/sandbox/applied-fp-course/src/Level02/Core.hs" dante-temp-epoch s-equals\? buffer-local-value dante-loaded-file dante-load-message flycheck-running-p save-buffer ":set -fbyte-code" ":set -fobject-code" dante-async-call make-byte-code 257 "p\211\303q\305\300\204\304\203\306\202\307\300\203\310\202\311\312\302!Q!\313\311\314\315\316\317\320\321\301\302#\322\"\323\324%#\262\262\262\207" vconcat vector [dante-async-write ":r" ":l " "*" "" dante-local-name dante-load-loop nil make-byte-code 257 "\211\211G\305U\203\211A\262\242\202\306\307\310GD\"\211A\262\242@\301\211\266\203\302q\300!\262\262\207" vconcat vector [dante-loaded-file dante-load-message 3 signal wrong-number-of-arguments nil] 7 "

(fn V562)"] 16 "

(fn V557)" flycheck-mode] 20 "

(fn V551)"] #<marker at 1 in Core.hs> marker-buffer] 3 "

(fn BUFFER)"])
dante-loaded-file "<DANTE:NO-FILE-LOADED>"
dante-load-message nil
lcr-process-callback #[257 "\303\304\305\300#\210r\306\302!\203\306\302!q\210\212\302b\210\301!*\207" [#<buffer  dante:applied-fp-course:exe:level02-exe:/Users/dmvianna/sandbox/applied-fp-course/> #[257 "\302\303\"\210\300\304\305\306#!\210\307\301!\207" [#[257 "\300\242P\300\240\301\304\300\242\"\240\305\302\242!\262\262\207" [("error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I), at /Users/dmvianna/sandbox/applied-fp-course/default.nix:1:20
") (nil) (#[0 "\303\242?\211\203\305\306\307\310\311\312\302\303\304#\313\"\314\315%!\202,\316\302\242\317\320\224SO!\301q\300!\262\262\207" [#[257 "\300\301!\207" [dante-set-state running] 3 "

(fn START-MESSAGES)"] #<buffer Core.hs> ("error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I), at /Users/dmvianna/sandbox/applied-fp-course/default.nix:1:20
") (nil) #6 dante-async-read make-byte-code 257 "\300\242P\300\240\301\304\300\242\"\240\305\302\242!\262\262\207" vconcat vector [dante-ghci-prompt string-match lcr-yield] 7 "

(fn V608)" s-trim-right 0 1] 10]) dante-ghci-prompt string-match lcr-yield] 7 "

(fn V608)"] #<buffer  dante:applied-fp-course:exe:level02-exe:/Users/dmvianna/sandbox/applied-fp-course/> dante-debug inputs s-replace "
" "" dante-schedule-next] 6 "

(fn INPUT)"] #<marker at 1 in  dante:applied-fp-course:exe:level02-exe:/Users/dmvianna/sandbox/applied-fp-course/> lcr-set-local lcr-process-callback nil marker-buffer] 5 "

(fn INPUT)"]

WHAT TO DO NEXT

Verify that the GHCi REPL can be loaded manually, then try to
customize (probably file-locally or directory-locally)
`dante-project-root' and/or `dante-repl-command-line'.  If you
fixed the problem, just kill this buffer, Dante will make a fresh
one and attempt to restart GHCi automatically.

If you do not want Dante will not attempt to restart GHCi, just
leave this buffer around. You can always run `dante-restart' to
make it try again.
jyp commented 6 years ago

Has new-repl superseded repl? If so I'll switch to that, but my understanding was that "it's not quite ready yet". Now, it should be easy to customize dante-repl-command-line-methods-alist to your liking.

BTW the answer given by @sboehler should work as well. Dante really simply runs the command line that you see, so if it works manually it should work via dante as well.

sboosali commented 6 years ago

afaik new-repl doesn't support multiple targets, and without a specified target, it defaults to all, which doesn't work.

On Sat, Sep 1, 2018, 3:06 PM Jean-Philippe Bernardy < notifications@github.com> wrote:

Has new-repl superseded repl? If so I'll switch to that, but my understanding was that "it's not quite ready yet". Now, it should be easy to customize dante-repl-command-line-methods-alist to your liking.

BTW the answer given by @sboehler https://github.com/sboehler should work as well. Dante really simply runs the command line that you see, so if it works manually it should work via dante as well.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/jyp/dante/issues/83#issuecomment-417880669, or mute the thread https://github.com/notifications/unsubscribe-auth/ACNoMSJejKNlSe6iwDoCo3VyQuMRLv3eks5uWtrMgaJpZM4WPB10 .

themoritz commented 5 years ago

@dmvianna the following note might be relevant: #58

dmvianna commented 5 years ago

Useless. It works with new-repl and without .dir-locals.el seamlessly. If I just place .dir-locals.el in the directory it simply kills dante. With or without new-repl, and with or without flycheck, and in whatever order. And by the way, I want flycheck to work.

templateK commented 5 years ago

I made a emacs module which has a feature that returns cabal target based on current buffer . It is not perfect but it works for me at least. I tested it with some of my projects and the applied fp course. It does not need to set .dir-locals.el but the downside is that it requires more memory due to haskell runtime. In order to use below setup, You need to enable emacs dynamic module feature. You can check your emacs build configuration with (print system-configuration-options) in emacs. If you see --with-modules then you are good to go.

Build

git clone https://github.com/templateK/edm-haskell.git
cd edm-haskell
cabal new-build flib:edm-haskell
mkdir -p ~/.emacs.d/native
# in macos
cp $(find . -name "libedm-haskell.dylib" -type f) ~/.emacs.d/native/
# in linux
cp $(find . -name "libedm-haskell.so" -type f) ~/.emacs.d/native/

Emacs dante package configuration

(use-package dante
  :after flycheck
  :init
  (defun dante-dynamic-target ()
    "Return dante-target based on it's cell type."
    ;; check dante-target defined as non-empty string.
    (if (and (boundp 'dante-target)
             dante-target
             (not (numberp dante-target))
             (not (string-empty-p dante-target)))
        dante-target
      ;; otherwise try to get cabal project target through edm-haskell.
      (if-let* ((cabal-file (haskell-cabal-find-file))
                (cabal-target (edm-haskell-cabal-target cabal-file (buffer-file-name))))
          cabal-target
         ;; if all above fails, just return nil or you can change to other default value.
        nil)))
  :config
  ;; emacs must be build with configure option --with-modules
  ;; to see configuration (print system-configuration-options)
  ;; dylib filename must be absolute path otherwise it throws "dlopen: image not found."
  (let ((edm-filename (concat (getenv "HOME") "/.emacs.d/native/libedm-haskell.dylib")))
    (if (and (file-exists-p edm-filename) (file-executable-p edm-filename))
        (progn
          (module-load edm-filename)
          (fset 'dante-target 'dante-dynamic-target)
          (setq-default dante-repl-command-line
                        '("cabal" "new-repl" (dante-target) "--builddir=dist-newstyle/dante")))
      (setq-default dante-repl-command-line '("cabal" "repl" dante-target "--builddir=dist/dante"))))
  (flycheck-add-next-checker 'haskell-dante '(warning . haskell-hlint))
 ) ;; end of dante

Patch dante-buffer-name function in dante.el in order to use edm-haskell

dante.el

@@ -654,7 +654,7 @@
   "Create a dante process buffer name."
   (let* ((root (dante-project-root))
          (package-name (dante-package-name)))
-    (concat "*dante:" package-name ":" dante-target ":" root "*")))
+    (concat "*dante:" package-name ":" (dante-target) ":" root "*")))

After the patch, don't forget to compile dante.el. You can do that with byte-compile-file in emacs.

cxandru commented 4 years ago

@sboehler You recommend setting dirlocals yet in your repository you have since removed them. What is the way you currently deal with multiple targets? Do you have any recommendations?

sboehler commented 4 years ago

@gregor-alexandru I have stopped using dante in that project.

qrilka commented 4 years ago

@sboehler did you find any replacement for it?

monoidcat commented 4 years ago

Not sure if this is still relevant for others. However, in my case, I forgot to add a .ghci file to the project root and seems like I've encounter the same issue (not being able to load the executable target). Adding the following line solved the issue: :set -iapp/. app here is the directory where my executable module lives.

freckletonj commented 4 years ago

Can multiple-targets work yet? I'd simply like to be able to work on tests and a library simultaneously.

freckletonj commented 4 years ago

The relevant intero: https://github.com/chrisdone/intero/blob/master/elisp/intero.el#L621

The relevant stack docs: https://docs.haskellstack.org/en/stable/build_command/#target-syntax

I still have no clue how to use dante's targets, and just would like them to work on my tests.

doyougnu commented 4 years ago

I'm on spacemacs but I did not have trouble getting this to work with .dir-locals.el for sbv's benchmarking suite library.

;; SBVBenchSuite/.dir-locals.el
((nil . ((dante-project-root . "~/Programming/sbv")
         (dante-target . "sbv:SBVBench"))))

I even have another dir-locals.el at the library level:

~/Programming/sbv:master? λ pwd
/home/doyougnu/Programming/sbv

~/Programming/sbv:master? λ ls -a
.               .hlint.yaml    LICENSE               buildUtils            release.nix
..              .travis.yml    Makefile              cabal.project.local   sbv.cabal
.appveyor.yml   CHANGES.md     README.md             cabal.project.local~  shell.nix
.dir-locals.el  COPYRIGHT      SBVBenchSuite         default.nix           
.ghci           Data           SBVTestSuite          dist                  
.git            Documentation  SMTSolverVersions.md  dist-newstyle         
.gitignore      INSTALL        Setup.hs              profile-shell.nix     
qrilka commented 4 years ago

@doyougnu how specifying 1 target makes it work with multiple targets?

doyougnu commented 4 years ago

There are two .dir-locals.el files: one for sbv:SBVBench in the SBVBenchSuite/ directory, and another for just sbv in the project's root directory. So if I open a file in the SBVBenchSuite directory then dante finds the first .dir-locals.el file, namely the one I posted above. However, if I open a file in src then it finds the .dir-locals.el file in the root directory.

This setup is discussed in detail in the emacs manual, you'll want to check out Directory Variables and File Local Variables. This is an example of using directory variables, you could set any of these variables as a local file variable in some Foo.hs file. Here is my project root .dir-locals.el

;; sbv/.dir-locals.el
((nil . ((dante-methods . (nix-ghci)))))

I only set the dante-methods here and let dante use the default sbv target it finds from the project root. If you really had to you could just overwrite dante-repl-command-line to change the entire dante repl call.

hhefesto commented 4 years ago

got it working (using dante-repl-command-line on doom emacs):

(use-package dante
  :ensure t
  :after haskell-mode
  :commands 'dante-mode
  :init
  (add-hook 'haskell-mode-hook 'flycheck-mode)
  (add-hook 'haskell-mode-hook 'company-mode)
  (add-hook 'haskell-mode-hook 'dante-mode)
  :config
  (setq-default dante-repl-command-line
                ;; Edit this if you use stack. Also set dante-target as needed (maybe "--test").
                '("cabal" "new-repl" dante-target "--builddir=dist-newstyle/dante"))
  )
;; stand-in-language/src/.dir-locals.el
((nil . ((dante-project-root . "/home/hhefesto/src/stand-in-language/")
         ;; (dante-target . "")
         )))
;; stand-in-language/test/.dir-locals.el
((nil . ((dante-project-root . "/home/hhefesto/src/stand-in-language/")
         (dante-target . "sil:sil-parser-test"))))
malteneuss commented 3 years ago

Thanks, @hhefesto.

Also if you struggle to get dante working with flycheck and Haskell hlint, as i did, you can add

(use-package dante
  :ensure t
  :after haskell-mode
  :commands 'dante-mode
  :init
  (add-hook 'haskell-mode-hook 'flycheck-mode)
  (add-hook 'haskell-mode-hook 'company-mode)
  (add-hook 'haskell-mode-hook 'dante-mode)
  :config
  (setq-default dante-repl-command-line
                '("cabal" "new-repl" dante-target "--builddir=dist-newstyle/dante"))
  (flycheck-add-next-checker 'haskell-dante '(warning . haskell-hlint))
  )

to your Doom Emacs config.el

jyp commented 1 year ago

I've put more information regarding this in the doc.