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

cabal-add integration as a CodeAction #4360

Merged
merged 66 commits into from
Aug 20, 2024

Conversation

VenInf
Copy link
Contributor

@VenInf VenInf commented Jul 12, 2024

This is a proof-of-concept solution for the issue #3853

If HLS detects a message like "module Bla.Bla.Bla is a member of a hidden package bla-1.2.3" it suggests a quick fix, that finds the closest cabal file and adds the dependency there.

Solution uses Distribution.Client.Add from the cabal-add and automaticly adds version requirement, if it's detected.

CC @Bodigrim

Video with an example:

cabalAddCodeAction.mp4

Implementation details

For now, the cabal-add project was linked using remote package specification. Some parts were heavily inspired by the cabal-add code in the main module and might be rewritten later.

CodeAction works by parsing all haskell diagnostics, and is constructed if a suited message was found. Parsed information is passed down to a new command, which itself uses tools provided by cabal-add. The command conducts IO actions with found cabal file.

What isn't impemented

  • automatic tests, and existing ones lack variety (Since the error is not documented it's hard to make it's different versions. Also GHC(?) does not register that .cabal file was changed and still shows error)
  • no recorders
  • no action for "missing dependencies" error, since I couldn't recreate it
  • no multiple packages suggestion

Copy link
Contributor

@Bodigrim Bodigrim 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 working on this!

@VenInf
Copy link
Contributor Author

VenInf commented Jul 18, 2024

Have made some significant changes to the solution.

  • Integrated readBuildTargets. It doesn't give all the targets in the cabal file, so the result of the action is similar. But it supports multiple targets, if there will be a way to get them.
  • Now sometimes genericPackageDescription is received via cabal rule.There are a lot of IO actions in the solution, and it will be hard/unnecessary(?) to add them all.
  • General refactoring

addDependency :: FilePath -> Maybe String -> NonEmpty String -> IO ()
addDependency cabalFilePath buildTarget dependency = do

cnfOrigContents <- readCabalFile cabalFilePath
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know if HLS does it in other cases, but it could be a good idea to lock the handle using https://hackage.haskell.org/package/lukko-0.1.2/docs/Lukko.html#v:hLock.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or without lukko, open file for read/write access and manipulate it without closing?.. Or am I talking nonsense?..

Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of manually reading and parsing, use https://github.com/haskell/haskell-language-server/blob/master/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs#L260, which hooks into the rule system.

E.g.

liftIO $ runAction "cabal-plugin.cabal-add" ide $ useWithStale ParseCabalFile $ toNormalizedFilePath $ toNormalizedFilePath cabalFilePath

This will give you the GPD already parsed and cached.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Or if you need the fields, the original link will give you that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the handle using approach is not very in the style of hls I used runAction approach instead. It falls back on reading files, if the data wasn't found in rules.

@VenInf
Copy link
Contributor Author

VenInf commented Aug 4, 2024

Have made a lot of changes to the solution:

  • Refactoring and fixed some naming issues
  • Write to a cabal file was changed to a comprehensive edit
  • Added logs
  • Tests (Many thanks for helping out with them to the @fendor)
  • Others

Copy link
Collaborator

@VeryMilkyJoe VeryMilkyJoe left a comment

Choose a reason for hiding this comment

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

Thank you for this awesome feature, I just have a few nitpicks and will check it out and manually test a little bit :)

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/test/Main.hs Outdated Show resolved Hide resolved
Copy link
Collaborator

@fendor fendor left a comment

Choose a reason for hiding this comment

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

Very nice, just a couple of changes, then this is fine by me :)

src/HlsPlugins.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
Copy link
Collaborator

@fendor fendor left a comment

Choose a reason for hiding this comment

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

LGTM, a couple nitpicks left 😇

plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs Outdated Show resolved Hide resolved
@fendor fendor enabled auto-merge (squash) August 20, 2024 14:21
@fendor fendor disabled auto-merge August 20, 2024 14:21
@fendor fendor enabled auto-merge (squash) August 20, 2024 14:46
@fendor fendor disabled auto-merge August 20, 2024 14:46
@fendor fendor merged commit 56fa0de into haskell:master Aug 20, 2024
36 checks passed
@Bodigrim
Copy link
Contributor

