diff --git a/idea/src/org/jetbrains/kotlin/idea/goto/SearchEverywherePsiRenderer.kt b/idea/src/org/jetbrains/kotlin/idea/goto/SearchEverywherePsiRenderer.kt index 5e2b5d5faf2..812f2643c94 100644 --- a/idea/src/org/jetbrains/kotlin/idea/goto/SearchEverywherePsiRenderer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/goto/SearchEverywherePsiRenderer.kt @@ -16,66 +16,165 @@ package org.jetbrains.kotlin.idea.goto -import com.intellij.ide.util.DefaultPsiElementCellRenderer +import com.intellij.ide.util.PlatformModuleRendererFactory +import com.intellij.ide.util.PsiElementListCellRenderer +import com.intellij.ide.util.gotoByName.GotoFileCellRenderer +import com.intellij.navigation.NavigationItem +import com.intellij.openapi.util.Iconable +import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFileSystemItem import com.intellij.psi.presentation.java.SymbolPresentationUtil +import com.intellij.ui.ColoredListCellRenderer +import com.intellij.ui.JBColor +import com.intellij.ui.SimpleTextAttributes +import com.intellij.util.ui.FilePathSplittingPolicy import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy +import java.awt.BorderLayout +import java.awt.Container +import java.io.File import java.util.* +import javax.swing.DefaultListCellRenderer import javax.swing.JList -class KotlinSearchEverywherePsiRenderer(private val list: JList<*>) : DefaultPsiElementCellRenderer() { - private val RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.withOptions { - parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE - modifiers = emptySet() - startFromName = false +// Mostly copied from com.intellij.ide.actions.SearchEverywherePsiRenderer +// TODO: Drop copied code when SearchEverywherePsiRenderer becomes public +internal class KotlinSearchEverywherePsiRenderer(private val myList: JList<*>) : PsiElementListCellRenderer() { + companion object { + private val RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.withOptions { + parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE + modifiers = emptySet() + startFromName = false + } } - override fun getElementText(element: PsiElement?): String { - if (element is KtNamedFunction) { - val descriptor = element.resolveToDescriptorIfAny() - if (descriptor != null) { - return buildString { - descriptor.extensionReceiverParameter?.let { append(RENDERER.renderType(it.type)).append('.') } - append(element.name) - descriptor.valueParameters.joinTo(this, prefix = "(", postfix = ")") { RENDERER.renderType(it.type) } + init { + setFocusBorderEnabled(false) + layout = object : BorderLayout() { + override fun layoutContainer(target: Container) { + super.layoutContainer(target) + val right = getLayoutComponent(BorderLayout.EAST) + val left = getLayoutComponent(BorderLayout.WEST) + + //IDEA-140824 + if (right != null && left != null && left.bounds.x + left.bounds.width > right.bounds.x) { + val bounds = right.bounds + val newX = left.bounds.x + left.bounds.width + right.setBounds(newX, bounds.y, bounds.width - (newX - bounds.x), bounds.height) } } } - return super.getElementText(element) } - // Mostly copied from SearchEverywherePsiRenderer - override fun getContainerText(element: PsiElement?, name: String?): String? { - var text = SymbolPresentationUtil.getSymbolContainerText(element) ?: return null - if (list.width == 0) return text + override fun getElementText(element: PsiElement?): String { + if (element !is KtNamedFunction) return "" + val descriptor = element.resolveToDescriptorIfAny() ?: return "" + return buildString { + descriptor.extensionReceiverParameter?.let { append(RENDERER.renderType(it.type)).append('.') } + append(element.name) + descriptor.valueParameters.joinTo(this, prefix = "(", postfix = ")") { RENDERER.renderType(it.type) } + } + } + + override fun getContainerText(element: PsiElement, name: String): String? { + if (element is PsiFileSystemItem) { + val file = element.virtualFile + val parent = file?.parent + if (parent == null) { + if (file != null) { // use fallback from Switcher + val presentableUrl = file.presentableUrl + return FileUtil.getLocationRelativeToUserHome(presentableUrl) + } + return null + } + val relativePath = GotoFileCellRenderer.getRelativePath(parent, element.getProject()) ?: return "( " + File.separator + " )" + var width = myList.width + if (width == 0) width += 800 + val path = FilePathSplittingPolicy.SPLIT_BY_SEPARATOR.getOptimalTextForComponent( + name, + File(relativePath), + this, + width - myRightComponentWidth - 16 - 10 + ) + return "($path)" + } + return getSymbolContainerText(name, element) + } + + private fun getSymbolContainerText(name: String, element: PsiElement): String? { + var text = SymbolPresentationUtil.getSymbolContainerText(element) + + if (myList.width == 0) return text + if (text == null) return null if (text.startsWith("(") && text.endsWith(")")) { text = text.substring(1, text.length - 1) } - - val inIndex = text.indexOf("in ") - if (inIndex >= 0) text = text.substring(inIndex + 3) - val fm = list.getFontMetrics(list.font) - val maxWidth = list.width - fm.stringWidth(name) - 16 - myRightComponentWidth - 20 - val left = if (inIndex >= 0) "(in " else "(" + val `in` = text.startsWith("in ") + if (`in`) text = text.substring(3) + val fm = myList.getFontMetrics(myList.font) + val maxWidth = myList.width - fm.stringWidth(name) - 16 - myRightComponentWidth - 20 + val left = if (`in`) "(in " else "(" val right = ")" if (fm.stringWidth(left + text + right) < maxWidth) return left + text + right - val parts = LinkedList(StringUtil.split(text, ".")) + val separator = if (text.contains(File.separator)) File.separator else "." + val parts = LinkedList(StringUtil.split(text, separator)) var index: Int while (parts.size > 1) { index = parts.size / 2 - 1 parts.removeAt(index) - if (fm.stringWidth(StringUtil.join(parts, ".") + "...") < maxWidth) { - parts.add(index, if (index == 0) "..." else ".") - return left + StringUtil.join(parts, ".") + right + if (fm.stringWidth(StringUtil.join(parts, separator) + "...") < maxWidth) { + parts.add(index, "...") + return left + StringUtil.join(parts, separator) + right } } - - return left + "..." + right + //todo + return "$left...$right" } -} + + + override fun customizeNonPsiElementLeftRenderer( + renderer: ColoredListCellRenderer<*>?, + list: JList<*>?, + value: Any?, + index: Int, + selected: Boolean, + hasFocus: Boolean + ): Boolean { + if (value !is NavigationItem) return false + + val item = value as NavigationItem? + + val attributes = getNavigationItemAttributes(item) + + var nameAttributes: SimpleTextAttributes? = if (attributes != null) SimpleTextAttributes.fromTextAttributes(attributes) else null + + val color = list!!.foreground + if (nameAttributes == null) nameAttributes = SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, color) + + renderer!!.append(item!!.toString() + " ", nameAttributes) + val itemPresentation = item.presentation!! + renderer.icon = itemPresentation.getIcon(true) + + val locationString = itemPresentation.locationString + if (!StringUtil.isEmpty(locationString)) { + renderer.append(locationString!!, SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, JBColor.GRAY)) + } + return true + } + + override fun getRightCellRenderer(value: Any): DefaultListCellRenderer? { + val rightRenderer = super.getRightCellRenderer(value) + return if (rightRenderer is PlatformModuleRendererFactory.PlatformModuleRenderer) { + // that renderer will display file path, but we're showing it ourselves - no need to show twice + null + } else rightRenderer + } + + override fun getIconFlags() = Iconable.ICON_FLAG_READ_STATUS +} \ No newline at end of file