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: apply fix command [IDE-976] #261

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -119,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 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 @@ -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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
import io.snyk.languageserver.SnykLanguageServer;
import io.snyk.languageserver.protocolextension.messageObjects.Diagnostic316;
import io.snyk.languageserver.protocolextension.messageObjects.FeatureFlagStatus;
import io.snyk.languageserver.protocolextension.messageObjects.Fix;
import io.snyk.languageserver.protocolextension.messageObjects.FolderConfig;
import io.snyk.languageserver.protocolextension.messageObjects.FolderConfigsParam;
import io.snyk.languageserver.protocolextension.messageObjects.HasAuthenticatedParam;
Expand Down Expand Up @@ -333,6 +334,14 @@ public String getIssueDescription(String issueId) {
return String.valueOf(result);
}

public void sendCodeFixDiffsCommand(String folderURI, String fileURI, String issueID) {
executeCommand(LsConstants.COMMAND_CODE_FIX_DIFFS, List.of(folderURI, fileURI, issueID));
}

public void sendCodeApplyAiFixEditCommand(String fixId) {
executeCommand(LsConstants.COMMAND_CODE_FIX_APPLY_AI_EDIT, List.of(fixId));
}

@JsonNotification(value = LsConstants.SNYK_HAS_AUTHENTICATED)
public void hasAuthenticated(HasAuthenticatedParam param) {
var prefs = Preferences.getInstance();
Expand Down Expand Up @@ -477,7 +486,7 @@ public CompletableFuture<ShowDocumentResult> showDocument(
return new ShowDocumentResult(true);
});
}

private Issue getIssueFromCache(String filePath, String issueId) {
SnykIssueCache issueCache = getIssueCache(filePath);
return issueCache.getCodeSecurityIssuesForPath(filePath).stream()
Expand Down Expand Up @@ -740,6 +749,18 @@ public CompletableFuture<Void> createProgress(
return super.createProgress(params);
}

@Override
public void notifyProgress(final ProgressParams params) {
if (params.getValue() == null) {
return;
}
WorkDoneProgressNotification progressNotification = params.getValue().getLeft();
if (progressNotification != null && progressNotification.getKind() == WorkDoneProgressKind.end) {
this.progressManager.removeProgress(params.getToken().getLeft());
}
super.notifyProgress(params);
}

/**
* Refresh the token using language server. Waits up to 2s for the token
* change.
Expand Down Expand Up @@ -811,10 +832,6 @@ public static <T> T convertInstanceOfObject(Object o, Class<T> clazz) {
}
}

public void setToolWindow(ISnykToolView toolView) {
this.toolView = toolView;
}

public void clearCache() {
List<IProject> openProjects = ResourceUtils
.getAccessibleTopLevelProjects();
Expand All @@ -831,24 +848,6 @@ public void clearCache() {

}

public void setProgressMgr(ProgressManager progressMgr) {
this.progressManager = progressMgr;
}

@Override
public void notifyProgress(final ProgressParams params) {
if (params.getValue() == null) {
return;
}
WorkDoneProgressNotification progressNotification = params.getValue()
.getLeft();
if (progressNotification != null
&& progressNotification.getKind() == WorkDoneProgressKind.end) {
this.progressManager.removeProgress(params.getToken().getLeft());
}
super.notifyProgress(params);
}

@JsonRequest(value = "workspace/snyk.sdks")
public CompletableFuture<List<LsSdk>> getSdks(
WorkspaceFolder workspaceFolder) {
Expand All @@ -861,12 +860,19 @@ public CompletableFuture<List<LsSdk>> getSdks(
});
}

public ProgressManager getProgressManager() {
return this.progressManager;
public void setToolWindow(ISnykToolView toolView) {
this.toolView = toolView;
}

public void setLs(LanguageServer ls) {
this.ls = ls;
}

public void setProgressMgr(ProgressManager progressMgr) {
this.progressManager = progressMgr;
}

public ProgressManager getProgressManager() {
return this.progressManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static Map<String, String> getQueryParameters(String queryString) {
if (!param.isEmpty()) {
String[] keyValue = param.split("=");

if (keyValue.length == 2) { // TODO add the no pmd here
if (keyValue.length == 2) { // NOPMD, AvoidLiteralsInIfCondition
try {
paramMap.put(keyValue[0],
URLDecoder.decode(keyValue[1], "UTF-8"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.snyk.languageserver.protocolextension.messageObjects;

import java.util.Map;

import com.google.gson.annotations.SerializedName;

public record Fix(
@SerializedName("fixId") String fixId,
@SerializedName("unifiedDiffsPerFile") Map<String, String> unifiedDiffsPerFile) {
// no-arg constructor is generated automatically by Java compiler
}