jyp / dante

392 stars 51 forks source link

Dante queues up many GHCi commands before the 1st load is completed #112

Closed emilypi closed 2 years ago

emilypi commented 5 years ago

Despite having flycheck errors deferred until save in the config, Dante sends many processes to GHCI which it can't resolve in a meaningful time frame on larger projects. For instance, typing gibberish into a larger project causes dante to build up 50 some proceses, resolving them slowly over a 15-20 minute period causing one to either kill dante and restart, or wait. This also triggers as many write events, which constantly disrupts flycheck and type information in the minibuffer. Is there any way to simply defer to the most recent process and not build up a backlog of jobs?

Config:

(require 'company-ghci)
(push 'company-ghci company-backends)

(use-package dante
  :ensure t
  :after haskell-mode
  :commands 'dante-mode
  :init
  (add-hook 'haskell-mode-hook 'company-mode)
  (add-hook 'haskell-mode-hook 'flycheck-mode)
  (add-hook 'haskell-mode-hook 'dante-mode)
  :config
  (setq haskell-tags-on-save t)
  (setq dante-debug '(inputs))
  (setq flymake-no-changes-timeout nil)
  (setq flymake-start-syntax-check-on-newline nil)
  (setq flycheck-check-syntax-automatically '(save mode-enabled)))

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

Example (65 jobs built up over 2 lines of gibberish, no saves - writes occur on finished job):

image

jyp commented 5 years ago

I am not experiencing this behaviour. Normally dante should start only one process per cabal target.

emilypi commented 5 years ago

What size projects are you working on @jyp? I suspect it has something to do with the difficulty of the compile. In this case, this project is >= 100k loc with long builds that can't be resolved as quickly as say, small libraries, but 100k lines isn't huge.

jyp commented 5 years ago

I see now what you mean. These are not processes, but simply waiting. It seems that your flycheck configuration is to blame: it should not start a check if some other isn't finished. What version of flycheck are you using ?

emilypi commented 5 years ago

The most up to date version. Perhaps there's a regression? I'll see if i can just switch off Flycheck

emilypi commented 5 years ago

@jyp So i switched up my config, and it seems I cannot start dante without hooking flycheck-mode - it simply doesn't spawn the ghci process buffer. Here's the minimal config to reproduce:

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

Edit: I tried hooking flycheck and simply disabling it in buffer, which works to start up dante, however, even with flycheck disabled, the problem persists

Screen Shot 2019-03-21 at 2 55 45 PM

jyp commented 5 years ago

I can't see what's going on unfortunately. I have tried the following program:

test :: Integer
test = id id id id id id id id id id id id id id id id id id id id id id id id id id id 234

whose type-checking takes a very long time (and crashes GHC). But I'm seeing only up to 6 queued up commands when I'm typing during the checking. (This is still more than what I'd expect --- but less than what you see).

Perhaps you can try the above and report what you see?

emilypi commented 5 years ago

I tried doing this with something that wasn't even difficult to type check - I created a bare project, with a single empty module and just typed really fast gibberish and managed to wrack up a bunch of processes.

Screen Shot 2019-03-22 at 9 43 22 PM

It seemed to resolve quickly, with the following outputs:

<** About a million of these **>

/Users/emilypi/haskell/test/src/Foo.hs:3:2-8: error:
    Parse error: module header, import declaration
    or top-level declaration expected.
Failed, no modules loaded.
Prelude|0 0 ""
Prelude|Prelude|[1 of 1] Compiling Foo              ( /Users/emilypi/haskell/test/src/Foo.hs, dist/dante/build/Foo.o )

Ok, one module loaded.
Collecting type info for 1 module(s) ... 
Prelude Foo|
emilypi commented 5 years ago

Later, I tried your example, and I noticed what you noticed: that if a single expression is difficult to compile, the entire project locks up, and processes queue without resolving (or at least, resolve slowly):

Screen Shot 2019-03-22 at 9 49 42 PM

I imagine what's happening on larger projects (or even small projects that are hard to compile), is that ghci processes pile up quickly because they can't figure out whether they should do a reload or a full compile, and end up simply queuing compiles.

jyp commented 5 years ago

I can reproduce this, but it happens only when the first load is not completed.

emilypi commented 5 years ago

Thanks for looking into this @jyp. I'm unsure of how to fix this, or I'd have looked into a patch already. Unfortunately, my Elisp is novice at best. This is a concerning problem, though, since every keystroke seems to generate a job.