clojure-emacs/squiggly-clojure

Display inferred types

Closed this issue · 10 comments

pnf commented

In principle, this is as easy as adding :file-mapping-true when invoking core.typed/check-ns-info and reporting the output at 'info level, but in practice this will blow through any reasonable flycheck-checker-error-threshold for non-trivial input.
An alternative would be to run core.typed/check-form-info on demand for particular sexps, but I don't know how or if it's possible to communicate the results via flycheck highlighting.
@lunaryorn Any ideas?

@pnf I don't think that Flycheck is the right tool for this task, both technically and UI-wise. Technically, you can't add overlays for any single expression in a file. Overlays are too expensive for that.

UI-wise, I think that showing the type of every single expression (or even only top-level expressions) won't work well. It's just too much information for any user to comprehend. And most of it is redundant, anyway: I think most programmers are usually aware of the types used in the current context, and only need the type checker to catch mistakes, or if they are unsure.

I think, I'd rather write a command to explicitly show the type of the expression at point. That's what Haskell Mode and Merlin (for OCaml) do. On the Emacs side, that's probably not difficult at all: You'd just (asynchronously) send the current position to a Clojure backend, and wait for the result, which you'd then show—either in the minibuffer, or in some graphical popup. The hard part would be the Clojure side, which has to figure out the limits of the expression and infer its type.

@pnf Besides, I think you should probably discuss this feature with @bbatsov, since it appears to be a very useful addition to Cider, imho.

pnf commented

What I'm hoping for is, essentially, augmented eldoc, so you can get type information (including inferred type) just by hovering; however, if you had to request it with a keystroke, that would be ok.
The main complication is that inferring all the types is really a batch operation, produced as a side-effect of type-checking, so we want it to be asynchronous, and it might as well be the same invocation as the check used by clojure-cider-typed. One possibility is to extract the type map into an emacs global that would then be queried by the type request command. Another is to leave the map in Clojure and query it through an nrepl-sync-request.

@pnf You can actually use eldoc for this purpose, just hook into eldoc-dcocumentation-function. If you implement that in Cider directly, you could even use some clever heuristics to toggle between the display of the current type, and the display of the signature of the current call form—or you could show both at the same time.

As for the inferred types themselves, I'd just cache them—either on Emacs' or on Clojure's side depending on what's easier to use for you. Run the type checker whenever Flycheck conducts a syntax check, and then store the results. In case a user has disabled Flycheck, you'd likely want to add your own timer to periodically update the buffer—run-with-idle-timer is the keyword here.

If you'd like to implement it yourself, separately from eldoc, just take a look at Flycheck's error display for inspiration (see flycheck-display-error-at-point-soon and related functions). Essentially you'd use run-at-time from post-command-hook to arrange for the type at point being displayed in x seconds (e.g. 0.5). If there's already a timer, cancel it, and wait 0.5 seconds again. That's how Flycheck's “error at point” works. It's about ten lines of code in Emacs.

I don't know how you'd do that with the mouse, though, if that's what you mean by “hover”. I scarcely use the mouse in Emacs, and my packages don't have dedicated support for the mouse. The tooltips you get when moving the mouse pointer on a Flycheck error in a buffer are a side-effect of using overlays, and no deliberate feature of Flycheck.

pnf commented

@lunaryorn Thanks. I'll look at eldoc-documentation-function, though I think I've convinced myself that displaying the information on demand in the minibuffer is fine, and that would have the advantage of not competing with existing eldoc information.
One somewhat irritating problem is that core.typed takes 5-10x as long when you ask it to return the map of returned types - extremely roughly 100 seconds per line of code - which is probably intolerable for regular flycheck use. Happily, the only part of the operation that actually consumes CPU is the file mapping, so I could have parallel checks running simultaneously without affecting performance as seen via flycheck. This means that I don't have to intermingle the concerns of type display and type checking. (Though it does imply some odd bottlenecks in core.typed.)

pnf commented

With 31ccbaf, the master branch now supports type display. I believe I've arranged this in a manner that won't upset anyone, as it is completely distinct from flycheck-clojure :

  1. The only version tag is at cd1d999, so melpa-stable won't see this.
  2. flycheck-clojure.el is now moved into elisp/flycheck-clojure, while clojure-typed-doc.el lives in elisp/typed-clojure; a copy of the former is in the root directory, pending melpa pull #2389.
  3. Flychecking will optionally produce the location -> type map, if the :typed-file-mapping option is set to true, which, by default, is not.
  4. clojure-typed-doc.el contains two interactive commands, one to build the mapping on demand (presuming it's not being done during flycheck), and another to display the type at the point.

👍

pnf commented

Side note: I found an easy 5x improvement in performance for the file-mapping in core.typed and submitted a patch. I suspect there's more room for improvement, especially in the type checking itself, which consumes mysteriously little cpu.

👍 👍 :)

👍