Skip to content
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: select treenode on snyk showdocument [IDE-957] #260

Merged
merged 21 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
### Changes
- add option for using a folder as reference instead of a branch in net-new scanning
- add scan summary to custom UI, updating scan statuses live
- added support for DeepCode AI Fixes

## [3.0.0]
### Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
Expand Down Expand Up @@ -120,9 +119,10 @@ public String replaceCssVariables(String html) {
getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0"));
htmlStyled = htmlStyled.replace("var(--circle-color)",
getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0"));

htmlStyled = htmlStyled.replace("var(--border-color)",
getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC"));
htmlStyled = htmlStyled.replace("var(--input-border)",
getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC"));
htmlStyled = htmlStyled.replace("var(--link-color)", getColorAsHex("ACTIVE_HYPERLINK_COLOR", "#0000FF"));
htmlStyled = htmlStyled.replace("var(--horizontal-border-color)",
getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC"));
Expand All @@ -142,8 +142,10 @@ private String getScaledFontSize() {
} catch (IllegalStateException e) {
defaultHeight = 13;
}
// Language server HTML assumes a base font size of 10px. The default Eclipse font size is 17px (13pt), so we
// apply a scaling factor here. This ensures that HTML fonts scale correctly if the user changes the text size.
// Language server HTML assumes a base font size of 10px. The default Eclipse
// font size is 17px (13pt), so we
// apply a scaling factor here. This ensures that HTML fonts scale correctly if
// the user changes the text size.
int scaledHeight = (int) (defaultHeight / 1.7);
return scaledHeight + "pt";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io.snyk.eclipse.plugin.preferences.Preferences;

public class CodeHtmlProvider extends BaseHtmlProvider {
private static CodeHtmlProvider instance = new CodeHtmlProvider();
private static CodeHtmlProvider instance = new CodeHtmlProvider();

public static CodeHtmlProvider getInstance() {
synchronized (CodeHtmlProvider.class) {
Expand All @@ -16,61 +16,80 @@ public static CodeHtmlProvider getInstance() {
return instance;
}

@Override
public String getInitScript() {
String themeScript = getThemeScript();
String initScript = super.getInitScript();
return initScript + "\n" + """
function navigateToIssue(e, target) {
e.preventDefault();
var filePath = target.getAttribute('file-path');
var startLine = target.getAttribute('start-line');
var endLine = target.getAttribute('end-line');
var startCharacter = target.getAttribute('start-character');
var endCharacter = target.getAttribute('end-character');
window.openInEditor(filePath, startLine, endLine, startCharacter, endCharacter);
}
var navigatableLines = document.getElementsByClassName('data-flow-clickable-row');
for(var i = 0; i < navigatableLines.length; i++) {
navigatableLines[i].onclick = function(e) {
navigateToIssue(e, this);
return false;
};
}
if(document.getElementById('position-line')) {
document.getElementById('position-line').onclick = function(e) {
var target = navigatableLines[0];
if(target) {
navigateToIssue(e, target);
}
}
}
// Disable AIfix
if(document.getElementById('ai-fix-wrapper') && document.getElementById('no-ai-fix-wrapper')){
document.getElementById('ai-fix-wrapper').className = 'hidden';
document.getElementById('no-ai-fix-wrapper').className = '';
}
""" + themeScript;
}
@Override
public String getInitScript() {
String themeScript = getThemeScript();
String initScript = super.getInitScript();
return initScript + "\n" + """
function navigateToIssue(e, target) {
e.preventDefault();
var filePath = target.getAttribute('file-path');
var startLine = target.getAttribute('start-line');
var endLine = target.getAttribute('end-line');
var startCharacter = target.getAttribute('start-character');
var endCharacter = target.getAttribute('end-character');
window.openInEditor(filePath, startLine, endLine, startCharacter, endCharacter);
}
var navigatableLines = document.getElementsByClassName('data-flow-clickable-row');
for(var i = 0; i < navigatableLines.length; i++) {
navigatableLines[i].onclick = function(e) {
navigateToIssue(e, this);
return false;
};
}
if(document.getElementById('position-line')) {
document.getElementById('position-line').onclick = function(e) {
var target = navigatableLines[0];
if(target) {
navigateToIssue(e, target);
}
}
}
""" + themeScript;
}

private String getThemeScript() {
if (Preferences.getInstance().isTest()) {
return "";
}

String themeScript = "var isDarkTheme = " + isDarkTheme() + ";\n"
+ "document.body.classList.add(isDarkTheme ? 'dark' : 'light');";
return themeScript;
}

@Override
public String replaceCssVariables(String html) {
String htmlStyled = super.replaceCssVariables(html);

// Replace CSS variables with actual color values
htmlStyled = htmlStyled.replace("var(--example-line-removed-color)",
super.getColorAsHex("DELETION_COLOR", "#ff0000"));
htmlStyled = htmlStyled.replace("var(--example-line-added-color)",
super.getColorAsHex("ADDITION_COLOR", "#00ff00"));
htmlStyled = htmlStyled.replace("var(--generated-ai-fix-button-background-color)",
super.getColorAsHex("BUTTON_COLOR", "#375578"));
htmlStyled = htmlStyled.replace("var(--disabled-background-color)",
super.getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC"));

private String getThemeScript() {
if(Preferences.getInstance().isTest()) {
return "";
}
String htmlWithScripts = replaceAIFixScripts(htmlStyled);

String themeScript = "var isDarkTheme = " + isDarkTheme() + ";\n" +
"document.body.classList.add(isDarkTheme ? 'dark' : 'light');";
return themeScript;
}
return htmlWithScripts;
}

private String replaceAIFixScripts(String html) {
String htmlWithAiFixScripts = html.replace("${ideGenerateAIFix}", getGenerateAiFixScript());
htmlWithAiFixScripts = htmlWithAiFixScripts.replace("${ideApplyAIFix}", getApplyAiFixScript());

return htmlWithAiFixScripts;
}

@Override
public String replaceCssVariables(String html) {
String htmlStyled = super.replaceCssVariables(html);
// Replace CSS variables with actual color values
htmlStyled = htmlStyled.replace("var(--example-line-removed-color)", super.getColorAsHex("DELETION_COLOR", "#ff0000"));
htmlStyled = htmlStyled.replace("var(--example-line-added-color)", super.getColorAsHex("ADDITION_COLOR", "#00ff00"));
private String getGenerateAiFixScript() {
return "window.ideGenAIFix(generateFixQueryString)\n;";
}

private String getApplyAiFixScript() {
return "window.ideApplyFix(fixId);\n";
}

return htmlStyled;
}
}
78 changes: 78 additions & 0 deletions plugin/src/main/java/io/snyk/eclipse/plugin/utils/UriUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.snyk.eclipse.plugin.utils;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class UriUtils {

/**
* Retrieves a parameter value from the given URI.
*
* If the query string does not contain the specified parameter or is
* null/empty, this method returns null.
*
* @param uri
* The URI to retrieve parameters from
* @param paramName
* The name of the parameter to retrieve
* @return The parameter value if it exists in the query string, otherwise
* null
*/
public static String getDecodedParam(URI uri, String paramName) {
String query = uri.getQuery();
if (query == null || query.isEmpty()) {
return null;
}

Map<String, String> paramMap = getQueryParameters(query);
String value = paramMap.get(paramName);

if (value == null) {
return null;
}

return value;
}

/**
* Parses a query string into a map of key-value pairs, decoding any
* URL-encoded values.
*
* If the input is null or empty, an empty map is returned. If an encoding
* error occurs while decoding a value, an error is logged and an empty map
* is returned.
*
* @param queryString
* The query string to parse
* @return A map containing the decoded parameters
*/
public static Map<String, String> getQueryParameters(String queryString) {
Map<String, String> paramMap = new HashMap<>();

if (queryString == null || queryString.isEmpty()) {
return paramMap;
}

for (String param : queryString.split("&")) {
if (!param.isEmpty()) {
String[] keyValue = param.split("=");

if (keyValue.length == 2) { // NOPMD, AvoidLiteralsInIfCondition
try {
paramMap.put(keyValue[0],
URLDecoder.decode(keyValue[1], "UTF-8"));
} catch (UnsupportedEncodingException e) {
SnykLogger.logError(e);
return Collections.emptyMap();
}
}
}
}

return paramMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.apache.commons.lang3.StringUtils.isEmpty;

import java.nio.file.Paths;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;

import org.eclipse.core.commands.common.CommandException;
Expand Down Expand Up @@ -31,6 +32,7 @@
import io.snyk.eclipse.plugin.utils.SnykLogger;
import io.snyk.eclipse.plugin.views.snyktoolview.handlers.IHandlerCommands;
import io.snyk.eclipse.plugin.wizards.SnykWizard;
import io.snyk.languageserver.protocolextension.SnykExtendedLanguageClient;

@SuppressWarnings("restriction")
public class BrowserHandler {
Expand Down Expand Up @@ -84,17 +86,44 @@ public Object function(Object[] arguments) {
new BrowserFunction(browser, "stopScan") {
@Override
public Object function(Object[] arguments) {
IHandlerService handlerService =
(IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench()
.getService(IHandlerService.class);

try {
handlerService.executeCommand(IHandlerCommands.STOP_SCAN, null);
} catch (CommandException e) {
SnykLogger.logError(e);
}
}
return null;
}
};

new BrowserFunction(browser, "ideGenAIFix") {
@Override
public Object function(Object[] arguments) {
String params = (String) arguments[0];
String[] parts = params.split("@|@");
String folderURI = (String) parts[0];
String fileURI = (String) parts[2];
String issueID = (String) parts[4];

SnykExtendedLanguageClient.getInstance().sendCodeFixDiffsCommand(folderURI, fileURI, issueID);

return Collections.emptyList();
}
};

new BrowserFunction(browser, "ideApplyFix") {
@Override
public Object function(Object[] arguments) {
String fixId = (String) arguments[0];

SnykExtendedLanguageClient.getInstance().sendCodeApplyAiFixEditCommand(fixId);

return Collections.emptyList();
}
};

browser.addLocationListener(new LocationListener() {
@Override
public void changing(LocationEvent event) {
Expand Down Expand Up @@ -158,7 +187,7 @@ public CompletableFuture<Void> updateBrowserContent(TreeNode node) {
}

final var browserContent = htmlProvider.replaceCssVariables(htmlContent);

Display.getDefault().syncExec(() -> {
browser.setText(browserContent);
});
Expand Down Expand Up @@ -191,7 +220,8 @@ public String generateHtmlContent(String text) {
}

public void setDefaultBrowserText() {
// If we are not authenticated, show the welcome page, else show the issue placeholder.
// If we are not authenticated, show the welcome page, else show the issue
// placeholder.
if (Preferences.getInstance().getAuthToken().isBlank()) {
browser.setText(StaticPageHtmlProvider.getInstance().getInitHtml());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.eclipse.jface.viewers.TreeViewer;

import io.snyk.languageserver.protocolextension.messageObjects.scanResults.Issue;

/**
* This interface captures the externally used methods with the tool window.
* Having it, should allow for easier testing of the business logic apart from
Expand Down Expand Up @@ -125,4 +127,6 @@ static String getPlural(long count) {
* @return
*/
abstract void disableDelta();

abstract void selectTreeNode(Issue issue, String product);
}
Loading