I built HLS from sources, but apparently something is off in my configuration, because when clicking on "Add dependency ..." I keep getting log messages like

haskell-ide-engine: 2024-08-21T22:41:41.162085Z | Warning | No plugin handles this "codeAction/resolve" request.
:: [23:41:41.162] <~~ haskell-ide-engine (124) (duration: 0ms): {'code': -32601, 'message': 'No plugins are available to handle this SMethod_CodeActionResolve request.\n Plugins installed for this method, but not available to handle this request are:\nexplicit-fields does not handle resolve requests for (unable to determine resolve owner)).\nhlint does not handle resolve requests for (unable to determine resolve owner)).\noverloaded-record-dot does not handle resolve requests for (unable to determine resolve owner)).\nimportLens does not handle resolve requests for (unable to determine resolve owner)).'}

Do I need to enable something specific in my HLS config? I put there

			"settings": {
				"haskell.plugin.ghcide-completions.config.snippetsOn": false,
				"haskell.formattingProvider": "fourmolu",
				"haskell.plugin.stan.globalOn": false,
				"haskell.plugin.semanticTokens.globalOn": true,
				"haskell.plugin.cabalHaskellIntegration.globalOn": true,
				"haskell.plugin.cabal.globalOn": true
			},

but seemingly I didn't get plugin name right.

@fendor
Copy link
Collaborator

fendor commented Aug 22, 2024

The config shouldn't need any modification as it is disabled by default.
You may have to tell your language server to also send notifications for .cabal files, but I don't know how to do that in non-vscode settings.

I also get errors such:

2024-08-22T08:20:24.550779Z | Warning | No plugin handles this "codeAction/resolve" request.
[Trace - 10:20:24] Received response 'codeAction/resolve - (71)' in 1ms. Request failed: No plugins are available to handle this SMethod_CodeActionResolve request.
 Plugins installed for this method, but not available to handle this request are:
explicit-fields does not handle resolve requests for (unable to determine resolve owner)).
hlint 's feature that handles this request is disabled in your config.
overloaded-record-dot does not handle resolve requests for (unable to determine resolve owner)).
importLens does not handle resolve requests for (unable to determine resolve owner)). (-32601).
[Error - 10:20:24] Request codeAction/resolve failed.
  Message: No plugins are available to handle this SMethod_CodeActionResolve request.
 Plugins installed for this method, but not available to handle this request are:
explicit-fields does not handle resolve requests for (unable to determine resolve owner)).
hlint 's feature that handles this request is disabled in your config.
overloaded-record-dot does not handle resolve requests for (unable to determine resolve owner)).
importLens does not handle resolve requests for (unable to determine resolve owner)).
  Code: -32601 

But it works otherwise fine on my machine after building from source.

Could you post the full logs?

soulomoon pushed a commit to soulomoon/haskell-language-server that referenced this pull request Sep 23, 2024
If HLS detects a message like  "module `Bla.Bla.Bla` is a member of a hidden package `bla-1.2.3`" it suggests a quick fix, that finds the closest cabal file and adds the dependency there.

Solution uses [`Distribution.Client.Add`](https://hackage.haskell.org/package/cabal-add-0.1/candidate/docs/Distribution-Client-Add.html) from the `cabal-add` and automaticly adds version requirement, if it's detected.

For now, the `cabal-add` project was linked using [remote package specification](https://cabal.readthedocs.io/en/3.4/cabal-project.html#specifying-packages-from-remote-version-control-locations). Some parts were heavily inspired by the `cabal-add` code in the main module and might be rewritten later. 

`CodeAction` works by parsing all haskell diagnostics, and is constructed if a suited message was found. Parsed information is passed down to a new command, which itself uses tools provided by `cabal-add`. The command conducts IO actions with found cabal file.
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.

6 participants