Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Extended Eval Plugin #438

Merged
merged 5 commits into from
Dec 25, 2020
Merged

Extended Eval Plugin #438

merged 5 commits into from
Dec 25, 2020

Conversation

tittoassini
Copy link
Contributor

A superset of the current Eval plugin.

It adds the following features:

  • Tests in both plain comments and Haddock comments
  • For Haddock comments: shows differences between latest and previous result
  • Setup section, executed before every test
  • Execution of a section/group of tests at the time
  • Property testing
  • Setup of GHC extensions

Does not yet support CPP or LHS modules.

@jneira jneira requested a review from pepeiborra September 27, 2020 21:28
@pepeiborra
Copy link
Collaborator

This looks awesome, but I won't be able to review it until later this week (or possibly next week)

@tittoassini
Copy link
Contributor Author

Take your time.

I was agonisingly slow in writing it, I won't complain if you are a bit slow in reviewing it :-)

@alanz alanz mentioned this pull request Oct 7, 2020
@tittoassini
Copy link
Contributor Author

The failed test on ghc-8.10.2 is unrelated to this PR.

@jneira
Copy link
Member

jneira commented Oct 8, 2020

@tittoassini yeah, could you rebase master to include the fix (c1d47b9)?

gcatch
-- gStrictTry op = MC.catch
(op >>= \v -> return $! Right $! v)
(\(err :: SomeException) -> return $! Left $! show $! err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What are you trying to accomplish here?
Without evaluate I'm not sure all the bangs make much difference

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 have redefined it as:

gStrictTry :: ExceptionMonad m => m b -> m (Either String b)
gStrictTry op =
gcatch
(op >>= fmap Right . gevaluate)
showErr

gevaluate :: MonadIO m => a -> m a
gevaluate = liftIO . evaluate

showErr :: Monad m => SomeException -> m (Either String b)
showErr = return . Left . show

[]
-}
tokens :: [String] -> [Loc TokenS]
tokens = concat . map (\(l, vs) -> map (Located l) vs) . zip [0 ..] . reverse . snd . foldl' next (InCode, [])
Copy link
Collaborator

Choose a reason for hiding this comment

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

without a strict accumulator, parsing errors will be concealed by laziness

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tokens should never actually return any parsing error.

it is just a way of classifying input lines by type and at least one of the types (codeLine) always succeeds.

Comment on lines 160 to 169
-- Use the preprocessor to normalise CPP/LHS files, but we still get an error.
-- fp <- handleMaybe "uri" $ uriToFilePath' uri
-- let nfp = toNormalizedFilePath' fp
-- session :: HscEnvEq <-
-- liftIO $
-- runAction "runEvalCmd.ghcSession" state $
-- use_ GhcSession nfp
-- Right (ppContent, dflags) <- liftIO $ evalGhcEnv (hscEnv session) $ runExceptT $ preprocessor fp (Just $ textToStringBuffer content)
-- let text = decodeUtf8 $ stringBufferToByteString ppContent
-- liftIO $ dbg "PREPROCESSED CONTENT" text
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should work. What error do you get?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The trivial problem was the preprocessor was not even accessible as it is not an exposed module in ghcide.

The main one was that even using the preprocessor I would still get an invalid session (or a similar error) when accessing a CPP file.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Make it an exposed module and/or reexport the function in Development.IDE, and upstream the change.
Please share the error, I don't think that we should merge this change until CPP works

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 have submitted a PR to ghcide to export the Preprocessor module.

The latest commit here, adds support for CPP and LHS (Bird-style) for GHC >= 8.8.

@tittoassini
Copy link
Contributor Author

As this PR is getting rather messy, I have created a clean branch that includes all the changes made here plus a few additional fixes and I was planning to push it as a PR to simplify the reviewers' work. Would that be ok?

@pepeiborra
Copy link
Collaborator

Personally, a new PR would mean more review work rather than less.

@tittoassini
Copy link
Contributor Author

tittoassini commented Oct 31, 2020 via email

@jneira
Copy link
Member

jneira commented Nov 25, 2020

@tittoassini it would need a rebase

@pepeiborra
Copy link
Collaborator

@tittoassini Is this ready for re-review?

@tittoassini tittoassini force-pushed the master branch 2 times, most recently from 1086040 to 9f822c6 Compare December 1, 2020 19:49
@tittoassini
Copy link
Contributor Author

Hi @pepeiborra,

yes, I think that is ready for review.

The main problem that appears in the test is the completely random appearance of this error:

ByteCodeLink.lookupCE\nDuring interactive linking ...

The problem seems to affect also the current implementation: #610

Any idea of what might cause it?

The good news is that when using the plugin manually, it rarely appears.

I have squashed the commits into two commits:

  • Extended Eval Plugin (seen changes)
    Contains all the changes that I had already pushed and that you had already seen, including all suggested modifications

  • Extended Eval Plugin (additional changes)
    A bunch of other changes that I have made in the last month (no new feature, just fixes)

