Skip to content

Commit 4384606

Browse files
committed
Decompose parseAtPoint method
Extract a method 'contentAndOffsetFromElement' that fetches the surrounding code snippet and the text offset from a PSI element and it's parent declaration.
1 parent 266d1dd commit 4384606

File tree

1 file changed

+49
-32
lines changed

1 file changed

+49
-32
lines changed

server/src/main/kotlin/org/javacs/kt/CompiledFile.kt

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.javacs.kt
22

33
import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange
4+
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
45
import org.jetbrains.kotlin.com.intellij.psi.PsiIdentifier
56
import org.javacs.kt.position.changedRegion
67
import org.javacs.kt.position.position
@@ -31,7 +32,7 @@ class CompiledFile(
3132
* Find the type of the expression at `cursor`
3233
*/
3334
fun typeAtPoint(cursor: Int): KotlinType? {
34-
var cursorExpr = parseAtPoint(cursor)?.findParent<KtExpression>() ?: return nullResult("Couldn't find expression at ${describePosition(cursor)}")
35+
var cursorExpr = parseAtPoint(cursor, asReference = true)?.findParent<KtExpression>() ?: return nullResult("Couldn't find expression at ${describePosition(cursor)}")
3536
val surroundingExpr = expandForType(cursor, cursorExpr)
3637
val scope = scopeAtPoint(cursor) ?: return nullResult("Couldn't find scope at ${describePosition(cursor)}")
3738
return typeOfExpression(surroundingExpr, scope)
@@ -52,7 +53,7 @@ class CompiledFile(
5253
}
5354

5455
fun referenceAtPoint(cursor: Int): Pair<KtExpression, DeclarationDescriptor>? {
55-
val element = parseAtPoint(cursor)
56+
val element = parseAtPoint(cursor, asReference = true)
5657
var cursorExpr = element?.findParent<KtExpression>() ?: return nullResult("Couldn't find expression at ${describePosition(cursor)} (only found $element)")
5758
val surroundingExpr = expandForReference(cursor, cursorExpr)
5859
val scope = scopeAtPoint(cursor) ?: return nullResult("Couldn't find scope at ${describePosition(cursor)}")
@@ -79,9 +80,13 @@ class CompiledFile(
7980
}
8081

8182
/**
82-
* Parse the expression at `cursor`
83+
* Parse the expression at `cursor`.
84+
*
85+
* If the `asReference` flag is set, the method will attempt to
86+
* convert a declaration (e.g. of a class or a function) to a referencing
87+
* expression before parsing it.
8388
*/
84-
fun parseAtPoint(cursor: Int): KtElement? {
89+
fun parseAtPoint(cursor: Int, asReference: Boolean = false): KtElement? {
8590
val oldCursor = oldOffset(cursor)
8691
val oldChanged = changedRegion(parse.text, content)?.first ?: TextRange(cursor, cursor)
8792
val psi = parse.findElementAt(oldCursor) ?: return nullResult("Couldn't find anything at ${describePosition(cursor)}")
@@ -91,40 +96,52 @@ class CompiledFile(
9196

9297
LOG.debug { "PSI path: ${psi.parentsWithSelf.toList()}" }
9398

99+
val (surroundingContent, offset) = contentAndOffsetFromElement(psi, oldParent, asReference)
100+
val padOffset = " ".repeat(offset)
101+
val recompile = classPath.compiler.createFile(padOffset + surroundingContent, Paths.get("dummy.virtual" + if (isScript) ".kts" else ".kt"), kind)
102+
return recompile.findElementAt(cursor)?.findParent<KtElement>()
103+
}
104+
105+
/**
106+
* Extracts the surrounding content and the text offset from a
107+
* PSI element.
108+
*
109+
* See `parseAtPoint` for documentation of the `asReference` flag.
110+
*/
111+
private fun contentAndOffsetFromElement(psi: PsiElement, parent: KtElement, asReference: Boolean): Pair<String, Int> {
94112
var surroundingContent: String
95113
var offset: Int
96114

97-
if (oldParent is KtClass) {
98-
// Parsing class, currently only identifiers are supported
99-
if (psi.node.elementType != KtTokens.IDENTIFIER) return null
100-
101-
// Parsing class name identifier: Use a fake property with the class name as type
102-
// Otherwise the compiler/analyzer would throw an exception due to a missing TopLevelDescriptorProvider
103-
val recoveryRange = psi.textRange
104-
LOG.info("Re-parsing {}", describeRange(recoveryRange, true))
105-
106-
val prefix = "val x: "
107-
surroundingContent = prefix + psi.text
108-
offset = recoveryRange.startOffset - prefix.length
109-
} else {
110-
// Parsing some other expression
111-
val recoveryRange = oldParent.textRange
112-
LOG.info("Re-parsing {}", describeRange(recoveryRange, true))
113-
114-
surroundingContent = content.substring(recoveryRange.startOffset, content.length - (parse.text.length - recoveryRange.endOffset))
115-
offset = recoveryRange.startOffset
116-
117-
if (!((oldParent as? KtParameter)?.hasValOrVar() ?: true)) {
118-
// Prepend 'val' to (e.g. function) parameters
119-
val prefix = "val "
120-
surroundingContent = prefix + surroundingContent
121-
offset -= prefix.length
115+
if (asReference) {
116+
// Convert the declaration into a fake reference expression
117+
when {
118+
parent is KtClass && psi.node.elementType == KtTokens.IDENTIFIER -> {
119+
// Converting class name identifier: Use a fake property with the class name as type
120+
// Otherwise the compiler/analyzer would throw an exception due to a missing TopLevelDescriptorProvider
121+
val prefix = "val x: "
122+
surroundingContent = prefix + psi.text
123+
offset = psi.textRange.startOffset - prefix.length
124+
125+
return Pair(surroundingContent, offset)
126+
}
122127
}
123128
}
124129

125-
val padOffset = " ".repeat(offset)
126-
val recompile = classPath.compiler.createFile(padOffset + surroundingContent, Paths.get("dummy.virtual" + if (isScript) ".kts" else ".kt"), kind)
127-
return recompile.findElementAt(cursor)?.findParent<KtElement>()
130+
// Otherwise just use the expression
131+
val recoveryRange = parent.textRange
132+
LOG.info("Re-parsing {}", describeRange(recoveryRange, true))
133+
134+
surroundingContent = content.substring(recoveryRange.startOffset, content.length - (parse.text.length - recoveryRange.endOffset))
135+
offset = recoveryRange.startOffset
136+
137+
if (asReference && !((parent as? KtParameter)?.hasValOrVar() ?: true)) {
138+
// Prepend 'val' to (e.g. function) parameters
139+
val prefix = "val "
140+
surroundingContent = prefix + surroundingContent
141+
offset -= prefix.length
142+
}
143+
144+
return Pair(surroundingContent, offset)
128145
}
129146

130147
/**

0 commit comments

Comments
 (0)