Skip to content
Draft
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
17 changes: 17 additions & 0 deletions io.cucumber.eclipse.editor/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@
<super type="cucumber.eclipse.marker"/>
<persistent value="true"/>
</extension>
<extension
id="cucumber.eclipse.marker.language.support_available"
name="Language Support Available"
point="org.eclipse.core.resources.markers">
<super type="org.eclipse.core.resources.problemmarker"/>
<super type="cucumber.eclipse.marker"/>
<attribute name="cucumber.eclipse.marker.language.support_available.bundle_id"/>
<attribute name="cucumber.eclipse.marker.language.support_available.language"/>
<persistent value="true"/>
</extension>
<extension
point="org.eclipse.ui.editors.markerAnnotationSpecification">
<specification
Expand Down Expand Up @@ -391,6 +401,13 @@
name="Cucumber">
</page>
</extension>
<extension
point="org.eclipse.ui.ide.markerResolution">
<markerResolutionGenerator
class="io.cucumber.eclipse.editor.quickfix.LanguageSupportMarkerResolutionGenerator"
markerType="cucumber.eclipse.marker.language.support_available">
</markerResolutionGenerator>
</extension>
<!-- Eclipse Activity Support -->
<extension
point="org.eclipse.ui.activities">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ public class MarkerFactory {

public static final String CUCUMBER_NATURE_MISSING_MARKER = CUCUMBER_MARKER + ".project.cucumber_nature_missing";

public static final String LANGUAGE_SUPPORT_AVAILABLE = CUCUMBER_MARKER + ".language.support_available";
public static final String LANGUAGE_SUPPORT_BUNDLE_ID_ATTRIBUTE = LANGUAGE_SUPPORT_AVAILABLE + ".bundle_id";
public static final String LANGUAGE_SUPPORT_LANGUAGE_ATTRIBUTE = LANGUAGE_SUPPORT_AVAILABLE + ".language";

private MarkerFactory() {
}

Expand Down Expand Up @@ -408,4 +412,35 @@ public static boolean hasMarker(IResource resource, String type, int lineNumber)

}

public static void languageSupportAvailable(final IResource resource, final String language,
final String bundleId, boolean persistent) {
mark(resource, new IMarkerBuilder() {
@Override
public void build() throws CoreException {
// Check if marker already exists for this language
String sourceId = language + "_" + bundleId;
Map<Object, IMarker> existingMarker = getExistingMarker(resource, LANGUAGE_SUPPORT_AVAILABLE);
IMarker marker = existingMarker.remove(sourceId);
if (marker == null) {
marker = resource.createMarker(LANGUAGE_SUPPORT_AVAILABLE);
marker.setAttribute(IMarker.SOURCE_ID, sourceId);
}
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
marker.setAttribute(IMarker.MESSAGE,
String.format("Enhanced %s support is available and can be installed", language));
marker.setAttribute(IMarker.LINE_NUMBER, 1);
marker.setAttribute(IMarker.TRANSIENT, persistent);
marker.setAttribute(LANGUAGE_SUPPORT_BUNDLE_ID_ATTRIBUTE, bundleId);
marker.setAttribute(LANGUAGE_SUPPORT_LANGUAGE_ATTRIBUTE, language);
}
});
}

public static void deleteLanguageSupportMarkers(final IResource resource) throws CoreException {
IMarker[] markers = resource.findMarkers(LANGUAGE_SUPPORT_AVAILABLE, true, IResource.DEPTH_ZERO);
for (IMarker marker : markers) {
marker.delete();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package io.cucumber.eclipse.editor.quickfix;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IMarkerResolution;
import org.eclipse.ui.IMarkerResolutionGenerator;
import org.eclipse.ui.IMarkerResolutionGenerator2;
import org.eclipse.ui.PlatformUI;

import io.cucumber.eclipse.editor.Images;
import io.cucumber.eclipse.editor.marker.MarkerFactory;

import java.lang.reflect.InvocationTargetException;

/**
* Provides quick fixes for language support markers.
* When a project has a supported language nature but the support bundle is not installed,
* this generator provides a resolution to install the support.
*/
public class LanguageSupportMarkerResolutionGenerator implements IMarkerResolutionGenerator, IMarkerResolutionGenerator2 {

@Override
public IMarkerResolution[] getResolutions(IMarker marker) {
try {
if (hasResolutions(marker)) {
String language = marker.getAttribute(MarkerFactory.LANGUAGE_SUPPORT_LANGUAGE_ATTRIBUTE, "");
String bundleId = marker.getAttribute(MarkerFactory.LANGUAGE_SUPPORT_BUNDLE_ID_ATTRIBUTE, "");

if (!language.isEmpty() && !bundleId.isEmpty()) {
return new IMarkerResolution[] {
new InstallLanguageSupportResolution(language, bundleId)
};
}
}
} catch (CoreException e) {
// Log error but don't fail
}
return new IMarkerResolution[0];
}

@Override
public boolean hasResolutions(IMarker marker) {
try {
return MarkerFactory.LANGUAGE_SUPPORT_AVAILABLE.equals(marker.getType());
} catch (CoreException e) {
return false;
}
}

/**
* Resolution that installs language support bundle.
*/
private static class InstallLanguageSupportResolution implements IMarkerResolution {
private final String language;
private final String bundleId;

public InstallLanguageSupportResolution(String language, String bundleId) {
this.language = language;
this.bundleId = bundleId;
}

@Override
public String getLabel() {
return String.format("Install %s support for Cucumber", language);
}

@Override
public void run(IMarker marker) {
Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();

ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell);
try {
dialog.run(true, false, new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask(String.format("Installing %s support...", language), 100);

// Simulate installation work - actual implementation will be added later
for (int i = 0; i < 10; i++) {
if (monitor.isCanceled()) {
break;
}
Thread.sleep(1000);
monitor.worked(10);
}

monitor.done();
}
});

// After successful installation, delete the marker
marker.delete();
} catch (InvocationTargetException | InterruptedException | CoreException e) {
// Handle error - actual implementation will show proper error dialog
}
}

public Image getImage() {
return Images.getCukesIcon();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Provides quick fixes (marker resolutions) for various Cucumber editor markers.
*
* <p>This package contains implementations of {@link org.eclipse.ui.IMarkerResolutionGenerator}
* that offer quick fixes for issues detected in Gherkin feature files.</p>
*
* <h2>Language Support Detection</h2>
* <p>The {@link io.cucumber.eclipse.editor.quickfix.LanguageSupportMarkerResolutionGenerator}
* provides quick fixes when enhanced language support is available but not installed.
* This is detected by checking project natures:
* <ul>
* <li>Java projects (org.eclipse.jdt.core.javanature) - suggests io.cucumber.eclipse.java</li>
* <li>Python projects (org.python.pydev.pythonNature) - suggests io.cucumber.eclipse.python</li>
* </ul>
* </p>
*
* @since 3.0.0
*/
package io.cucumber.eclipse.editor.quickfix;
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@
import java.util.stream.Collectors;

import org.eclipse.core.filebuffers.IDocumentSetupParticipant;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

import io.cucumber.eclipse.editor.Activator;
import io.cucumber.eclipse.editor.document.GherkinEditorDocument;
import io.cucumber.eclipse.editor.marker.MarkerFactory;
import io.cucumber.messages.types.ParseError;
Expand Down Expand Up @@ -103,12 +108,75 @@ protected IStatus run(IProgressMonitor monitor) {
return Status.CANCEL_STATUS;
}
MarkerFactory.syntaxErrorOnGherkin(resource, list, peristent);

// Check for language-specific support
checkLanguageSupport(resource);
}
}
jobMap.remove(document, this);
return monitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS;
}

private void checkLanguageSupport(IResource resource) {
try {
IProject project = resource.getProject();
if (project == null || !project.isAccessible()) {
return;
}

// Clear existing language support markers
MarkerFactory.deleteLanguageSupportMarkers(resource);

// Check for Java nature
if (project.hasNature("org.eclipse.jdt.core.javanature")) {
if (!isBundleInstalled("io.cucumber.eclipse.java")) {
MarkerFactory.languageSupportAvailable(resource, "Java",
"io.cucumber.eclipse.java", false);
}
}

// Check for Python nature
if (project.hasNature("org.python.pydev.pythonNature")) {
if (!isBundleInstalled("io.cucumber.eclipse.python")) {
MarkerFactory.languageSupportAvailable(resource, "Python",
"io.cucumber.eclipse.python", false);
}
}
} catch (CoreException e) {
Activator activator = Activator.getDefault();
if (activator != null) {
activator.getLog().log(new Status(IStatus.WARNING, Activator.PLUGIN_ID,
"Failed to check language support", e));
}
}
}

private boolean isBundleInstalled(String bundleSymbolicName) {
try {
Activator activator = Activator.getDefault();
if (activator == null) {
return false;
}

BundleContext context = activator.getBundle().getBundleContext();
if (context == null) {
return false;
}

for (Bundle bundle : context.getBundles()) {
if (bundleSymbolicName.equals(bundle.getSymbolicName())) {
int state = bundle.getState();
return state == Bundle.ACTIVE || state == Bundle.STARTING ||
state == Bundle.RESOLVED || state == Bundle.STOPPING;
}
}
} catch (Exception e) {
// If we can't check, assume it's not installed
return false;
}
return false;
}

}

}
Loading