I apologise if this is not the correct procedure but I am no git jedi and the 30+ commit were becoming hard to manage when rebasing.

This is a one-off rewriting of the commit history. Any further change will come as a separate commit.

Thanks for your patience!

Comment on lines 362 to 372
-- Desperately looking for an import path
let impPaths0 = envImportPaths session
impPaths1 = if null (importPaths df) then Nothing else Just (importPaths df)
impPaths2 = maybeToList $ moduleImportPath nfp modName
impPaths = fromMaybe impPaths2 $ impPaths0 <> impPaths1
dbg "importPaths0" impPaths0
dbg "importPaths1" impPaths1
dbg "importPaths2" impPaths2
dbg "importPaths" impPaths
-- Restore the cradle import paths
df <- return df{importPaths = impPaths}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
-- Desperately looking for an import path
let impPaths0 = envImportPaths session
impPaths1 = if null (importPaths df) then Nothing else Just (importPaths df)
impPaths2 = maybeToList $ moduleImportPath nfp modName
impPaths = fromMaybe impPaths2 $ impPaths0 <> impPaths1
dbg "importPaths0" impPaths0
dbg "importPaths1" impPaths1
dbg "importPaths2" impPaths2
dbg "importPaths" impPaths
-- Restore the cradle import paths
df <- return df{importPaths = impPaths}
df <- return df{importPaths = fromMaybe (importPaths df) (envImportPaths session)}

Copy link
Contributor Author

@tittoassini tittoassini Dec 3, 2020

Choose a reason for hiding this comment

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

OK, there were some cases where envImportPaths and importPaths did not return anything, so I added moduleImportPath but I have not been able to reproduce the problem so I simplified this section down to your one-liner

. gStrictTry
$ evalGhcEnv (hscEnvWithImportPaths session) $
do
env <- getSession
Copy link
Collaborator

@pepeiborra pepeiborra Dec 2, 2020

Choose a reason for hiding this comment

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

Curious choice of indentation. Not only is this PR 60 files long, it's also 200 columns wide!

Treat your reviewers as you wish to be treated ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

beauty is in the eye of the beholder :-)

I have squashed those 'header' lines 350-356 in a single line (that is now 182 columns wide!) so that we can fit the code under it into less than 100 columns.

@@ -0,0 +1,231 @@
module Ide.Plugin.Eval.Tutorial where
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't it be useful to use Haddock markup consistently in this module, so that it is rendered nicely?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Tutorial module was actually meant to be read as source code (so that one can play with it while reading and can see exactly how tests are written down in different kinds of comments).

But you are right, it also makes sense to make it into a proper Haddock-ready module so that it can work as a static README. I modified it accordingly.

Are haddocks built and accessible on the project GitHub site?

Copy link
Collaborator

@pepeiborra pepeiborra left a comment

Choose a reason for hiding this comment

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

Thanks for splitting in two parts. Unfortunately the second commit has tons of formatting changes so it didn't really help that much, there's tons of changes.

I don't have time to do a deeper review. It looks reasonable and I imagine that you have been using it extensively and hopefully it works.

My main concern is future maintainability. With so much new code, it would be great if you would commit to supporting this plugin, starting with a commitment to split it out to its own package.

@tittoassini
Copy link
Contributor Author

I do understand that it is going to take a lot of additional effort to polish this plugin and to fully realise its potential (I have a number of ideas for improvements and extensions) and I am willing to invest the time necessary.

I use it heavily every day, so it's a worthwhile investment.

I will however need help with GHC API and ghcide issues.

As for moving it into its only subpackage, would you like me to do it as part of this PR or at a later time?

@pepeiborra
Copy link
Collaborator

I do understand that it is going to take a lot of additional effort to polish this plugin and to fully realise its potential (I have a number of ideas for improvements and extensions) and I am willing to invest the time necessary.

I use it heavily every day, so it's a worthwhile investment.

I will however need help with GHC API and ghcide issues.

As for moving it into its only subpackage, would you like me to do it as part of this PR or at a later time?

Definitely as a follow up, this PR is already long enough

@tittoassini
Copy link
Contributor Author

Should I rebase and squash everything in a single commit, in preparation for merging?

@pepeiborra
Copy link
Collaborator

I don't think so

@pepeiborra
Copy link
Collaborator

I haven't looked into this issue. Given that the current incarnation of the Eval plugin completely ignores the contents of the GHC session maintained by ghcide and instead reloads everything from source, there are two alternative paths forward:

  1. Embrace the current design, and then the logical step forward is turning on the -fexternal-interpreter flag to enforce isolation of the evaluated code w.r.t the ghcide process. This would also solve Codelens eval should not interact with IO #461 and potentially other issues
  2. Eschew the current design, and rewrite the GHC guts of the eval plugin to rely on the byte codes produced by ghcide.

@wz1000
Copy link
Collaborator

wz1000 commented Dec 6, 2020

Eschew the current design, and rewrite the GHC guts of the eval plugin to rely on the byte codes produced by ghcide.

