Skip to content

Commit 3a48531

Browse files
authored
Merge pull request #260 from snyk/feat/IDE-957_code_action_opens_issue_panel
feat: select treenode on snyk showdocument [IDE-957] feat: clicking Apply Button should call Snyk Language Server with fixId [IDE-976]
2 parents 25d529a + f799fda commit 3a48531

File tree

14 files changed

+731
-204
lines changed

14 files changed

+731
-204
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
### Changes
55
- add option for using a folder as reference instead of a branch in net-new scanning
66
- add scan summary to custom UI, updating scan statuses live
7+
- added support for DeepCode AI Fixes
78

89
## [3.0.0]
910
### Changes

plugin/src/main/java/io/snyk/eclipse/plugin/html/BaseHtmlProvider.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import org.eclipse.core.runtime.Platform;
88
import org.eclipse.jface.resource.ColorRegistry;
9-
import org.eclipse.jface.resource.FontRegistry;
109
import org.eclipse.jface.resource.JFaceResources;
1110
import org.eclipse.swt.graphics.Color;
1211
import org.eclipse.swt.graphics.RGB;
@@ -120,9 +119,10 @@ public String replaceCssVariables(String html) {
120119
getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0"));
121120
htmlStyled = htmlStyled.replace("var(--circle-color)",
122121
getColorAsHex("org.eclipse.ui.workbench.INACTIVE_TAB_BG_START", "#F0F0F0"));
123-
124122
htmlStyled = htmlStyled.replace("var(--border-color)",
125123
getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC"));
124+
htmlStyled = htmlStyled.replace("var(--input-border)",
125+
getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC"));
126126
htmlStyled = htmlStyled.replace("var(--link-color)", getColorAsHex("ACTIVE_HYPERLINK_COLOR", "#0000FF"));
127127
htmlStyled = htmlStyled.replace("var(--horizontal-border-color)",
128128
getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC"));
@@ -142,8 +142,10 @@ private String getScaledFontSize() {
142142
} catch (IllegalStateException e) {
143143
defaultHeight = 13;
144144
}
145-
// Language server HTML assumes a base font size of 10px. The default Eclipse font size is 17px (13pt), so we
146-
// apply a scaling factor here. This ensures that HTML fonts scale correctly if the user changes the text size.
145+
// Language server HTML assumes a base font size of 10px. The default Eclipse
146+
// font size is 17px (13pt), so we
147+
// apply a scaling factor here. This ensures that HTML fonts scale correctly if
148+
// the user changes the text size.
147149
int scaledHeight = (int) (defaultHeight / 1.7);
148150
return scaledHeight + "pt";
149151
}

plugin/src/main/java/io/snyk/eclipse/plugin/html/CodeHtmlProvider.java

+73-54
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import io.snyk.eclipse.plugin.preferences.Preferences;
44

55
public class CodeHtmlProvider extends BaseHtmlProvider {
6-
private static CodeHtmlProvider instance = new CodeHtmlProvider();
6+
private static CodeHtmlProvider instance = new CodeHtmlProvider();
77

88
public static CodeHtmlProvider getInstance() {
99
synchronized (CodeHtmlProvider.class) {
@@ -16,61 +16,80 @@ public static CodeHtmlProvider getInstance() {
1616
return instance;
1717
}
1818

19-
@Override
20-
public String getInitScript() {
21-
String themeScript = getThemeScript();
22-
String initScript = super.getInitScript();
23-
return initScript + "\n" + """
24-
function navigateToIssue(e, target) {
25-
e.preventDefault();
26-
var filePath = target.getAttribute('file-path');
27-
var startLine = target.getAttribute('start-line');
28-
var endLine = target.getAttribute('end-line');
29-
var startCharacter = target.getAttribute('start-character');
30-
var endCharacter = target.getAttribute('end-character');
31-
window.openInEditor(filePath, startLine, endLine, startCharacter, endCharacter);
32-
}
33-
var navigatableLines = document.getElementsByClassName('data-flow-clickable-row');
34-
for(var i = 0; i < navigatableLines.length; i++) {
35-
navigatableLines[i].onclick = function(e) {
36-
navigateToIssue(e, this);
37-
return false;
38-
};
39-
}
40-
if(document.getElementById('position-line')) {
41-
document.getElementById('position-line').onclick = function(e) {
42-
var target = navigatableLines[0];
43-
if(target) {
44-
navigateToIssue(e, target);
45-
}
46-
}
47-
}
48-
// Disable AIfix
49-
if(document.getElementById('ai-fix-wrapper') && document.getElementById('no-ai-fix-wrapper')){
50-
document.getElementById('ai-fix-wrapper').className = 'hidden';
51-
document.getElementById('no-ai-fix-wrapper').className = '';
52-
}
53-
""" + themeScript;
54-
}
19+
@Override
20+
public String getInitScript() {
21+
String themeScript = getThemeScript();
22+
String initScript = super.getInitScript();
23+
return initScript + "\n" + """
24+
function navigateToIssue(e, target) {
25+
e.preventDefault();
26+
var filePath = target.getAttribute('file-path');
27+
var startLine = target.getAttribute('start-line');
28+
var endLine = target.getAttribute('end-line');
29+
var startCharacter = target.getAttribute('start-character');
30+
var endCharacter = target.getAttribute('end-character');
31+
window.openInEditor(filePath, startLine, endLine, startCharacter, endCharacter);
32+
}
33+
var navigatableLines = document.getElementsByClassName('data-flow-clickable-row');
34+
for(var i = 0; i < navigatableLines.length; i++) {
35+
navigatableLines[i].onclick = function(e) {
36+
navigateToIssue(e, this);
37+
return false;
38+
};
39+
}
40+
if(document.getElementById('position-line')) {
41+
document.getElementById('position-line').onclick = function(e) {
42+
var target = navigatableLines[0];
43+
if(target) {
44+
navigateToIssue(e, target);
45+
}
46+
}
47+
}
48+
""" + themeScript;
49+
}
50+
51+
private String getThemeScript() {
52+
if (Preferences.getInstance().isTest()) {
53+
return "";
54+
}
55+
56+
String themeScript = "var isDarkTheme = " + isDarkTheme() + ";\n"
57+
+ "document.body.classList.add(isDarkTheme ? 'dark' : 'light');";
58+
return themeScript;
59+
}
60+
61+
@Override
62+
public String replaceCssVariables(String html) {
63+
String htmlStyled = super.replaceCssVariables(html);
64+
65+
// Replace CSS variables with actual color values
66+
htmlStyled = htmlStyled.replace("var(--example-line-removed-color)",
67+
super.getColorAsHex("DELETION_COLOR", "#ff0000"));
68+
htmlStyled = htmlStyled.replace("var(--example-line-added-color)",
69+
super.getColorAsHex("ADDITION_COLOR", "#00ff00"));
70+
htmlStyled = htmlStyled.replace("var(--generated-ai-fix-button-background-color)",
71+
super.getColorAsHex("BUTTON_COLOR", "#375578"));
72+
htmlStyled = htmlStyled.replace("var(--disabled-background-color)",
73+
super.getColorAsHex("org.eclipse.ui.workbench.ACTIVE_TAB_OUTER_KEYLINE_COLOR", "#CCCCCC"));
5574

56-
private String getThemeScript() {
57-
if(Preferences.getInstance().isTest()) {
58-
return "";
59-
}
75+
String htmlWithScripts = replaceAIFixScripts(htmlStyled);
6076

61-
String themeScript = "var isDarkTheme = " + isDarkTheme() + ";\n" +
62-
"document.body.classList.add(isDarkTheme ? 'dark' : 'light');";
63-
return themeScript;
64-
}
77+
return htmlWithScripts;
78+
}
79+
80+
private String replaceAIFixScripts(String html) {
81+
String htmlWithAiFixScripts = html.replace("${ideGenerateAIFix}", getGenerateAiFixScript());
82+
htmlWithAiFixScripts = htmlWithAiFixScripts.replace("${ideApplyAIFix}", getApplyAiFixScript());
83+
84+
return htmlWithAiFixScripts;
85+
}
6586

66-
@Override
67-
public String replaceCssVariables(String html) {
68-
String htmlStyled = super.replaceCssVariables(html);
69-
70-
// Replace CSS variables with actual color values
71-
htmlStyled = htmlStyled.replace("var(--example-line-removed-color)", super.getColorAsHex("DELETION_COLOR", "#ff0000"));
72-
htmlStyled = htmlStyled.replace("var(--example-line-added-color)", super.getColorAsHex("ADDITION_COLOR", "#00ff00"));
87+
private String getGenerateAiFixScript() {
88+
return "window.ideGenAIFix(generateFixQueryString)\n;";
89+
}
90+
91+
private String getApplyAiFixScript() {
92+
return "window.ideApplyFix(fixId);\n";
93+
}
7394

74-
return htmlStyled;
75-
}
7695
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package io.snyk.eclipse.plugin.utils;
2+
3+
import java.io.UnsupportedEncodingException;
4+
import java.net.URI;
5+
import java.net.URLDecoder;
6+
import java.util.Collections;
7+
import java.util.HashMap;
8+
import java.util.Map;
9+
10+
public class UriUtils {
11+
12+
/**
13+
* Retrieves a parameter value from the given URI.
14+
*
15+
* If the query string does not contain the specified parameter or is
16+
* null/empty, this method returns null.
17+
*
18+
* @param uri
19+
* The URI to retrieve parameters from
20+
* @param paramName
21+
* The name of the parameter to retrieve
22+
* @return The parameter value if it exists in the query string, otherwise
23+
* null
24+
*/
25+
public static String getDecodedParam(URI uri, String paramName) {
26+
String query = uri.getQuery();
27+
if (query == null || query.isEmpty()) {
28+
return null;
29+
}
30+
31+
Map<String, String> paramMap = getQueryParameters(query);
32+
String value = paramMap.get(paramName);
33+
34+
if (value == null) {
35+
return null;
36+
}
37+
38+
return value;
39+
}
40+
41+
/**
42+
* Parses a query string into a map of key-value pairs, decoding any
43+
* URL-encoded values.
44+
*
45+
* If the input is null or empty, an empty map is returned. If an encoding
46+
* error occurs while decoding a value, an error is logged and an empty map
47+
* is returned.
48+
*
49+
* @param queryString
50+
* The query string to parse
51+
* @return A map containing the decoded parameters
52+
*/
53+
public static Map<String, String> getQueryParameters(String queryString) {
54+
Map<String, String> paramMap = new HashMap<>();
55+
56+
if (queryString == null || queryString.isEmpty()) {
57+
return paramMap;
58+
}
59+
60+
for (String param : queryString.split("&")) {
61+
if (!param.isEmpty()) {
62+
String[] keyValue = param.split("=");
63+
64+
if (keyValue.length == 2) { // NOPMD, AvoidLiteralsInIfCondition
65+
try {
66+
paramMap.put(keyValue[0],
67+
URLDecoder.decode(keyValue[1], "UTF-8"));
68+
} catch (UnsupportedEncodingException e) {
69+
SnykLogger.logError(e);
70+
return Collections.emptyMap();
71+
}
72+
}
73+
}
74+
}
75+
76+
return paramMap;
77+
}
78+
}

plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/BrowserHandler.java

+35-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static org.apache.commons.lang3.StringUtils.isEmpty;
44

55
import java.nio.file.Paths;
6+
import java.util.Collections;
67
import java.util.concurrent.CompletableFuture;
78

89
import org.eclipse.core.commands.common.CommandException;
@@ -31,6 +32,7 @@
3132
import io.snyk.eclipse.plugin.utils.SnykLogger;
3233
import io.snyk.eclipse.plugin.views.snyktoolview.handlers.IHandlerCommands;
3334
import io.snyk.eclipse.plugin.wizards.SnykWizard;
35+
import io.snyk.languageserver.protocolextension.SnykExtendedLanguageClient;
3436

3537
@SuppressWarnings("restriction")
3638
public class BrowserHandler {
@@ -84,17 +86,44 @@ public Object function(Object[] arguments) {
8486
new BrowserFunction(browser, "stopScan") {
8587
@Override
8688
public Object function(Object[] arguments) {
87-
IHandlerService handlerService =
88-
(IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
89+
IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench()
90+
.getService(IHandlerService.class);
91+
8992
try {
9093
handlerService.executeCommand(IHandlerCommands.STOP_SCAN, null);
9194
} catch (CommandException e) {
9295
SnykLogger.logError(e);
93-
}
96+
}
9497
return null;
9598
}
9699
};
97100

101+
new BrowserFunction(browser, "ideGenAIFix") {
102+
@Override
103+
public Object function(Object[] arguments) {
104+
String params = (String) arguments[0];
105+
String[] parts = params.split("@|@");
106+
String folderURI = (String) parts[0];
107+
String fileURI = (String) parts[2];
108+
String issueID = (String) parts[4];
109+
110+
SnykExtendedLanguageClient.getInstance().sendCodeFixDiffsCommand(folderURI, fileURI, issueID);
111+
112+
return Collections.emptyList();
113+
}
114+
};
115+
116+
new BrowserFunction(browser, "ideApplyFix") {
117+
@Override
118+
public Object function(Object[] arguments) {
119+
String fixId = (String) arguments[0];
120+
121+
SnykExtendedLanguageClient.getInstance().sendCodeApplyAiFixEditCommand(fixId);
122+
123+
return Collections.emptyList();
124+
}
125+
};
126+
98127
browser.addLocationListener(new LocationListener() {
99128
@Override
100129
public void changing(LocationEvent event) {
@@ -158,7 +187,7 @@ public CompletableFuture<Void> updateBrowserContent(TreeNode node) {
158187
}
159188

160189
final var browserContent = htmlProvider.replaceCssVariables(htmlContent);
161-
190+
162191
Display.getDefault().syncExec(() -> {
163192
browser.setText(browserContent);
164193
});
@@ -191,7 +220,8 @@ public String generateHtmlContent(String text) {
191220
}
192221

193222
public void setDefaultBrowserText() {
194-
// If we are not authenticated, show the welcome page, else show the issue placeholder.
223+
// If we are not authenticated, show the welcome page, else show the issue
224+
// placeholder.
195225
if (Preferences.getInstance().getAuthToken().isBlank()) {
196226
browser.setText(StaticPageHtmlProvider.getInstance().getInitHtml());
197227
} else {

plugin/src/main/java/io/snyk/eclipse/plugin/views/snyktoolview/ISnykToolView.java

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import org.eclipse.jface.viewers.TreeViewer;
44

5+
import io.snyk.languageserver.protocolextension.messageObjects.scanResults.Issue;
6+
57
/**
68
* This interface captures the externally used methods with the tool window.
79
* Having it, should allow for easier testing of the business logic apart from
@@ -125,4 +127,6 @@ static String getPlural(long count) {
125127
* @return
126128
*/
127129
abstract void disableDelta();
130+
131+
abstract void selectTreeNode(Issue issue, String product);
128132
}

0 commit comments

Comments
 (0)