Skip to content
This repository has been archived by the owner on Jan 2, 2021. It is now read-only.

Code actions for filling typed holes #69

Merged
merged 5 commits into from
Sep 16, 2019

Conversation

jacg
Copy link
Contributor

@jacg jacg commented Sep 14, 2019

Add code actions which replace typed holes, using GHC's "Relevant bindings include" hints.

@mitchellwrosen
Copy link

Just curious, would this involve parsing those things out of GHC's stringy output?

@@ -132,7 +132,8 @@ suggestAction contents Diagnostic{_range=_range@Range{..},..}
| "Valid hole fits include" `T.isInfixOf` _message = let
findSuggestedHoleFits :: T.Text -> [T.Text]
findSuggestedHoleFits = extractFitNames . selectLinesWithFits . dropPreceding . T.lines
proposeHoleFit name = ("replace hole with " <> name, [TextEdit _range name])
proposeHoleFit name = ("replace hole `" <> holeName <> "` with " <> name, [TextEdit _range name])
holeName = T.strip $ last $ T.splitOn ":" $ head . T.splitOn "::" $ head $ filter ("Found hole" `T.isInfixOf`) $ T.lines _message
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not at all proud of the pattern matching in places like holeName and surroundings. Any hints on how to do a better job of pattern matching on Text would be most welcome.

@mitchellwrosen
Copy link

(oh, oops, this is a PR, not a feature request)

@ndmitchell
Copy link
Collaborator

@mitchellwrosen yes, it involves parsing crap out of the GHC error message, which is sad, but effective. We turn on Unicode all the time to slightly reduce the amount of surface area we have to cover. We are also working with someone who is writing an error messages as ADT proposal, so as of GHC 8.12 or so we hope to avoid this string parsing.

@jacg
Copy link
Contributor Author

jacg commented Sep 14, 2019

We turn on Unicode all the time to slightly reduce the amount of surface area we have to cover.

Which probably explains the mismatch I had in quotes between the action name that I generate, and the action name that I have to search for in the test.

We are also working with someone who is writing an error messages as ADT proposal, so as of GHC 8.12 or so we hope to avoid this string parsing.

So for 8.6 and 8.4 we're stuck with string parsing ... and I'm guessing that some subtle differences between the crap that 8.4 and 8.6 spit out are responsible for the CI failures that have shown up. So now I have to get ghcide working on the same project with two different GHC versions simultaneously ... I'm so looking forward to that!

@jacg jacg force-pushed the typed-hole-actions branch 2 times, most recently from d1d3dbb to 0eccd2e Compare September 14, 2019 22:45
@jacg
Copy link
Contributor Author

jacg commented Sep 15, 2019

Given that parsing text is central to writing these actions, should we use some parser combinator library? If so, which one?

@ndmitchell
Copy link
Collaborator

It's unclear if a parsing library makes it easier or harder. It probably makes it more demanding about the precise layout, which may not be what we want. But someone would need to try it to know.

@jacg
Copy link
Contributor Author

jacg commented Sep 15, 2019

But at least there's no upfront objection to adding a parsing library as a dependency?

@ndmitchell
Copy link
Collaborator

Not from me. If the code looks better or more robust after it sounds like a good idea. I believe attoparsec would be the "default" parser lib nowadays

@jacg
Copy link
Contributor Author

jacg commented Sep 15, 2019

All the CI checks now pass, but the implementation simply comments out two tests which work on GHC 8.6 but fail on 8.4, because the latter doesn't provide hints about local bindings.

Is there a way of excluding these tests on GHC 8.4 only, without resorting to CPP?

@hsenag
Copy link
Member

hsenag commented Sep 15, 2019

@ndmitchell btw, who's working on error messages as an ADT? I had some thoughts about it after digging through the GHC code a bit so would be interested to know what's planned.

@ndmitchell
Copy link
Collaborator

@hsenag @bgamari and @alpmestan I think (but I might be wrong)

Copy link
Collaborator