This would require some hooks in ghcide, so that it will compile bytecode for modules that use the eval plugin (and all their dependencies). In particular, the NeedsCompilation rule will have to be modified so that it can evaluate arbitrary conditions (probably specified as an NormalizedFilePath -> Action Bool), and HLS would then have to expose this hook in the plugin API.

@pepeiborra
Copy link
Collaborator

This issue has been there for a while but is more evident now because the extended plugin tests evaluate more than one test at the time and so increases the chance of a failure.

I see that failed tests are now rerun and this tends to weed out random failures, maybe a third run should be added to reduce them even further.

Maybe the simpler solution would be to ensure that the eval tests are run sequentially

@tittoassini
Copy link
Contributor Author

I haven't looked into this issue. Given that the current incarnation of the Eval plugin completely ignores the contents of the GHC session maintained by ghcide and instead reloads everything from source, there are two alternative paths forward:

  1. Embrace the current design, and then the logical step forward is turning on the -fexternal-interpreter flag to enforce isolation of the evaluated code w.r.t the ghcide process. This would also solve Codelens eval should not interact with IO #461 and potentially other issues

That sounds great.

Do you mean simply setting -fexternal-interpreter in the haskell-language-server section of the cabal file?

That does not seem to fix the problem.

@jneira
Copy link
Member

jneira commented Dec 6, 2020

@tittoassini if you are fine me too 😄
But i personally try to have commits describing what it does (ideally also why if they are complex) and avoid generic messages like Update Module.hs. But not always i follow that rule so i will not force nobody to do it neither.

@pepeiborra
Copy link
Collaborator

I haven't looked into this issue. Given that the current incarnation of the Eval plugin completely ignores the contents of the GHC session maintained by ghcide and instead reloads everything from source, there are two alternative paths forward:

  1. Embrace the current design, and then the logical step forward is turning on the -fexternal-interpreter flag to enforce isolation of the evaluated code w.r.t the ghcide process. This would also solve Codelens eval should not interact with IO #461 and potentially other issues

That sounds great.

Do you mean simply setting -fexternal-interpreter in the haskell-language-server section of the cabal file?

That does not seem to fix the problem.

No, I mean setting enabling the external interpreter in the DynFlags used by the Eval interpreter

@tittoassini
Copy link
Contributor Author

No, I mean setting enabling the external interpreter in the DynFlags used by the Eval interpreter

setting the flag in setupDynFlagsForGHCiLike causes all evaluations to fail with "haskell-language-server: ghc-iserv terminated (-11)

@tittoassini
Copy link
Contributor Author

@jneira
But i personally try to have commits describing what it does (ideally also why if they are complex) and avoid generic messages like Update Module.hs. But not always i follow that rule so i will not force nobody to do it neither.

You are right, I can edit the descriptions of those and/or squash them together, but given that there are also other commits that are not particularly meaningful (such as Extended Eval Plugin (seen changes)) I thought that they could all be merged in a single "Extended Eval Plugin" commit.

Really, I have no preferences whatsoever. I am just trying to figure out how you prefer things to be done :-)

@jneira
Copy link
Member

jneira commented Dec 6, 2020

I thought that they could all be merged in a single "Extended Eval Plugin" commit.

Fair enough, will use the "squash and merge" option then.

@tittoassini
Copy link
Contributor Author

I have followed @pepeiborra advice and I have modified the Eval test suite to run the codelens commands in each test sequentially (previously they would be started in parallel, my bad).

If in addition I also run the test suite sequentially passing '--num-threads 1' to tasty, I cannot locally reproduce the linking issue.

Looking at the automated test run on the PR:

  • the circleci tests, that by a quick look seem to run sequentially, now pass (with some small exception that disapper when the test is automatically rerun).

  • the nix tests pass

  • the "Testing / test" ... tests fails (with both the linking problem and a QuickCheck library missing) I guess that these are run in parallel and that's what is causing it?

@tittoassini
Copy link
Contributor Author

I thought that they could all be merged in a single "Extended Eval Plugin" commit.

Fair enough, will use the "squash and merge" option then.

Done.

@tittoassini
Copy link
Contributor Author

@jneira, might it be a good idea to merge this now?

This is a rather complex PR and it would be good to have a full month till next release to iron out any kinks.

@jneira
Copy link
Member

jneira commented Dec 16, 2020

@tittoassini yeah, but maybe @pepeiborra could take a final look, cause there are changes after his review (and make ci green)

@jneira jneira requested a review from pepeiborra December 16, 2020 13:56
@tittoassini
Copy link
Contributor Author

@tittoassini yeah, but maybe @pepeiborra could take a final look, cause there are changes after his review (and make ci green)

Green tests, @jneira, green tests as far as the eye can see :-)

Merry Christmas!

@pepeiborra
Copy link
Collaborator

I'm fine with merging this, I've already given it two reviews in the 3 months it's been open and am not going to review it again

@jneira
Copy link
Member

jneira commented Dec 25, 2020

@tittoassini thanks for the Christmas present

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants