Skip to content

General override member(s) functionality #379

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

Merged
merged 9 commits into from
Sep 13, 2022
Merged

General override member(s) functionality #379

merged 9 commits into from
Sep 13, 2022

Conversation

themkat
Copy link
Collaborator

@themkat themkat commented Aug 4, 2022

General info

Override any member that is currently not implemented in the current class. This includes open functions, functions with default implementations, open members, abstract stuff etc. The main difference here is that this is general override member functionality, NOT JUST abstract members like the quick fix does. This is super useful for cases where you need to override a default implementation (e.g toString, Thread run, lsp4j interface methods like in TextDocumentService etc.), NOT just the unimplemented abstract ones. For me it would be super useful for my day job using Kotlin 😄 Having to google api docs to find signatures are a pain in the ass just for doing simple stuff like this.

Two small issues:

  • Implicit Any/Object methods does not seem to work. I can't really figure out why. The methods from them show up if the class has at least one superclass. Unsure on how to solve this, there are a TODO in the code at a relevant place.
  • No support for empty class bodies (i.e, no brackets). Not sure how to solve that. Probably not super important either.

I think these are very minor. Just having this functionality in place would at least make me more productive using the language server 😄

The quick fix for abstract methods and this new functionality, share a lot of code. I've moved most of the code to the override-package, as that was the most logical place to put it.

Fixes #359

This is a new protocol extension, so each lsp-client will need to have an implementation to use it. I've only done one for Emacs so far, and speaking of...

Emacs screenshots

Running lsp-kotlin-override-members on MyClass (notice that multiple selections are possible!):
image

When we click enter:
image
(yes, it is the same sample project I often use for screenshot. A boring Spring Boot demo app)

Yes, it works for external interfaces and open classes as well, don't worry 🙂 There is an example of JDK Thread in the tests (in this PR, not in Emacs)

Emacs client implementation (lsp-mode):
https://github.com/themkat/lsp-mode/blob/kotlin_overrides/clients/lsp-kotlin.el#L224

Will make a PR for it once this PR has been merged. Just in case something changes.

Will see if I can find the time to make a VSCode version or not. If not, I will make an issue in the vscode-kotlin repo with enough details to get someone else started 🙂 Really want to work on either Type hierarchy or Javadoc for Kotlin SDK and the JDK instead 😛

EDIT: Made a VSCode version, as I was bored after work yesterday: fwcd/vscode-kotlin#104

Copy link
Owner

@fwcd fwcd left a comment

Choose a reason for hiding this comment

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

Looks good, a few style suggestions here and there, but nothing too major 👍

import org.jetbrains.kotlin.types.typeUtil.asTypeProjection

// TODO: see where this should ideally be placed
private const val DEFAULT_TAB_SIZE = 4
Copy link
Owner

Choose a reason for hiding this comment

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

We currently hardcode this in the formatter too, I think there was an open issue to make this configurable, but that's probably out-of-scope for this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm. That's a great point. I can't find the exact issue, but I suggest we do this as part of another PR. Like you said, that is a bit out of scope for this PR... I prefer to keep the PRs as to-the-point as possible, which helps me keep focus on each task. 🙂 Please link the issue if you find it 🙂

Comment on lines +38 to +46
fun listOverridableMembers(file: CompiledFile, cursor: Int): List<CodeAction> {
val kotlinClass = file.parseAtPoint(cursor)

if (kotlinClass is KtClass) {
return createOverrideAlternatives(file, kotlinClass)
}

return emptyList()
}
Copy link
Owner

Choose a reason for hiding this comment

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

I think we can rewrite this to be a bit more concise

Suggested change
fun listOverridableMembers(file: CompiledFile, cursor: Int): List<CodeAction> {
val kotlinClass = file.parseAtPoint(cursor)
if (kotlinClass is KtClass) {
return createOverrideAlternatives(file, kotlinClass)
}
return emptyList()
}
fun listOverridableMembers(file: CompiledFile, cursor: Int): List<CodeAction> =
when (file.parseAtPoint(cursor)) {
is KtClass -> createOverrideAlternatives(file, kotlinClass)
else -> emptyList()
}

Copy link
Collaborator Author

@themkat themkat Aug 12, 2022

Choose a reason for hiding this comment

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

We could in theory rewrite this to use a when-expression like above (with modifications), but we need the kotlinClass-reference for the createOverrideAlternatives-call. That means the suggestion above won't compile due to unknown reference to kotlinClass. If we still need that variable, I don't think it improves much just using a when-expression instead of the if-version. Feel free to disagree 🙂 (or tell me if I'm missing something!)

Comment on lines +167 to +171
// Checks if two functions have matching parameters
private fun parametersMatch(
function: KtNamedFunction,
functionDescriptor: FunctionDescriptor
): Boolean {
Copy link
Owner

Choose a reason for hiding this comment

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

Perhaps we could rewrite this into a big functional zip? I haven't checked on the specifics though, maybe there's too much specialized logic to do that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A bit unsure about this. We loop through two different lists; value parameters and type parameters. Unsure on how much it would improve in this case, but I may miss something...

@themkat themkat requested a review from fwcd August 21, 2022 10:27
@themkat
Copy link
Collaborator Author

themkat commented Aug 25, 2022

@fwcd , do you have time to look at this again soon? Would be good to get it done while the details are still semi-fresh in my memory. Sorry for nagging, know you are probably busy 🙁

@themkat
Copy link
Collaborator Author

themkat commented Sep 8, 2022

@fwcd , bump?

@fwcd
Copy link
Owner

fwcd commented Sep 13, 2022

Lgtm, sorry for the delay. Thanks!

@fwcd fwcd merged commit eef14bd into fwcd:main Sep 13, 2022
@themkat themkat deleted the GH-359 branch September 13, 2022 16:35
@themkat
Copy link
Collaborator Author

themkat commented Sep 13, 2022

@fwcd , no problem 🙂 Sorry for nagging you. Just very eager about this project, so I can get a little impatient 🙁

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.

General override/implement member/function/variable operation
2 participants