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

Add RangeMap for unified "in-range" filtering #3343

Merged
merged 13 commits into from Nov 26, 2022

Conversation

ozkutuk
Copy link
Collaborator

@ozkutuk ozkutuk commented Nov 18, 2022

This PR makes RangeMap a part of the plugin API, first introduced as part of hls-explicit-record-fields-plugin. RangeMap is a data structure that allows for fast queries of range intersections. Details on usage can be found in the provided documentation, and the performance characteristics compared to the traditional list-based approach can be found in the newly-added benchmark.

Browsing through existing plugins, I couldn't find as much opportunities to use this as I hoped to (without significantly modifying plugin source), since to make the best use of this, a plugin needs to:

  1. do range filtering
  2. utilize custom Shake rules (for the reason specified in the documentation)

However, I still think this lays down a nice foundation for future plugins, if they were to need such a functionality.


Screenshot from the output of: cabal bench hls-plugin-api -O2 --benchmark-options="-o benchmark.html"

benchmark

@@ -57,6 +58,7 @@ library
, transformers
, unordered-containers
, megaparsec > 9
, hw-fingertree
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not keen on adding new dependencies to his-plugin-api. Other alternatives worth considering:

  • Extract RangeMap to a standalone package
  • Leave it in its current home - plugins can depend on each other just fine

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Although I think the functionality is pretty small to warrant a standalone package, I think I can do that. I am not too keen on the second option. Even if the plugins can depend on each other, I think having this as part of a specific plugin signals the idea that this is something specific to that plugin. I think that may discourage plugin authors to use it (it certainly would discourage me).

What is the downside of adding new dependencies to hls-plugin-api?

Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the downside of adding new dependencies to hls-plugin-api?

When we update HLS for a new version of GHC, hls-plugin-api is one of the packages that needs to get updated if we are to do anything. We are frequently blocked by upstream dependencies not working with the new version of GHC; every new dependency that we add to key packages is another opportunity for that to happen...

Copy link
Collaborator

Choose a reason for hiding this comment

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

That said, hw-fingertree depends only on base, which is promising.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I see. Indeed, I had to add GHC 9.4 support to hw-fingertree myself, so I can definitely see that happening 😅 Then I will extract this into a separate small package.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Personally I suggest extracting it to a module in hls-plugin-api or something. Having a whole other package is a pain in its own right!

Also, if it would require approximately the same amount of code as is in hw-fingertree then we may not benefit much.

Here's a compromise thought: since RangeMap is exactly supposed to be a faster alternative to a list-based approach, what if we add a cabal flag to hls-plugin-api use-fingertree, such that if it is on, RangeMap is backed by a fingertree, and if it is off, RangeMap is backed by a list. Then we can turn it off for newer GHC versions until we get a working hw-fingertree?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Personally I suggest extracting it to a module in hls-plugin-api or something.

I am a bit confused by this. Do you suggest something other than what currently is present in the PR? Because it currently is a module within hls-plugin-api (unless there is a separate concept of module which I'm not aware).

what if we add a cabal flag to hls-plugin-api use-fingertree

Sounds good to me. What do you think @pepeiborra?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, I think I misunderstood. I thought we were talking about extracting the fingertree code from hw-fingertree so we controlled it, rather than extracting RangeMap. Ignore me, then.

what if we add a cabal flag to hls-plugin-api use-fingertree

... but I still think this is the cleanest solution.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have put the fingertree implementation behind a flag. All related tests (tests of RangeMap itself and the plugins that make use of it) pass with both implementations. Can you take another look to see if it reflects what you had in mind? @michaelpj

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.

I like it!

hls-plugin-api/src/Ide/Plugin/RangeMap.hs Show resolved Hide resolved
hls-plugin-api/src/Ide/Plugin/RangeMap.hs Outdated Show resolved Hide resolved
@@ -52,7 +54,7 @@ instance NFData CollectLiterals
type instance RuleResult CollectLiterals = CollectLiteralsResult

data CollectLiteralsResult = CLR
{ literals :: [Literal]
{ literals :: RangeMap Literal
Copy link
Collaborator

Choose a reason for hiding this comment

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

nice

@@ -29,7 +29,7 @@ test :: TestTree
test = testGroup "alternateNumberFormat" [
codeActionHex "TIntDtoH" 3 13
, codeActionOctal "TIntDtoO" 3 13
, codeActionBinary "TIntDtoB" 4 12
, codeActionBinary "TIntDtoB" 4 13
Copy link
Collaborator

Choose a reason for hiding this comment

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

what happened here?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't cause any worries, it's just the column number to run the code action. I think the column number is arbitrarily in the middle of a number. Looks like it just matches the other test cases.

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.

I think this just needs a couple of comments and it's good to go!

@michaelpj michaelpj enabled auto-merge (squash) November 25, 2022 20:09
@michaelpj michaelpj merged commit 9e5fc88 into haskell:master Nov 26, 2022
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.

None yet

4 participants