@cocreature cocreature left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great thank you, the version checks need to be fixed (not your fault, I only added the new macro after you’ve opened this PR) but apart from that it looks great!


suggestAction _ _ = []

topOfHoleFitsMarker = if versionBranch compilerVersion >= [8,6]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

versionBranch isn’t going to work when compiling against ghc-lib. I’ve added a MIN_GHC_API_VERSION macro in #73 which should always be used for checking the GHC API version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, but this requires CPP, and CI rejected my first attempt because it used CPP, which hlint didn't like. Do I have to permit CPP for this module somewhere?

Copy link
Collaborator

@cocreature cocreature Sep 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a whitelist of modules that are allowed to use CPP to make people a bit more cautious when using it. This is definitely a valid usecase, so you can just add your module to the whitelist https://github.com/digital-asset/ghcide/blob/7133f4192f8d11908e5fb4c3f52ca3af54a23459/.hlint.yaml#L78

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I add the test Main to this whitelist?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that Main should do the trick?

Copy link
Collaborator

@cocreature cocreature Sep 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm not sure why this doesn’t work, maybe @ndmitchell has an idea?
edit: looks like you figured it out.

Copy link
Contributor Author

@jacg jacg Sep 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm struggling to get CPP to work in Main.hs of the tests:

It works in CodeAction.hs, and I've done exactly the same in Main.hs AFAICT, that is:

Add

{-# LANGUAGE CPP #-}
#include "ghc-api-version.h"

at the top, then

#if MIN_GHC_API_VERSION(8,6,0)

I did need to add include-dirs: include to test-suite ghcide-tests in ghcide.cabal, for the #include to work at all in Main.hs.

Now I get:

$ cabal v2-test -w ghc-8.6.4
Build profile: -w ghc-8.6.4 -O1
In order, the following will be built (use -v for more details):
 - ghcide-0.0.2 (test:ghcide-tests) (file test/exe/Main.hs changed)
Preprocessing test suite 'ghcide-tests' for ghcide-0.0.2..
Building test suite 'ghcide-tests' for ghcide-0.0.2..

test/exe/Main.hs:507:0: error:
     error: missing binary operator before token "("
     #if MIN_GHC_API_VERSION(8,6,0)
     
    |
507 | #if MIN_GHC_API_VERSION(8,6,0)
    | ^

test/exe/Main.hs:521:0: error:
     error: missing binary operator before token "("
     #if MIN_GHC_API_VERSION(8,6,0)
     
    |
521 | #if MIN_GHC_API_VERSION(8,6,0)
    | ^
`cc' failed in phase `C pre-processor'. (Exit code: 1)

What am I missing?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see what’s going on here. We rely on the MIN_VERSION pragmas which only exist if you depend on ghc which the test suite does not atm. Not quite sure what a nice solution would be but for now, just adding a dependency on ghc is not that bad.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that solves the problem.

Where shall I document the ghc-dependence in the tests? Comment in the cabal file itself?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think a comment in the cabal file should be sufficient, thank you!

test/exe/Main.hs Outdated
, check "replace hole `_c` with globalInt"
"_a" "_b" "_c"
"_a" "_b" "globalInt"
] <> if versionBranch compilerVersion < [8,6] then [] else
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, this should also use MIN_GHC_API_VERSION.

@cocreature
Copy link
Collaborator

As for nicer ways of doing the string matching, I’m definitely not opposed to using a parser library (but I would prefer megaparsec over attoparsec). However, ime for these kind of grep-style tasks regex libraries actually work quite well and with regex-applicative(-text) we have a nice library that avoids the usual issue with regex quickly turning into a non-composable and unreadable mess.

Definitely not something for this PR but if someone wants to give it a shot I’d certainly be interested in whether it ends up being cleaner (I hope it does).

Useful if more than one hole appears on the same line. Not so useful
if both of these holes are just `_` rather than `_name` (or more than
one hole on the same line has the same `_name`): In which case perhaps
some numbers could be attached to the action titles, to distinguish
the holes. But I suspect that this would not be worth the effort.
These test hints about local bindings, whic GHC 8.4 does not provide.
@jacg jacg force-pushed the typed-hole-actions branch 2 times, most recently from 510f478 to cec1323 Compare September 16, 2019 13:18
@jacg
Copy link
Contributor Author

jacg commented Sep 16, 2019

It seems to have passed CI, though I am confused by the hlint output, which includes stuff like:

Warning: Can't find file "ghc-api-version.h" in directories
	./src/Development/IDE/Core
	.
  Asked for by: ./src/Development/IDE/Core/Compile.hs  at line 6 col 1
Warning: Can't find file "ghc-api-version.h" in directories
	./src/Development/IDE/GHC
	.
  Asked for by: ./src/Development/IDE/GHC/CPP.hs  at line 12 col 1
Warning: Can't find file "ghc-api-version.h" in directories
	./src/Development/IDE/GHC
…	.
  Asked for by: ./test/exe/Main.hs  at line 6 col 1

@cocreature
Copy link
Collaborator

Ah, it looks like we need to pass --cpp-include to HLint. We can do that in a separate PR.

Copy link
Collaborator

@cocreature cocreature left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot @jacg!

@cocreature cocreature merged commit 4fc09fa into haskell:master Sep 16, 2019
@alpmestan
Copy link

alpmestan commented Sep 16, 2019

@hsenag @bgamari and @alpmestan I think (but I might be wrong)

Correct, Ben and I have been discussing various approaches for this and I will in fact soon write a proposal for a design that we settled on with lots of useful inputs from Richard Eisenberg and Neil (Mitchell). One of its goals will be to make the life of IDE-style tooling devs a lot easier, essentially doing away with the need to parse GHC's messages, instead getting good old Haskell values to manipulate, inspect, use and print.

I can perhaps ping the ghcide team and you Ganesh, once I have something in a reviewable state, if some of you would like to give some feedback.

@jacg jacg deleted the typed-hole-actions branch October 4, 2019 08:05
pepeiborra pushed a commit to pepeiborra/ide that referenced this pull request Dec 29, 2020
* Add code action for filling type holes

* Incorporate hole name into action title

Useful if more than one hole appears on the same line. Not so useful
if both of these holes are just `_` rather than `_name` (or more than
one hole on the same line has the same `_name`): In which case perhaps
some numbers could be attached to the action titles, to distinguish
the holes. But I suspect that this would not be worth the effort.

* Add tests for fill-type-hole actions

* Disable two tests on GHC 8.4

These test hints about local bindings, whic GHC 8.4 does not provide.

* Replace compilerVersion with new MIN_GHC_API_VERSION macro
pepeiborra pushed a commit to pepeiborra/ide that referenced this pull request Dec 29, 2020
* Add code action for filling type holes

* Incorporate hole name into action title

Useful if more than one hole appears on the same line. Not so useful
if both of these holes are just `_` rather than `_name` (or more than
one hole on the same line has the same `_name`): In which case perhaps
some numbers could be attached to the action titles, to distinguish
the holes. But I suspect that this would not be worth the effort.

* Add tests for fill-type-hole actions

* Disable two tests on GHC 8.4

These test hints about local bindings, whic GHC 8.4 does not provide.

* Replace compilerVersion with new MIN_GHC_API_VERSION macro
pepeiborra pushed a commit to pepeiborra/ide that referenced this pull request Dec 29, 2020
* Add code action for filling type holes

* Incorporate hole name into action title

Useful if more than one hole appears on the same line. Not so useful
if both of these holes are just `_` rather than `_name` (or more than
one hole on the same line has the same `_name`): In which case perhaps
some numbers could be attached to the action titles, to distinguish
the holes. But I suspect that this would not be worth the effort.

* Add tests for fill-type-hole actions

* Disable two tests on GHC 8.4

These test hints about local bindings, whic GHC 8.4 does not provide.

* Replace compilerVersion with new MIN_GHC_API_VERSION macro
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants