Skip to content

Commit d05e763

Browse files
authored
Merge pull request #229 from snyk/feat/IDE-739_branch_selection_node
feat: add branch selection node [IDE-739]
2 parents ba6787a + 5279418 commit d05e763

22 files changed

Lines changed: 1054 additions & 451 deletions
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package io.snyk.eclipse.plugin.properties.preferences;
2+
3+
import java.nio.file.Path;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
import java.util.stream.Collectors;
7+
8+
import org.eclipse.core.resources.IProject;
9+
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
10+
import org.eclipse.core.runtime.preferences.InstanceScope;
11+
12+
import com.google.gson.Gson;
13+
14+
import io.snyk.eclipse.plugin.utils.ResourceUtils;
15+
import io.snyk.eclipse.plugin.utils.SnykLogger;
16+
import io.snyk.languageserver.protocolextension.messageObjects.FolderConfig;
17+
import io.snyk.languageserver.protocolextension.messageObjects.FolderConfigsParam;
18+
19+
public class FolderConfigs {
20+
protected static FolderConfigs instance;
21+
private static final IEclipsePreferences preferenceState = InstanceScope.INSTANCE.getNode("io.snyk.eclipse.plugin");
22+
private static final Gson gson = new Gson();
23+
24+
private FolderConfigs() {
25+
}
26+
27+
public static synchronized FolderConfigs getInstance() {
28+
if (instance == null) {
29+
instance = new FolderConfigs();
30+
}
31+
return instance;
32+
}
33+
34+
public void addFolderConfig(FolderConfig folderConfig) {
35+
preferenceState.put(folderConfig.getFolderPath(), gson.toJson(folderConfig));
36+
try {
37+
preferenceState.flush();
38+
} catch (Exception e) {
39+
SnykLogger.logError(e);
40+
}
41+
}
42+
43+
public List<String> getLocalBranches(Path projectPath) {
44+
String json = preferenceState.get(normalizePath(projectPath), null);
45+
if (json != null) {
46+
FolderConfig folderConfig = gson.fromJson(json, FolderConfig.class);
47+
return folderConfig.getLocalBranches();
48+
}
49+
return List.of();
50+
}
51+
52+
private String normalizePath(Path projectPath) {
53+
return projectPath.normalize().toString();
54+
}
55+
56+
public void setBaseBranch(Path projectPath, String newBaseBranch) {
57+
String path = normalizePath(projectPath);
58+
String json = preferenceState.get(path, null);
59+
if (json == null) {
60+
SnykLogger.logInfo("No valid configuration for project: " + path);
61+
return;
62+
}
63+
FolderConfig folderConfig = gson.fromJson(json, FolderConfig.class);
64+
folderConfig.setBaseBranch(newBaseBranch);
65+
String updatedJson = gson.toJson(folderConfig);
66+
preferenceState.put(path, updatedJson);
67+
try {
68+
preferenceState.flush();
69+
} catch (Exception e) {
70+
SnykLogger.logError(e);
71+
}
72+
}
73+
74+
public String getBaseBranch(Path projectPath) {
75+
String json = preferenceState.get(normalizePath(projectPath), null);
76+
if (json == null)
77+
return null;
78+
FolderConfig folderConfig = gson.fromJson(json, FolderConfig.class);
79+
return folderConfig.getBaseBranch();
80+
}
81+
82+
public void addAll(List<FolderConfig> folderConfigs) {
83+
for (FolderConfig folderConfig : folderConfigs) {
84+
addFolderConfig(folderConfig);
85+
}
86+
}
87+
88+
public FolderConfigsParam updateFolderConfigs() {
89+
List<IProject> openProjects = ResourceUtils.getAccessibleTopLevelProjects();
90+
91+
List<String> projectPaths = openProjects.stream().map(project -> ResourceUtils.getFullPath(project).toString())
92+
.collect(Collectors.toList());
93+
94+
FolderConfigsParam folderConfigs = getFolderConfigs(projectPaths);
95+
96+
return folderConfigs;
97+
98+
}
99+
100+
private FolderConfigsParam getFolderConfigs(List<String> folderPaths) {
101+
List<FolderConfig> folderConfigs = new ArrayList<>();
102+
for (String folderPath : folderPaths) {
103+
String json = preferenceState.get(folderPath, null);
104+
if (json != null) {
105+
FolderConfig folderConfig = gson.fromJson(json, FolderConfig.class);
106+
folderConfigs.add(folderConfig);
107+
}
108+
}
109+
return new FolderConfigsParam(folderConfigs);
110+
}
111+
112+
public static void setInstance(FolderConfigs folderConfigs) {
113+
instance = folderConfigs;
114+
115+
}
116+
}

plugin/src/main/java/io/snyk/eclipse/plugin/properties/preferences/Preferences.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,8 @@ public boolean isTest() {
261261
return getBooleanPref("isTesting");
262262
}
263263

264+
public static boolean isDeltaEnabled() {
265+
return Preferences.getInstance().getBooleanPref(Preferences.FILTER_DELTA_NEW_ISSUES);
266+
}
267+
264268
}

