Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Hlint plugin using ghc-lib #166

Merged
merged 71 commits into from
Oct 29, 2020
Merged

Conversation

jneira
Copy link
Member

@jneira jneira commented Jun 19, 2020

Pending:

After merging the pr (we will create issues)

  • Continue investigating bad refactors and create issues upstream if needed
  • Investigate how ghc flags (in cabal file, commnand line or in the hs file) affecting the parse of the module by hlint are handled

@jneira jneira mentioned this pull request Jun 19, 2020
@ndmitchell
Copy link
Collaborator

Yes, the fact that you can have Haddock XOR tokens is super frustrating. Would be great to get that fixed upstream. I think using reparsing always is quite reasonable, although it means that things like CPP are likely to be worse. Passing the right extensions onward would be great though.

@pepeiborra
Copy link
Collaborator

Note that ghcide HEAD currently returns the raw AST instead of the Haddock one, unlike the hls fork. This means that hovers and completions of identifiers in the same module don't have docs, which is oddly inconsistent and should probably be addressed by returning the Haddock AST instead.

manual: True
description: Force dependency on ghc-lib-parser even if GHC API in the ghc package is supported

library hls-ghc-lib
Copy link
Member

Choose a reason for hiding this comment

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

Does stack cradle work with private library now? I thought it hasn't been fixed.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, it won't, thanks for pointing it out, i forgot to warn about that in pr description. Unfortunately afaik we cant convert it in a common stanza cause the main reason is to be able to have its own flags and dependencies.
There are plans to allow plugins live in its own package, i think this one could be one of them.

Copy link
Member

Choose a reason for hiding this comment

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

Thank you for the clarification. Could you direct me to the description of the plan? I may take and/or tackle some parts of the plan.

Copy link
Member Author

Choose a reason for hiding this comment

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

@Ailrun sure: #164

@shayne-fletcher
Copy link

Yes, the fact that you can have Haddock XOR tokens is super frustrating. Would be great to get that fixed upstream.

I'm pretty sure this will fall out of Vlad's haddock work in progress right now.

@jneira
Copy link
Member Author

jneira commented Jul 10, 2020

  • Status
    • Diagnostics works, they are shown like in hie
    • Refactor applying hints works for simple cases but generate bad ws edits for the HLint modult itself for example. Investigating

manual: True
description: Force dependency on ghc-lib-parser even if GHC API in the ghc package is supported

library hlint-plugin
Copy link
Member Author

@jneira jneira Jul 10, 2020

Choose a reason for hiding this comment

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

As noted above the private lib does not work for stack based cradle.
Moreover i have to periodically delete the haskell-language-server library from the cabal store due to

exe\Main.hs:147:9: error:
    • Couldn't match expected type ‘Ide.Types.PluginDescriptor’
                  with actual type ‘haskell-language-server-0.2.0.0:Ide.Types.PluginDescriptor’
      NB: ‘haskell-language-server-0.2.0.0:Ide.Types.PluginDescriptor’
            is defined in ‘Ide.Types’
                in package ‘haskell-language-server-0.2.0.0’
          ‘Ide.Types.PluginDescriptor’
            is defined in ‘Ide.Types’
                in package ‘haskell-language-server-0.2.0.0’
    • In the expression: Hlint.descriptor "hlint"
      In the expression:
        [GhcIde.descriptor "ghcide", Pragmas.descriptor "pragmas",
         Floskell.descriptor "floskell", Ormolu.descriptor "ormolu", ....]
      In an equation for ‘basePlugins’:
          basePlugins
            = [GhcIde.descriptor "ghcide", Pragmas.descriptor "pragmas",
               Floskell.descriptor "floskell", ....]
    |
147 |       , Hlint.descriptor "hlint"
    |         ^^^^^^^^^^^^^^^^^^^^^^^^

So i think this plugin must be separated in its own package, after the restructuration of the lib (see #164)

let dflags = hsc_dflags hsc
let hscExts = EnumSet.toList (extensionFlags dflags)
logm $ "getIdeas:setExtensions:hscExtensions:" ++ show hscExts
let hlintExts = mapMaybe (GhclibParserEx.readExtension . show) hscExts
Copy link
Member Author

Choose a reason for hiding this comment

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

Having to cast the types from the "real" ghc used by ghcide and the ghc-lib used by hlint is not good at all.
Not sure if there is an alternative.

type instance RuleResult GetHlintDiagnostics = ()

rules :: Rules ()
rules = do
Copy link
Member Author

Choose a reason for hiding this comment

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

I would need Config here to enable or disable hlint usage. I guess i could get it using getClientConfig :: LSP.LspFuncs Config -> IO Config but i did not find a way to get LSP.LspFuncs Config in Rules aor Action a.
Or maybe should it be disabled in other higher level place?
//cc @alanz

Copy link
Member Author

Choose a reason for hiding this comment

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

After further investigation:

  • It seems Config it is not being stored in the shake graph (or at least i have been not able to find it). ghcide seems to ignore it.
    • So it cant be used in Rules, the actual way to provide diagnostics in the hls plugin api
  • The rest of plugin types (completion, renaming, formatting) are not using Rules but PartialHandlers, via custom providers so they have access to LspFuncs Config
  • There is a DiagnosticProvider defined (from hie) that could be used to get the config but it is not implemented in hls

So, there would be two alternatives:

  • Store the lsp config in the shake graph, creating a Rule for it. We should have in account that it can change at any time by the user, so it have to be changed
  • Implement the DiagnosticProvider and dont use Rules for provide hlint diagnostics

//cc @alanz

Copy link
Collaborator

Choose a reason for hiding this comment

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

I like the idea of a Shake Rule for the config, that way if the user does change the config the diagnostics should then get updated again right?

@jneira
Copy link
Member Author

jneira commented Jul 13, 2020

The output of hlint file.hs --refactor is giving me weird results, calling it directly from the cli over several files of hls itself:

  • hints are not applied
  • spaces are inserted
  • in some regions tokens are being repeated:
importimportqualifiedqualified Language.Haskell.LSP.TypesLanguage.Haskell.LSP.Typesasas LSPLSP
importimportqualifiedqualified Language.Haskell.LSP.Types.LensLanguage.Haskell.LSP.Types.Lensasas LSPLSP

I've posted a bad refactored file example: https://gist.github.com/jneira/70478dee9e9a01d3d10d68218f8fa514#file-hlint-refactored-hs
Refactors works for simpler files so if it is related with windows there should be another factor involved.
I've got same results calling refactors programatically using the plugin.

Pending:

  • Separate the plugin in its own package, it needs Project architecture to support plugins #164
  • Make the plugin honour the lsp client configuration to activate it
  • Show some progress while the refactor is run (it could take some seconds)
  • Continue investigating bad refactors and create issues upstream if needed

@jneira jneira force-pushed the hlint-plugin-ghc-lib branch 4 times, most recently from 1916bdf to 1d3d0d0 Compare July 15, 2020 07:23
@ndmitchell
Copy link
Collaborator

Note that the latest version of the refactor tool has a lot of improvements. I'd check you are on the latest version first.

I'd also check line endings on Windows. Perhaps it is broken on Windows style newlines?

@jneira
Copy link
Member Author

jneira commented Jul 15, 2020

Note that the latest version of the refactor tool has a lot of improvements. I'd check you are on the latest version first.

I am afraid i was already using the last released apply-refact version 0.8.1.0, as a lib and the cli executable (they both generate the same wrong results)

I'd also check line endings on Windows. Perhaps it is broken on Windows style newlines?

I have CRLF in the simple file that is refactored correctly and in the files that fail, but it still could be a factor involved, i will test it

Thanks for the help!

@jneira
Copy link
Member Author

jneira commented Jul 15, 2020

I've tried the refactoring after converting new lines to LF and the result remains the same. The refactoring even has converted the new lines to CRLF again 😞

@jneira
Copy link
Member Author

jneira commented Aug 16, 2020

@jneira
Copy link
Member Author

jneira commented Aug 25, 2020

Pending:

  • Separate the plugin in its own package, it needs Project architecture to support plugins #164
  • Make the plugin honour the lsp client configuration to activate it
  • Show some progress while the refactor is run (it could take some seconds)
  • Continue investigating bad refactors and create issues upstream if needed
  • Activate/create tests

@jneira jneira force-pushed the hlint-plugin-ghc-lib branch 2 times, most recently from 79210d3 to 1ddacf7 Compare August 26, 2020 08:02
@expipiplus1
Copy link
Contributor

Gave this a spin and it seems to be working well!

I resurrected the old hlint commands for vscode-haskell (expipiplus1/vscode-hie-server@6c1d055) applyAll seems to work but applyOne does not. It's almost certainly on my end though!

@expipiplus1
Copy link
Contributor

Not 100% sure where the issue lies (in hls or coc.nvim) but the diagnostic messages seem to include the filepath and line number which seems a little redundant:

image

@jneira
Copy link
Member Author

jneira commented Oct 29, 2020

Ok, i gave up on flaky tests and copied the way ghcide run the test suite:

cabal test --test-options="-j1 --rerun-update" || cabal test --test-options="-j1 --rerun" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test --test-options="-j1 --rerun"

The test suite is retried three times with --rerun and the las one outputs stderr and lsp server messages to help debug them.
I suspect we are using the first cabal test to warm the ide specific cache: the shake database and hie files and it makes the tests pass in the two last executions. Imo that should be taken in account in lsp-test, cause tests are failing even if they does not reach the session timeout so it seems there is another timeout involved (???)

@ndmitchell when you have some time, could you take one last look? i would prefer to merge with your approval 🙂

Copy link
Collaborator

@ndmitchell ndmitchell 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 to me! Keen to try it out in practice 😄

@jneira jneira merged commit 5673556 into haskell:master Oct 29, 2020
@jneira jneira deleted the hlint-plugin-ghc-lib branch December 3, 2020 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

hlint diagnostics not working hlint plugin