diff --git a/compiler/visualizer/common/src/org/jetbrains/kotlin/compiler/visualizer/Annotator.kt b/compiler/visualizer/common/src/org/jetbrains/kotlin/compiler/visualizer/Annotator.kt index 92a015a7cd9..5b0292d4946 100644 --- a/compiler/visualizer/common/src/org/jetbrains/kotlin/compiler/visualizer/Annotator.kt +++ b/compiler/visualizer/common/src/org/jetbrains/kotlin/compiler/visualizer/Annotator.kt @@ -6,103 +6,56 @@ package org.jetbrains.kotlin.compiler.visualizer import com.intellij.openapi.util.TextRange +import kotlin.math.max object Annotator { - class AnnotationInfo(val text: String, val globalRange: TextRange) + private const val verticalLine = "│" + private const val comment = "//" - class RangeSet { - data class Rec(val value: T, val rangeInLine: TextRange) - val localRanges = ArrayList>() + class AnnotationInfo(val text: String, val range: TextRange) - private inline fun binarySearch(range: TextRange, found: (Int, Rec?) -> U): U { - var high = localRanges.size - 1 - var low = 0 - var median = 0 - println("search: $low, $high, ins: $range") - while (low <= high) { - median = (high + low) / 2 - val element = localRanges[median] + private fun putAnnotationToLines(annotations: List, lineStart: Int, lineSize: Int): Map { + val annotationLines = mutableMapOf(0 to StringBuilder(comment + " ".repeat(lineSize - comment.length))) - println("cmp ${element.rangeInLine}") - when { - range.endOffset < element.rangeInLine.startOffset -> { - println("take low, $low, $high") - high = median - 1 - } - range.startOffset > element.rangeInLine.endOffset -> { - println("take high, $low, $high") - low = median + 1 - } - else -> { - println("conflict $median") - return found(median, element) - } - } + var prevAnnStart = Int.MAX_VALUE + var lastLevel = 1 + for (ann in annotations) { + if (ann.range.startOffset + ann.text.length >= prevAnnStart) { + lastLevel++ + } else { + lastLevel = 1 } - println("empty at $median") - return found(median, null) + + if (!annotationLines.containsKey(lastLevel)) { + annotationLines[lastLevel] = StringBuilder(comment + " ".repeat(lineSize - comment.length)) + } + val startReplace = max(comment.length, ann.range.startOffset - lineStart) + annotationLines[lastLevel] = annotationLines[lastLevel]!!.replace(startReplace, startReplace + ann.text.length, ann.text) + + for (i in 0 until lastLevel) { + annotationLines[i] = annotationLines[i]!!.replace(startReplace, startReplace + 1, verticalLine) + } + + prevAnnStart = ann.range.startOffset } - fun put(range: TextRange, element: T): Boolean { - binarySearch(range) { index, conflicting -> - return if (conflicting == null) { - localRanges.add(index, Rec(element, range)) - true - } else { - false - } - } - } - } - - private fun tryPutToAnnotationLines(annotationLines: MutableList>, annotation: Pair) { - val range = annotation.second - fun buildArrow(from: RangeSet) { - for (line in annotationLines) { - if (line == from) { - break - } - line.put(TextRange(range.startOffset, range.startOffset + 1), "|") - } - } - - for (line in annotationLines.drop(1)) { - if (line.put(range, annotation.first.text)) { - buildArrow(line) - return - } - } - annotationLines.add(RangeSet()) - tryPutToAnnotationLines(annotationLines, annotation) + return annotationLines } fun annotate(text: String, annotation: List): List { val lines = text.lines() val resultLines = mutableListOf() - val annotationLines = mutableListOf>() var lineStartOffset = 0 for (line in lines) { - annotationLines.clear() val lineEndOffset = lineStartOffset + line.length - val annotations = - annotation.filter { it.globalRange.startOffset in lineStartOffset until lineEndOffset } - .sortedByDescending { it.globalRange.startOffset } - .map { - val startInLine = it.globalRange.startOffset - lineStartOffset - it to TextRange(startInLine, startInLine + it.text.length) - } - for (ann in annotations) { - tryPutToAnnotationLines(annotationLines, ann) - } - annotationLines.asReversed().mapTo(resultLines) { - var prevOffset = 2 - println(it.localRanges) - it.localRanges.fold("//") { acc, element -> - var res = acc - res += " ".repeat(element.rangeInLine.startOffset - prevOffset) - prevOffset = element.rangeInLine.endOffset - res += element.value - res + val annotations = annotation + .filter { it.range.startOffset in lineStartOffset until lineEndOffset } + .sortedByDescending { it.range.startOffset } + + if (annotations.isNotEmpty()) { + val annotationLines = putAnnotationToLines(annotations, lineStartOffset, line.length) + annotationLines.toSortedMap(Comparator.reverseOrder()).mapTo(resultLines) { + it.value.toString() } } resultLines.add(line) diff --git a/compiler/visualizer/render-psi/src/org/jetbrains/kotlin/compiler/visualizer/PsiRenderer.kt b/compiler/visualizer/render-psi/src/org/jetbrains/kotlin/compiler/visualizer/PsiRenderer.kt index 80c9e8ab921..a9781b481f2 100644 --- a/compiler/visualizer/render-psi/src/org/jetbrains/kotlin/compiler/visualizer/PsiRenderer.kt +++ b/compiler/visualizer/render-psi/src/org/jetbrains/kotlin/compiler/visualizer/PsiRenderer.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.BindingContext.* import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.types.* class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : BaseRenderer { @@ -41,6 +42,7 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : Ba } private fun addAnnotation(text: String, range: TextRange) { + annotations.removeIf { it.range.startOffset == range.startOffset } annotations.add(Annotator.AnnotationInfo(text, range)) } @@ -85,6 +87,7 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : Ba override fun visitTypeReference(typeReference: KtTypeReference) { val type = bindingContext[TYPE, typeReference] addAnnotation(renderType(type), typeReference.textRange) + super.visitTypeReference(typeReference) } override fun visitConstantExpression(expression: KtConstantExpression) {