plugin/src/main/java/io/snyk/eclipse/plugin/utils/ResourceUtils.java

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
public class ResourceUtils {
2323

24-
private static final Comparator<IProject> projectByPathComparator = new Comparator<IProject>() {
24+
private static final Comparator<IProject> projectByPathComparator = new Comparator<IProject>() {
2525
@Override
2626
public int compare(IProject o1, IProject o2) {
2727
Path fullPath = ResourceUtils.getFullPath(o1);
@@ -67,7 +67,7 @@ public static Path getFullPath(IResource resource) {
6767

6868
public static IProject getProjectByPath(Path path) {
6969
var projects = getAccessibleTopLevelProjects();
70-
70+
7171
for (IProject iProject : projects) {
7272
Path projectPath = ResourceUtils.getFullPath(iProject);
7373
if (path.normalize().startsWith(projectPath)) {
@@ -78,15 +78,10 @@ public static IProject getProjectByPath(Path path) {
7878
}
7979

8080
public static List<IProject> getAccessibleTopLevelProjects() {
81-
var projects = Arrays.stream(ResourcesPlugin.getWorkspace().getRoot().getProjects())
82-
.filter((project) -> {
83-
return project.isAccessible()
84-
&& !project.isDerived()
85-
&& !project.isHidden();
86-
})
87-
.sorted(projectByPathComparator)
88-
.collect(Collectors.toList());
89-
81+
var projects = Arrays.stream(ResourcesPlugin.getWorkspace().getRoot().getProjects()).filter((project) -> {
82+
return project.isAccessible() && !project.isDerived() && !project.isHidden();
83+
}).sorted(projectByPathComparator).collect(Collectors.toList());
84+
9085
Set<IProject> topLevel = new TreeSet<>(projectByPathComparator);
9186
boolean add = true;
9287
for (IProject iProject : projects) {
@@ -102,7 +97,7 @@ public static List<IProject> getAccessibleTopLevelProjects() {
10297
topLevel.add(iProject);
10398
}
10499
}
105-
100+
106101
return new ArrayList<>(topLevel);
107102
}
108-
}
103+
}
Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,46 @@
11
package io.snyk.eclipse.plugin.views;
22

3+
import java.nio.file.Path;
4+
import java.util.concurrent.CompletableFuture;
5+
36
import org.eclipse.core.commands.AbstractHandler;
47
import org.eclipse.core.commands.ExecutionEvent;
58
import org.eclipse.core.commands.ExecutionException;
9+
import org.eclipse.core.resources.IProject;
10+
import org.eclipse.jdt.internal.core.JavaProject;
11+
import org.eclipse.jface.viewers.IStructuredSelection;
12+
import org.eclipse.ui.ISelectionService;
613
import org.eclipse.ui.IWorkbenchWindow;
714
import org.eclipse.ui.handlers.HandlerUtil;
815

16+
import io.snyk.eclipse.plugin.utils.ResourceUtils;
917
import io.snyk.languageserver.protocolextension.SnykExtendedLanguageClient;
1018

1119
public class MenuHandler extends AbstractHandler {
1220

13-
public Object execute(ExecutionEvent event) throws ExecutionException {
14-
IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
15-
SnykExtendedLanguageClient.getInstance().triggerScan(window);
16-
return null;
17-
}
21+
public Object execute(ExecutionEvent event) throws ExecutionException {
22+
IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
23+
24+
ISelectionService service = window.getSelectionService();
25+
IStructuredSelection structured = (IStructuredSelection) service.getSelection();
26+
27+
Object firstElement = structured.getFirstElement();
28+
IProject project = null;
29+
30+
if (firstElement instanceof JavaProject) {
31+
project = ((JavaProject) firstElement).getProject();
32+
}
33+
34+
if (firstElement instanceof IProject) {
35+
project = (IProject) firstElement;
36+
}
37+
38+
Path fullPath = ResourceUtils.getFullPath(project);
39+
40+
CompletableFuture.runAsync(() -> {
41+
SnykExtendedLanguageClient.getInstance().triggerScan(fullPath);
42+
});
43+
44+
return null;
45+
}
1846
}

plugin/src/main/java/io/snyk/eclipse/plugin/views/ScanWorkspaceMenuHandler.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.snyk.eclipse.plugin.views;
22

3+
import java.util.concurrent.CompletableFuture;
4+
35
import org.eclipse.core.commands.AbstractHandler;
46
import org.eclipse.core.commands.ExecutionEvent;
57
import org.eclipse.core.commands.ExecutionException;
@@ -9,7 +11,9 @@
911
public class ScanWorkspaceMenuHandler extends AbstractHandler {
1012

1113
public Object execute(ExecutionEvent event) throws ExecutionException {
12-
SnykExtendedLanguageClient.getInstance().triggerScan(null);
14+
CompletableFuture.runAsync(() -> {
15+
SnykExtendedLanguageClient.getInstance().triggerScan(null);
16+
});
1317
return null;
1418
}
1519
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package io.snyk.eclipse.plugin.views.snyktoolview;
2+
3+
import java.nio.file.Path;
4+
import java.util.Arrays;
5+
import java.util.concurrent.CompletableFuture;
6+
7+
import org.eclipse.core.resources.IProject;
8+
import org.eclipse.swt.SWT;
9+
import org.eclipse.swt.events.SelectionAdapter;
10+
import org.eclipse.swt.events.SelectionEvent;
11+
import org.eclipse.swt.layout.GridData;
12+
import org.eclipse.swt.layout.GridLayout;
13+
import org.eclipse.swt.widgets.Button;
14+
import org.eclipse.swt.widgets.Combo;
15+
import org.eclipse.swt.widgets.Display;
16+
import org.eclipse.swt.widgets.Label;
17+
import org.eclipse.swt.widgets.Shell;
18+
19+
import io.snyk.eclipse.plugin.properties.preferences.FolderConfigs;
20+
import io.snyk.eclipse.plugin.utils.SnykLogger;
21+
import io.snyk.languageserver.protocolextension.SnykExtendedLanguageClient;
22+
23+
public class BaseBranchDialog {
24+
private FolderConfigs folderConfigs = FolderConfigs.getInstance();
25+
26+
public BaseBranchDialog() {
27+
}
28+
29+
public void open(Display display, Path projectPath, String[] localBranches) {
30+
Shell shell = new Shell(display, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
31+
shell.setText("Choose base branch for net-new issues scanning");
32+
shell.setLayout(new GridLayout(1, false));
33+
Label label = new Label(shell, SWT.NONE);
34+
label.setText("Base Branch for: " + projectPath);
35+
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
36+
37+
Combo dropdown = new Combo(shell, SWT.DROP_DOWN);
38+
dropdown.setItems(localBranches);
39+
dropdown.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
40+
dropdown.setText(folderConfigs.getBaseBranch(projectPath));
41+
42+
Button okButton = new Button(shell, SWT.PUSH);
43+
okButton.setText("OK");
44+
okButton.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false));
45+
okButton.addSelectionListener(new SelectionAdapter() {
46+
@Override
47+
public void widgetSelected(SelectionEvent e) {
48+
// Handle OK button press
49+
String selectedBranch = dropdown.getText();
50+
if (Arrays.asList(localBranches).contains(selectedBranch)) {
51+
folderConfigs.setBaseBranch(projectPath, selectedBranch);
52+
shell.close();
53+
CompletableFuture.runAsync(() -> {
54+
SnykExtendedLanguageClient.getInstance().triggerScan(projectPath);
55+
});
56+
} else {
57+
SnykLogger.logInfo("Branch is not a valid local branch for repository: " + projectPath);
58+
}
59+
}
60+
});
61+
62+
shell.pack();
63+
shell.open();
64+
65+
while (!shell.isDisposed()) {
66+
if (!display.readAndDispatch()) {
67+
display.sleep();
68+
}
69+
}
70+
}
71+
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class ContentRootNode extends BaseTreeNode {
2222
public ContentRootNode(String name, Path value) {
2323
super(value);
2424
reset();
25-
this.name = name;
25+
this.setName(name);
2626
this.setPath(value);
2727
}
2828

@@ -79,7 +79,7 @@ public void reset() {
7979

8080
@Override
8181
public String getText() {
82-
return name;
82+
return getName();
8383
}
8484

8585
public Path getPath() {
@@ -89,4 +89,12 @@ public Path getPath() {
8989
public void setPath(Path path) {
9090
this.path = path.normalize();
9191
}
92+
93+
public String getName() {
94+
return name;
95+
}
96+
97+
public void setName(String name) {
98+
this.name = name;
99+
}
92100
}

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

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

5-
65
/**
76
* This interface captures the externally used methods with the tool window.
87
* Having it, should allow for easier testing of the business logic apart from
@@ -96,5 +95,24 @@ static String getPlural(long count) {
9695

9796
abstract TreeViewer getTreeViewer();
9897

98+
/**
99+
* Hides or shows the Ignore buttons.
100+
*
101+
* @return
102+
*/
99103
abstract void toggleIgnoresButtons();
104+
105+
/**
106+
* Enables the net new issues scans.
107+
*
108+
* @return
109+
*/
110+
abstract void enableDelta();
111+
112+
/**
113+
* Disable the net new issues scans.
114+
*
115+
* @return
116+
*/
117+
abstract void disableDelta();
100118
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ public void reset() {
1717
super.reset();
1818
List<IProject> openProjects = ResourceUtils.getAccessibleTopLevelProjects();
1919

20+
if (openProjects.isEmpty()) {
21+
var contentRoot = new ContentRootNode("No projects in workspace to scan", null);
22+
this.addChild(contentRoot);
23+
return;
24+
}
25+
2026
for (IProject project : openProjects) {
2127
Path path = ResourceUtils.getFullPath(project);
2228
var contentRoot = new ContentRootNode(project.getName(), path);

0 commit comments

Comments
 (0)