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

Resolve 1: Support for resolve in overloaded-record-dot #3658

Merged
merged 24 commits into from
Jun 30, 2023

Conversation

joyfulmantis
Copy link
Collaborator

In order to better understand how resolve should be implemented, I have started to implement codeAction resolve in overloaded-record-dot. This branch is based off of the new-lsp-release.

@joyfulmantis joyfulmantis changed the title WIP Preliminary support for resolve in overloaded-record-dot Support for resolve in overloaded-record-dot Jun 19, 2023
@joyfulmantis joyfulmantis marked this pull request as ready for review June 19, 2023 13:41
| otherwise = False

instance PluginRequestMethod Method_CodeActionResolve where
-- CodeAction resolve is currently only used to changed the edit field, thus
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmmm, this made me realize that something funny may happen here. Suppose we have N code action handlers, each of which has a corresponding resolve handler. Now if one of the code action handlers produces a code action, then when the client asks to resolve it... it's going to get sent to every resolve handler. So we'll need to make sure that resolve handlers "know" if it's one of "their" code actions...

Copy link
Collaborator

Choose a reason for hiding this comment

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

And indeed, I think we therefore don't want to combine the results of multiple resolve handlers firing. What would that even mean? Something has gone wrong if that has happened! We should get exactly one response.

Hard to do for now, but I do think that combineResponses should be able to throw an error in cases like this. For now I'd probably do the crappy "just take the first result" thing we do elsewhere.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So even if every plugin's resolve handler used the same type theoretically Data.Unique should allow types to know whether a resolve belongs to them (resolvable ones eventually anyways). To make sure no extra processing was needed I was thinking of having the plugin record their name or id in the type serialized to the data field, and then first match on that, to make sure no extra processing was needed.

Regarding combineResponses, I was actually originally just returning the unmodified codeAction if I was unable to resolve with the provided resolve data, which is why combineRespones would still be needed to find the modified codeAction from the list. I guess I could/should just throw an error, and that would be equivalent to returning Nothing? The plugin ultimately responsible for the resolve also needs to throw a ContentModified responseError if it can't resolve it. So not actually sure we can use pluginResponse in the end (or at least we need to modify it)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Right, if we use standard infrastructure and tag every code action with a Unique then we probably should be fairly okay. Although it requires some state on the plugin side somewhere to remember which uniques it produced. The advantage of the "just pass the CodeActionParams again" approach is that it's easier to be stateless if you want to. Not sure if it's possible to be stateless in general, though 🤔

I guess I could/should just throw an error, and that would be equivalent to returning Nothing?

I think that's fine. I think we run all the handlers, throw away any that failed and then combine the results of the rest. We could potentially also use the pluginResponsible method for this, I think? We use it in a similar way to restrict the scope of some handlers. So if we put the responsible plugin ID in the data then I think we could restrict to just that plugin in pluginResponsible.

@@ -135,56 +147,100 @@ instance Pretty RecordSelectorExpr where
instance NFData RecordSelectorExpr where
rnf = rwhnf

data ORDResolveData = ORDRD {
Copy link
Collaborator

Choose a reason for hiding this comment

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

WDYT about the idea of just reusing the CodeActionParams?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

At least in this case, the CodeActionParams doesn't make sense. The problem with the codeActionParams is we are going to need to do processing anyways to know whether we can provide the codeAction, and right now it's a title too, so it makes sense to just process once instead of once to present and once to execute

Copy link
Collaborator

Choose a reason for hiding this comment

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

Right. I think it definitely makes sense that we'll want a "stateful version" of resolve-based handlers. Maybe we'll also want a stateless one... I think there are some plugins that don't define any of their own rules so don't even have anywhere to put state. But perhaps easier to do a few and then refactor afterwards.

@@ -201,11 +257,15 @@ collectRecSelsRule recorder = define (cmapWithPrio LogShake recorder) $
-- the OverloadedRecordDot pragma
exts = getEnabledExtensions tmr
recSels = mapMaybe (rewriteRange pm) (getRecordSelectors tmr)
uniques <- liftIO $ replicateM (length recSels) (hashUnique <$> newUnique)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is newUnique threadsafe? We run handlers in individual threads, so there could well be multiple instances of this running at once.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

great

Copy link
Collaborator

Choose a reason for hiding this comment

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

we might have to worry about contention on the ioref if we end up using this everywhere, but I expect that won't manifest until we're using it a lot. We should remember to try and check when we're nearly done

plugins/hls-overloaded-record-dot-plugin/test/Main.hs Outdated Show resolved Hide resolved
plugins/hls-overloaded-record-dot-plugin/test/Main.hs Outdated Show resolved Hide resolved
Copy link
Collaborator

@michaelpj michaelpj left a comment

Choose a reason for hiding this comment

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

Nice, I like where this is going.

hls-plugin-api/src/Ide/Types.hs Outdated Show resolved Hide resolved
hls-plugin-api/src/Ide/Types.hs Outdated Show resolved Hide resolved
hls-plugin-api/src/Ide/Types.hs Outdated Show resolved Hide resolved
hls-plugin-api/src/Ide/Types.hs Outdated Show resolved Hide resolved
hls-plugin-api/src/Ide/Types.hs Outdated Show resolved Hide resolved
hls-plugin-api/src/Ide/Types.hs Outdated Show resolved Hide resolved
@joyfulmantis joyfulmantis changed the title Support for resolve in overloaded-record-dot Resolve 1: Support for resolve in overloaded-record-dot Jun 27, 2023
resolveProvider :: PluginMethodHandler IdeState 'Method_CodeActionResolve
resolveProvider ideState pId ca@(CodeAction _ _ _ _ _ _ _ (Just resData)) =
pluginResponse $ do
case fromJSON resData of
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just flagging that this seems like it's going to be a pretty much universal pattern for resolve handlers: the first thing they're going to do is decode their data from the data field. So we might want to make it part of the generic machinery.

Just rse -> pure $ ca {_edit = mkWorkspaceEdit uri rse exts pragma}
-- We need to throw a content modified error here, but we need fendor's
-- plugin error response pr to make it convenient to use here.
_ -> throwE "Content Modified Error"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looking at the spec, I'm not sure this is the correct meaning of ContentModified. I don't think there's actually anything wrong with the client sending us a document change notification in between asking for code actions and resolving them, even if we can't handle that. Perhaps just RequestFailed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It was mentioned in a GitHub issue that this was the way to do it. microsoft/language-server-protocol#1738

Copy link
Collaborator

Choose a reason for hiding this comment

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

Okay, let's link to that when we do this!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Bonus points: make a PR upstream to clarify in the spec that this is what you should do!

@michaelpj michaelpj added the merge me Label to trigger pull request merge label Jun 29, 2023
@mergify mergify bot merged commit 6f775e9 into haskell:master Jun 30, 2023
44 checks passed
@fendor fendor mentioned this pull request Aug 8, 2023
19 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge me Label to trigger pull request merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants