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 18 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
4 changes: 3 additions & 1 deletion plugin/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.ui.genericeditor,
org.eclipse.swt,
org.eclipse.jface,
com.google.gson;bundle-version="2.11.0"
com.google.gson;bundle-version="2.11.0",
org.junit,
junit-jupiter-api
Automatic-Module-Name: io.snyk.eclipse.plugin.tests
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .,
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;
}
}
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeNode;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.lsp4e.LSPEclipseUtils;
Expand All @@ -42,6 +43,7 @@
import org.eclipse.ui.menus.CommandContributionItemParameter;
import org.eclipse.ui.part.ViewPart;

import io.snyk.eclipse.plugin.domain.ProductConstants;
import io.snyk.eclipse.plugin.preferences.Preferences;
import io.snyk.eclipse.plugin.properties.FolderConfigs;
import io.snyk.eclipse.plugin.utils.ResourceUtils;
Expand Down Expand Up @@ -477,4 +479,31 @@ protected void outputCommandResult(Object result) {
}
}

@Override
public void selectTreeNode(Issue issue) {
ProductTreeNode productNode = getProductNode(ProductConstants.DISPLAYED_CODE_SECURITY, issue.filePath());
selectTreenodeForIssue((TreeNode) productNode, issue);
}

private void selectTreenodeForIssue(TreeNode currentParent, Issue issue) {
for (Object child : currentParent.getChildren()) {
TreeNode childNode = (TreeNode) child;

if (childNode instanceof IssueTreeNode && ((IssueTreeNode) childNode).getIssue().id().equals(issue.id())) {
updateSelection((IssueTreeNode) childNode);
return; // Exit the function as we've found a match
}

if (childNode.getChildren() != null && childNode.getChildren().length != 0) {
selectTreenodeForIssue(childNode, issue);
}
}
}

private void updateSelection(IssueTreeNode issueTreeNode) {
Display.getDefault().asyncExec(() -> {
IStructuredSelection selection = new StructuredSelection(issueTreeNode);
treeViewer.setSelection(selection, true);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ private LsConstants() {
public static final String COMMAND_REPORT_ANALYTICS = "snyk.reportAnalytics";
public static final String COMMAND_GET_FEATURE_FLAG_STATUS = "snyk.getFeatureFlagStatus";
public static final String COMMAND_CODE_FIX_DIFFS = "snyk.code.fixDiffs";
public static final String COMMAND_CODE_FIX_APPLY_AI_EDIT = "snyk.code.fixApplyEdit";
public static final String COMMAND_CODE_SUBMIT_FIX_FEEDBACK = "snyk.code.submitFixFeedback";
public static final String COMMAND_SNYK_CLI = "snyk.executeCLI";
public static final String SNYK_HAS_AUTHENTICATED = "$/snyk.hasAuthenticated";
Expand All @@ -26,4 +27,5 @@ private LsConstants() {
public static final String SNYK_PUBLISH_DIAGNOSTICS_316 = "$/snyk.publishDiagnostics316";
public static final String SNYK_FOLDER_CONFIG = "$/snyk.folderConfigs";
public static final String SNYK_SCAN_SUMMARY = "$/snyk.scanSummary";
public static final String SNYK_SHOW_DOCUMENT = "$/snyk.showDocument";
}
Loading
Loading