diff --git a/platform/core-api/src/com/intellij/navigation/TargetPresentation.java b/platform/core-api/src/com/intellij/navigation/TargetPresentation.java
new file mode 100644
index 000000000000..e653face7cb6
--- /dev/null
+++ b/platform/core-api/src/com/intellij/navigation/TargetPresentation.java
@@ -0,0 +1,59 @@
+// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.navigation;
+
+import com.intellij.openapi.editor.markup.TextAttributes;
+import org.jetbrains.annotations.ApiStatus.Experimental;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * Represents presentation in target popup as follows:
+ * | $icon $presentable_text (in $location_text) spacer $right_text $right_icon |
+ * Elements before spacer are aligned to the left, right text and right icon are aligned to the right.
+ */
+@Experimental
+public interface TargetPresentation {
+
+ @Nullable
+ default Icon getIcon() {
+ return null;
+ }
+
+ @NotNull
+ String getPresentableText();
+
+ /**
+ * Note that returned instance's {@link TextAttributes#getBackgroundColor()} is used for the whole item background.
+ *
+ * @return attributes to highlight {@link #getPresentableText()}
+ */
+ @Nullable
+ default TextAttributes getPresentableAttributes() {
+ return null;
+ }
+
+ @Nullable
+ default String getLocationText() {
+ return null;
+ }
+
+ /**
+ * @return attributes to highlight {@link #getLocationText()}
+ */
+ @Nullable
+ default TextAttributes getLocationAttributes() {
+ return null;
+ }
+
+ @Nullable
+ default String getRightText() {
+ return null;
+ }
+
+ @Nullable
+ default Icon getRightIcon() {
+ return null;
+ }
+}
diff --git a/platform/lang-impl/src/com/intellij/ide/ui/TargetPresentationMainRenderer.kt b/platform/lang-impl/src/com/intellij/ide/ui/TargetPresentationMainRenderer.kt
new file mode 100644
index 000000000000..dce9ebe7d574
--- /dev/null
+++ b/platform/lang-impl/src/com/intellij/ide/ui/TargetPresentationMainRenderer.kt
@@ -0,0 +1,59 @@
+// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.ide.ui
+
+import com.intellij.navigation.LocationPresentation.DEFAULT_LOCATION_PREFIX
+import com.intellij.navigation.LocationPresentation.DEFAULT_LOCATION_SUFFIX
+import com.intellij.navigation.TargetPresentation
+import com.intellij.openapi.project.Project
+import com.intellij.ui.ColoredListCellRenderer
+import com.intellij.ui.JBColor
+import com.intellij.ui.SimpleTextAttributes
+import com.intellij.ui.SimpleTextAttributes.*
+import com.intellij.ui.speedSearch.SearchAwareRenderer
+import com.intellij.ui.speedSearch.SpeedSearchUtil.appendColoredFragmentForMatcher
+import com.intellij.util.text.MatcherHolder
+import com.intellij.util.ui.UIUtil
+import org.jetbrains.annotations.ApiStatus.Experimental
+import javax.swing.JList
+
+@Experimental
+abstract class TargetPresentationMainRenderer(
+ private val project: Project
+) : ColoredListCellRenderer(),
+ SearchAwareRenderer {
+
+ protected abstract fun getPresentation(value: T): TargetPresentation?
+
+ final override fun getItemSearchString(item: T): String? = getPresentation(item)?.presentableText
+
+ final override fun customizeCellRenderer(list: JList, value: T, index: Int, selected: Boolean, hasFocus: Boolean) {
+ val presentation = getPresentation(value) ?: run {
+ append("Invalid", ERROR_ATTRIBUTES)
+ return
+ }
+ val attributes = presentation.presentableAttributes
+ val bgColor = attributes?.backgroundColor ?: UIUtil.getListBackground()
+
+ icon = presentation.icon
+ background = if (selected) UIUtil.getListSelectionBackground(hasFocus) else bgColor
+
+ val nameAttributes = attributes?.let(::fromTextAttributes)
+ ?: SimpleTextAttributes(STYLE_PLAIN, list.foreground)
+ val matcher = MatcherHolder.getAssociatedMatcher(list)
+ appendColoredFragmentForMatcher(presentation.presentableText, this, nameAttributes, matcher, bgColor, selected)
+
+ presentation.locationText?.let { locationText ->
+ val locationAttributes = presentation.locationAttributes?.let {
+ merge(defaultLocationAttributes, fromTextAttributes(it))
+ } ?: defaultLocationAttributes
+ append(DEFAULT_LOCATION_PREFIX, defaultLocationAttributes)
+ append("in ", defaultLocationAttributes)
+ append(locationText, locationAttributes)
+ append(DEFAULT_LOCATION_SUFFIX, defaultLocationAttributes)
+ }
+ }
+
+ companion object {
+ private val defaultLocationAttributes = SimpleTextAttributes(STYLE_PLAIN, JBColor.GRAY)
+ }
+}
diff --git a/platform/lang-impl/src/com/intellij/ide/ui/TargetPresentationRightRenderer.kt b/platform/lang-impl/src/com/intellij/ide/ui/TargetPresentationRightRenderer.kt
new file mode 100644
index 000000000000..ca7c3898e6b5
--- /dev/null
+++ b/platform/lang-impl/src/com/intellij/ide/ui/TargetPresentationRightRenderer.kt
@@ -0,0 +1,52 @@
+// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.ide.ui
+
+import com.intellij.navigation.TargetPresentation
+import com.intellij.ui.components.JBLabel
+import com.intellij.ui.speedSearch.SearchAwareRenderer
+import com.intellij.util.ui.JBUI
+import com.intellij.util.ui.UIUtil
+import org.jetbrains.annotations.ApiStatus.Experimental
+import java.awt.Component
+import javax.swing.JList
+import javax.swing.ListCellRenderer
+import javax.swing.SwingConstants
+
+@Experimental
+internal abstract class TargetPresentationRightRenderer : ListCellRenderer, SearchAwareRenderer {
+
+ companion object {
+ private val ourBorder = JBUI.Borders.emptyRight(UIUtil.getListCellHPadding())
+ }
+
+ protected abstract fun getPresentation(value: T): TargetPresentation?
+
+ private val component = JBLabel().apply {
+ border = ourBorder
+ horizontalTextPosition = SwingConstants.LEFT
+ horizontalAlignment = SwingConstants.RIGHT // align icon to the right
+ }
+
+ final override fun getItemSearchString(item: T): String? = getPresentation(item)?.locationText
+
+ final override fun getListCellRendererComponent(list: JList,
+ value: T,
+ index: Int,
+ isSelected: Boolean,
+ cellHasFocus: Boolean): Component {
+ component.apply {
+ text = ""
+ icon = null
+ background = UIUtil.getListBackground(isSelected)
+ foreground = if (isSelected) UIUtil.getListSelectionForeground() else UIUtil.getInactiveTextColor()
+ font = list.font
+ }
+ getPresentation(value)?.let { presentation ->
+ presentation.rightText?.let { rightText ->
+ component.text = rightText
+ component.icon = presentation.rightIcon
+ }
+ }
+ return component
+ }
+}
diff --git a/platform/lang-impl/src/com/intellij/ide/ui/renderers.kt b/platform/lang-impl/src/com/intellij/ide/ui/renderers.kt
new file mode 100644
index 000000000000..59a45b600920
--- /dev/null
+++ b/platform/lang-impl/src/com/intellij/ide/ui/renderers.kt
@@ -0,0 +1,32 @@
+// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.ide.ui
+
+import com.intellij.navigation.TargetPresentation
+import com.intellij.openapi.project.Project
+import com.intellij.ui.list.LeftRightSearchAwareRenderer
+import com.intellij.ui.speedSearch.SearchAwareRenderer
+import org.jetbrains.annotations.ApiStatus.Experimental
+
+@Experimental
+fun createTargetPresentationRenderer(project: Project, presentation: (T) -> TargetPresentation?): SearchAwareRenderer {
+ val mainRenderer = createMainRenderer(project, presentation)
+ if (UISettings.instance.showIconInQuickNavigation) {
+ val rightRenderer = createRightRenderer(presentation)
+ return LeftRightSearchAwareRenderer(mainRenderer, rightRenderer)
+ }
+ else {
+ return mainRenderer
+ }
+}
+
+private fun createMainRenderer(project: Project, presentation: (T) -> TargetPresentation?): SearchAwareRenderer {
+ return object : TargetPresentationMainRenderer(project) {
+ override fun getPresentation(value: T): TargetPresentation? = presentation(value)
+ }
+}
+
+private fun createRightRenderer(presentation: (T) -> TargetPresentation?): SearchAwareRenderer {
+ return object : TargetPresentationRightRenderer() {
+ override fun getPresentation(value: T): TargetPresentation? = presentation(value)
+ }
+}