-
Notifications
You must be signed in to change notification settings - Fork 232
New command to resolve main class - used for run/debug code lenses in editors #345
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
Changes from 3 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
0e68b8a
Basic resolve main-class command for use with run code lenses (and
themkat 29d2c96
Work in progress companion object main function support.
themkat cd01576
Clean up and finalized companion object main resolving. Also adding
themkat 2f46b72
Merge branch 'main' into GH-321
themkat d9d7b6a
Moved main resolve logic to protocol extensions instead of commands
themkat File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
package org.javacs.kt.command | ||
|
||
const val JAVA_TO_KOTLIN_COMMAND = "convertJavaToKotlin" | ||
const val RESOLVE_MAIN = "resolveMain" | ||
|
||
val ALL_COMMANDS = listOf( | ||
JAVA_TO_KOTLIN_COMMAND | ||
JAVA_TO_KOTLIN_COMMAND, | ||
RESOLVE_MAIN | ||
) |
62 changes: 62 additions & 0 deletions
62
server/src/main/kotlin/org/javacs/kt/resolve/ResolveMain.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package org.javacs.kt.resolve | ||
|
||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil | ||
import org.jetbrains.kotlin.psi.KtFile | ||
import org.jetbrains.kotlin.psi.KtNamedFunction | ||
import org.jetbrains.kotlin.psi.KtClass | ||
import org.jetbrains.kotlin.psi.KtObjectDeclaration | ||
import org.javacs.kt.CompiledFile | ||
import org.javacs.kt.position.range | ||
import org.javacs.kt.util.partitionAroundLast | ||
import com.intellij.openapi.util.TextRange | ||
|
||
|
||
fun resolveMain(file: CompiledFile): Map<String,Any> { | ||
val parsedFile = file.parse.copy() as KtFile | ||
|
||
val mainFunction = findTopLevelMainFunction(parsedFile) | ||
if(null != mainFunction) { | ||
// the KtFiles name is weird. Full path. This causes the class to have full path in name as well. Correcting to top level only | ||
parsedFile.name = parsedFile.name.partitionAroundLast("/").second.substring(1) | ||
|
||
return mapOf("mainClass" to JvmFileClassUtil.getFileClassInfoNoResolve(parsedFile).facadeClassFqName.asString(), | ||
"range" to range(file.content, mainFunction.second)) | ||
} | ||
|
||
val companionMain = findCompanionObjectMain(parsedFile) | ||
if(null != companionMain) { | ||
return mapOf( | ||
"mainClass" to (companionMain.first ?: ""), | ||
"range" to range(file.content, companionMain.second) | ||
) | ||
} | ||
|
||
return emptyMap() | ||
} | ||
|
||
// only one main method allowed top level in a file (so invalid syntax files will not show any main methods) | ||
private fun findTopLevelMainFunction(file: KtFile): Pair<String?, TextRange>? = file.declarations.find { | ||
it is KtNamedFunction && "main" == it.name | ||
}?.let { | ||
Pair(it.name, it.textRangeInParent) | ||
} | ||
|
||
// finds a top level class that contains a companion object with a main function inside | ||
private fun findCompanionObjectMain(file: KtFile): Pair<String?, TextRange>? = file.declarations.flatMap { topLevelDeclaration -> | ||
if(topLevelDeclaration is KtClass) { | ||
topLevelDeclaration.companionObjects | ||
} else { | ||
emptyList<KtObjectDeclaration>() | ||
} | ||
}.flatMap { companionObject -> | ||
companionObject.body?.children?.toList() ?: emptyList() | ||
}.mapNotNull { companionObjectInternal -> | ||
if(companionObjectInternal is KtNamedFunction && "main" == companionObjectInternal.name && companionObjectInternal.text.startsWith("@JvmStatic")) { | ||
companionObjectInternal | ||
} else { | ||
null | ||
} | ||
}.firstOrNull()?.let { | ||
// a little ugly, but because of success of the above, we know that "it" has 4 layers of parent objects (child of companion object body, companion object body, companion object, outer class) | ||
Pair((it.parent.parent.parent.parent as KtClass).fqName?.toString(), it.textRange) | ||
} |
76 changes: 76 additions & 0 deletions
76
server/src/test/kotlin/org/javacs/kt/ResolveMainCommandTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package org.javacs.kt | ||
|
||
import com.google.gson.Gson | ||
import org.eclipse.lsp4j.ExecuteCommandParams | ||
import org.eclipse.lsp4j.Position | ||
import org.eclipse.lsp4j.Range | ||
import org.junit.Test | ||
import org.junit.Assert.assertEquals | ||
import org.junit.Assert.assertNotNull | ||
import org.junit.Assert.assertNull | ||
import org.javacs.kt.command.RESOLVE_MAIN | ||
|
||
class NoMainResolve : SingleFileTestFixture("resolvemain", "NoMain.kt") { | ||
@Test | ||
fun `Should not find any main class info`() { | ||
val root = testResourcesRoot().resolve(workspaceRoot) | ||
val executeCommandParams = ExecuteCommandParams(RESOLVE_MAIN, listOf(Gson().toJsonTree(root.resolve(file).toUri().toString(), ))) | ||
|
||
val commandResult = languageServer.workspaceService.executeCommand(executeCommandParams).get() | ||
|
||
assertNotNull(commandResult) | ||
val mainInfo = commandResult as Map<String, String> | ||
assertNull(mainInfo["mainClass"]) | ||
assertEquals(root.toString(), mainInfo["projectRoot"]) | ||
} | ||
} | ||
|
||
|
||
class SimpleMainResolve : SingleFileTestFixture("resolvemain", "Simple.kt") { | ||
@Test | ||
fun `Should resolve correct main class of simple file`() { | ||
val root = testResourcesRoot().resolve(workspaceRoot) | ||
val executeCommandParams = ExecuteCommandParams(RESOLVE_MAIN, listOf(Gson().toJsonTree(root.resolve(file).toUri().toString()))) | ||
|
||
val commandResult = languageServer.workspaceService.executeCommand(executeCommandParams).get() | ||
|
||
assertNotNull(commandResult) | ||
val mainInfo = commandResult as Map<String, Any> | ||
assertEquals("test.SimpleKt", mainInfo["mainClass"]) | ||
assertEquals(Range(Position(2, 0), Position(4, 1)), mainInfo["range"]) | ||
assertEquals(root.toString(), mainInfo["projectRoot"]) | ||
} | ||
} | ||
|
||
|
||
class JvmNameAnnotationMainResolve : SingleFileTestFixture("resolvemain", "JvmNameAnnotation.kt") { | ||
@Test | ||
fun `Should resolve correct main class of file annotated with JvmName`() { | ||
val root = testResourcesRoot().resolve(workspaceRoot) | ||
val executeCommandParams = ExecuteCommandParams(RESOLVE_MAIN, listOf(Gson().toJsonTree(root.resolve(file).toUri().toString()))) | ||
|
||
val commandResult = languageServer.workspaceService.executeCommand(executeCommandParams).get() | ||
|
||
assertNotNull(commandResult) | ||
val mainInfo = commandResult as Map<String, Any> | ||
assertEquals("com.mypackage.name.Potato", mainInfo["mainClass"]) | ||
assertEquals(Range(Position(5, 0), Position(7, 1)), mainInfo["range"]) | ||
assertEquals(root.toString(), mainInfo["projectRoot"]) | ||
} | ||
} | ||
|
||
class CompanionObjectMainResolve : SingleFileTestFixture("resolvemain", "CompanionObject.kt") { | ||
@Test | ||
fun `Should resolve correct main class of main function inside companion object`() { | ||
val root = testResourcesRoot().resolve(workspaceRoot) | ||
val executeCommandParams = ExecuteCommandParams(RESOLVE_MAIN, listOf(Gson().toJsonTree(root.resolve(file).toUri().toString()))) | ||
|
||
val commandResult = languageServer.workspaceService.executeCommand(executeCommandParams).get() | ||
|
||
assertNotNull(commandResult) | ||
val mainInfo = commandResult as Map<String, Any> | ||
assertEquals("test.my.companion.SweetPotato", mainInfo["mainClass"]) | ||
assertEquals(Range(Position(8, 8), Position(11, 9)), mainInfo["range"]) | ||
assertEquals(root.toString(), mainInfo["projectRoot"]) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package test.my.companion | ||
|
||
val SOME_GLOBAL_CONSTANT = 42 | ||
|
||
fun multiplyByOne(num: Int) = num*1 | ||
|
||
class SweetPotato { | ||
companion object { | ||
@JvmStatic | ||
fun main() { | ||
println("42 multiplied by 1: ${multiplyByOne(42)}") | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
@JvmName("Potato") | ||
package com.mypackage.name | ||
|
||
val MY_CONSTANT = 1 | ||
|
||
fun main(args: Array<String>) { | ||
|
||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package no.main.found.hopefully | ||
|
||
fun multiplyByOne(num: Int) = num*1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package test | ||
|
||
fun main() { | ||
println("Hello!") | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.