-
Notifications
You must be signed in to change notification settings - Fork 34
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
feat: display up to 60 chars of the filepath in tree view #451
Changes from all commits
3206289
4ea3279
315b46e
741ebee
9bad140
a7bc3fb
79604aa
017eb1b
60972c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package io.snyk.plugin.ui.toolwindow | |
|
||
import ai.deepcode.javaclient.core.SuggestionForFile | ||
import com.intellij.icons.AllIcons | ||
import com.intellij.ide.util.gotoByName.GotoFileCellRenderer | ||
import com.intellij.openapi.util.Iconable | ||
import com.intellij.ui.ColoredTreeCellRenderer | ||
import com.intellij.ui.SimpleTextAttributes | ||
|
@@ -26,6 +27,7 @@ import io.snyk.plugin.ui.toolwindow.nodes.root.RootSecurityIssuesTreeNode | |
import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.ErrorTreeNode | ||
import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.FileTreeNode | ||
import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.SnykCodeFileTreeNode | ||
import org.jetbrains.kotlin.idea.base.util.letIf | ||
import snyk.common.ProductType | ||
import snyk.common.SnykError | ||
import snyk.container.ContainerIssue | ||
|
@@ -38,21 +40,17 @@ import snyk.iac.ui.toolwindow.IacFileTreeNode | |
import snyk.iac.ui.toolwindow.IacIssueTreeNode | ||
import snyk.oss.OssVulnerabilitiesForFile | ||
import snyk.oss.Vulnerability | ||
import java.util.Locale | ||
import java.util.* | ||
import javax.swing.Icon | ||
import javax.swing.JTree | ||
|
||
private const val MAX_FILE_TREE_NODE_LENGTH = 60 | ||
|
||
class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | ||
@Suppress("UNCHECKED_CAST") | ||
override fun customizeCellRenderer( | ||
tree: JTree, | ||
value: Any, | ||
selected: Boolean, | ||
expanded: Boolean, | ||
leaf: Boolean, | ||
row: Int, | ||
hasFocus: Boolean | ||
tree: JTree, value: Any, selected: Boolean, expanded: Boolean, leaf: Boolean, row: Int, hasFocus: Boolean | ||
) { | ||
|
||
var nodeIcon: Icon? = null | ||
var text: String? = null | ||
var attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES | ||
|
@@ -68,18 +66,32 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | |
nodeIcon = getDisabledIcon(nodeIcon) | ||
} | ||
} | ||
|
||
is FileTreeNode -> { | ||
val fileVulns = value.userObject as OssVulnerabilitiesForFile | ||
nodeIcon = PackageManagerIconProvider.getIcon(fileVulns.packageManager.lowercase(Locale.getDefault())) | ||
text = fileVulns.sanitizedTargetFile + ProductType.OSS.getCountText(value.childCount) | ||
val relativePath = fileVulns.virtualFile?.let { | ||
GotoFileCellRenderer.getRelativePath( | ||
fileVulns.virtualFile, value.project | ||
) | ||
} ?: "" | ||
toolTipText = | ||
relativePath + fileVulns.sanitizedTargetFile + ProductType.OSS.getCountText(value.childCount) | ||
|
||
text = toolTipText.letIf(toolTipText.length > MAX_FILE_TREE_NODE_LENGTH) { | ||
"..." + it.substring( | ||
it.length - MAX_FILE_TREE_NODE_LENGTH, it.length | ||
) | ||
} | ||
|
||
val snykCachedResults = getSnykCachedResults(value.project) | ||
if (snykCachedResults?.currentOssResults == null) { | ||
attributes = SimpleTextAttributes.GRAYED_ATTRIBUTES | ||
text += obsoleteSuffix | ||
text += OBSOLETE_SUFFIX | ||
nodeIcon = getDisabledIcon(nodeIcon) | ||
} | ||
} | ||
|
||
is SuggestionTreeNode -> { | ||
val (suggestion, index) = value.userObject as Pair<SuggestionForFile, Int> | ||
nodeIcon = SnykIcons.getSeverityIcon(suggestion.getSeverityAsEnum()) | ||
|
@@ -94,31 +106,55 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | |
nodeIcon = getDisabledIcon(nodeIcon) | ||
} | ||
} | ||
|
||
is SnykCodeFileTreeNode -> { | ||
val (file, productType) = value.userObject as Pair<SnykCodeFile, ProductType> | ||
text = PDU.toSnykCodeFile(file).virtualFile.name + productType.getCountText(value.childCount) | ||
toolTipText = | ||
GotoFileCellRenderer.getRelativePath(file.virtualFile, file.project) + productType.getCountText( | ||
value.childCount | ||
) | ||
|
||
text = toolTipText.letIf(toolTipText.length > MAX_FILE_TREE_NODE_LENGTH) { | ||
"..." + it.substring( | ||
it.length - MAX_FILE_TREE_NODE_LENGTH, it.length | ||
) | ||
} | ||
|
||
Comment on lines
+112
to
+122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may be interesting for review |
||
val psiFile = PDU.toPsiFile(file) | ||
nodeIcon = psiFile?.getIcon(Iconable.ICON_FLAG_READ_STATUS) | ||
if (!AnalysisData.instance.isFileInCache(file)) { | ||
attributes = SimpleTextAttributes.GRAYED_ATTRIBUTES | ||
text += obsoleteSuffix | ||
text += OBSOLETE_SUFFIX | ||
nodeIcon = getDisabledIcon(nodeIcon) | ||
} | ||
} | ||
|
||
is IacFileTreeNode -> { | ||
val iacVulnerabilitiesForFile = value.userObject as IacIssuesForFile | ||
nodeIcon = PackageManagerIconProvider.getIcon( | ||
iacVulnerabilitiesForFile.packageManager.lowercase(Locale.getDefault()) | ||
) | ||
text = iacVulnerabilitiesForFile.targetFile + ProductType.IAC.getCountText(value.childCount) | ||
val relativePath = iacVulnerabilitiesForFile.virtualFile?.let { | ||
GotoFileCellRenderer.getRelativePath( | ||
iacVulnerabilitiesForFile.virtualFile, value.project | ||
) | ||
} ?: iacVulnerabilitiesForFile.targetFilePath | ||
toolTipText = relativePath + ProductType.IAC.getCountText(value.childCount) | ||
|
||
text = toolTipText.letIf(toolTipText.length > MAX_FILE_TREE_NODE_LENGTH) { | ||
"..." + it.substring( | ||
it.length - MAX_FILE_TREE_NODE_LENGTH, it.length | ||
) | ||
} | ||
Comment on lines
+137
to
+148
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may be interesting for review |
||
|
||
val snykCachedResults = getSnykCachedResults(value.project) | ||
if (snykCachedResults?.currentIacResult == null || iacVulnerabilitiesForFile.obsolete) { | ||
attributes = SimpleTextAttributes.GRAYED_ATTRIBUTES | ||
nodeIcon = getDisabledIcon(nodeIcon) | ||
text += obsoleteSuffix | ||
text += OBSOLETE_SUFFIX | ||
} | ||
} | ||
|
||
is ContainerImageTreeNode -> { | ||
val issuesForImage = value.userObject as ContainerIssuesForImage | ||
nodeIcon = SnykIcons.CONTAINER_IMAGE | ||
|
@@ -128,43 +164,47 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | |
if (snykCachedResults?.currentContainerResult == null || issuesForImage.obsolete) { | ||
attributes = SimpleTextAttributes.GRAYED_ATTRIBUTES | ||
nodeIcon = getDisabledIcon(nodeIcon) | ||
text += obsoleteSuffix | ||
text += OBSOLETE_SUFFIX | ||
} | ||
} | ||
|
||
is ErrorTreeNode -> { | ||
val snykError = value.userObject as SnykError | ||
text = snykError.path + " - " + snykError.message | ||
nodeIcon = AllIcons.General.Error | ||
} | ||
|
||
is IacIssueTreeNode -> { | ||
val issue = (value.userObject as IacIssue) | ||
val snykCachedResults = getSnykCachedResults(value.project) | ||
nodeIcon = SnykIcons.getSeverityIcon(issue.getSeverity()) | ||
val prefix = if (issue.lineNumber > 0) "line ${issue.lineNumber}: " else "" | ||
text = prefix + issue.title + when { | ||
issue.ignored -> ignoredSuffix | ||
snykCachedResults?.currentIacResult == null || issue.obsolete -> obsoleteSuffix | ||
issue.ignored -> IGNORED_SUFFIX | ||
snykCachedResults?.currentIacResult == null || issue.obsolete -> OBSOLETE_SUFFIX | ||
else -> "" | ||
} | ||
if (snykCachedResults?.currentIacResult == null || issue.ignored || issue.obsolete) { | ||
attributes = SimpleTextAttributes.GRAYED_ATTRIBUTES | ||
nodeIcon = getDisabledIcon(nodeIcon) | ||
} | ||
} | ||
|
||
is ContainerIssueTreeNode -> { | ||
val issue = value.userObject as ContainerIssue | ||
val snykCachedResults = getSnykCachedResults(value.project) | ||
nodeIcon = SnykIcons.getSeverityIcon(issue.getSeverity()) | ||
text = issue.title + when { | ||
issue.ignored -> ignoredSuffix | ||
snykCachedResults?.currentContainerResult == null || issue.obsolete -> obsoleteSuffix | ||
issue.ignored -> IGNORED_SUFFIX | ||
snykCachedResults?.currentContainerResult == null || issue.obsolete -> OBSOLETE_SUFFIX | ||
else -> "" | ||
} | ||
if (snykCachedResults?.currentContainerResult == null || issue.ignored || issue.obsolete) { | ||
attributes = SimpleTextAttributes.GRAYED_ATTRIBUTES | ||
nodeIcon = getDisabledIcon(nodeIcon) | ||
} | ||
} | ||
|
||
is RootOssTreeNode -> { | ||
val settings = pluginSettings() | ||
if (settings.ossScanEnable && settings.treeFiltering.ossResults) { | ||
|
@@ -177,9 +217,10 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | |
text = if (settings.ossScanEnable) { | ||
value.userObject.toString() | ||
} else { | ||
SnykToolWindowPanel.OSS_ROOT_TEXT + disabledSuffix | ||
SnykToolWindowPanel.OSS_ROOT_TEXT + DISABLED_SUFFIX | ||
} | ||
} | ||
|
||
is RootSecurityIssuesTreeNode -> { | ||
val settings = pluginSettings() | ||
if (settings.snykCodeSecurityIssuesScanEnable && settings.treeFiltering.codeSecurityResults) { | ||
|
@@ -192,10 +233,11 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | |
text = if (settings.snykCodeSecurityIssuesScanEnable) { | ||
value.userObject.toString() | ||
} else { | ||
SnykToolWindowPanel.CODE_SECURITY_ROOT_TEXT + | ||
snykCodeAvailabilityPostfix().ifEmpty { disabledSuffix } | ||
SnykToolWindowPanel.CODE_SECURITY_ROOT_TEXT + snykCodeAvailabilityPostfix() | ||
.ifEmpty { DISABLED_SUFFIX } | ||
} | ||
} | ||
|
||
is RootQualityIssuesTreeNode -> { | ||
val settings = pluginSettings() | ||
if (settings.snykCodeQualityIssuesScanEnable && settings.treeFiltering.codeQualityResults) { | ||
|
@@ -208,10 +250,11 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | |
text = if (settings.snykCodeQualityIssuesScanEnable) { | ||
value.userObject.toString() | ||
} else { | ||
SnykToolWindowPanel.CODE_QUALITY_ROOT_TEXT + | ||
snykCodeAvailabilityPostfix().ifEmpty { disabledSuffix } | ||
SnykToolWindowPanel.CODE_QUALITY_ROOT_TEXT + snykCodeAvailabilityPostfix() | ||
.ifEmpty { DISABLED_SUFFIX } | ||
} | ||
} | ||
|
||
is RootIacIssuesTreeNode -> { | ||
val settings = pluginSettings() | ||
if (settings.iacScanEnabled && settings.treeFiltering.iacResults) { | ||
|
@@ -224,9 +267,10 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | |
text = if (settings.iacScanEnabled) { | ||
value.userObject.toString() | ||
} else { | ||
SnykToolWindowPanel.IAC_ROOT_TEXT + disabledSuffix | ||
SnykToolWindowPanel.IAC_ROOT_TEXT + DISABLED_SUFFIX | ||
} | ||
} | ||
|
||
is RootContainerIssuesTreeNode -> { | ||
val settings = pluginSettings() | ||
if (settings.containerScanEnabled && settings.treeFiltering.containerResults) { | ||
|
@@ -239,7 +283,7 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | |
text = if (settings.containerScanEnabled) { | ||
value.userObject.toString() | ||
} else { | ||
SnykToolWindowPanel.CONTAINER_ROOT_TEXT + disabledSuffix | ||
SnykToolWindowPanel.CONTAINER_ROOT_TEXT + DISABLED_SUFFIX | ||
} | ||
} | ||
} | ||
|
@@ -249,8 +293,8 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { | |
} | ||
|
||
companion object { | ||
private const val obsoleteSuffix = " (obsolete)" | ||
private const val ignoredSuffix = " (ignored)" | ||
private const val disabledSuffix = " (disabled in Settings)" | ||
private const val OBSOLETE_SUFFIX = " (obsolete)" | ||
private const val IGNORED_SUFFIX = " (ignored)" | ||
private const val DISABLED_SUFFIX = " (disabled in Settings)" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
package snyk.iac | ||
|
||
import com.intellij.openapi.vfs.LocalFileSystem | ||
import com.intellij.openapi.vfs.VirtualFile | ||
|
||
data class IacIssuesForFile( | ||
val infrastructureAsCodeIssues: List<IacIssue>, | ||
val targetFile: String, | ||
|
@@ -9,6 +12,8 @@ data class IacIssuesForFile( | |
val obsolete: Boolean get() = infrastructureAsCodeIssues.any { it.obsolete } | ||
val ignored: Boolean get() = infrastructureAsCodeIssues.all { it.ignored } | ||
val uniqueCount: Int get() = infrastructureAsCodeIssues.groupBy { it.id }.size | ||
|
||
val virtualFile: VirtualFile? = LocalFileSystem.getInstance().findFileByPath(this.targetFilePath) | ||
Comment on lines
+15
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may be interesting for review |
||
} | ||
|
||
/* Real json Example: src/integTest/resources/iac-test-results/infrastructure-as-code-goof.json */ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
package snyk.oss | ||
|
||
import com.intellij.openapi.vfs.LocalFileSystem | ||
import com.intellij.openapi.vfs.VirtualFile | ||
|
||
data class OssVulnerabilitiesForFile( | ||
val vulnerabilities: List<Vulnerability>, | ||
private val displayTargetFile: String, | ||
|
@@ -11,6 +14,8 @@ data class OssVulnerabilitiesForFile( | |
|
||
val sanitizedTargetFile: String get() = displayTargetFile.replace("-lock", "") | ||
|
||
val virtualFile: VirtualFile? = LocalFileSystem.getInstance().findFileByPath(this.path) | ||
|
||
Comment on lines
+17
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may be interesting for review |
||
fun toGroupedResult(): OssGroupedResult { | ||
val id2vulnerabilities = vulnerabilities.groupBy({ it.id }, { it }) | ||
val uniqueCount = id2vulnerabilities.keys.size | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be interesting for review