eclipse-xtext / eclipse-xtext/xtext
Quick fix across files
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 831
- Forks
- 330
- Avg merge
- 3d 7h
- Merged PRs (30d)
- 12
Description
Problem
I am trying to create a quickfix that can work across files, in the LSP context.
The goal is to add a missing reference in block to solve a linking issue.
The quick fix works when the block is in the document in which the error is present and if there is no block, but not if the block is in another file.
Expected behavior
The quick fix works across files.
Actual behavior
The quick fix does nothing.
Example
If I activate my quick fix in this situation, it works, and the missing reference is created:
test.mydsl
other MyElement references SourcePresent
other MyElement2 references SourceNOTPresent
sources { SourcePresent }
This situation works as well (a source block is created with the missing reference):
test.mydsl
other MyElement references SourcePresent
other MyElement2 references SourceNOTPresent
But in this situation it does nothing when I select the quick fix:
test.mydsl
other MyElement references SourcePresent
other MyElement2 references SourceNOTPresent
dict.mydsl
sources { SourcePresent }
Files to reproduce the issue
The quickfix script:
package org.xtext.example.mydsl.ide;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.ide.editor.quickfix.AbstractDeclarativeIdeQuickfixProvider;
import org.eclipse.xtext.ide.editor.quickfix.DiagnosticResolutionAcceptor;
import org.eclipse.xtext.ide.editor.quickfix.QuickFix;
import org.xtext.example.mydsl.myDsl.AbstractElement;
import org.xtext.example.mydsl.myDsl.Model;
import org.xtext.example.mydsl.myDsl.MyDslFactory;
import org.xtext.example.mydsl.myDsl.Source;
import org.xtext.example.mydsl.myDsl.SourcesContainer;
public class MyDslQuickfixProvider extends AbstractDeclarativeIdeQuickfixProvider{
@QuickFix(org.eclipse.xtext.diagnostics.Diagnostic.LINKING_DIAGNOSTIC)
public void fixCreateMissingRegference(DiagnosticResolutionAcceptor acceptor) {
acceptor.accept("create missing ref", (diag, obj)->{
return (objInLambda)->{
Source newSource = MyDslFactory.eINSTANCE.createSource();
newSource.setName(extractMissingSourceName(diag.getMessage()));
ResourceSet resourceSet = obj.eResource().getResourceSet();
Resource targetResource = findSourceResource(resourceSet);
EObject targetRoot = null;
Model targetModel = null;
if (targetResource != null) {
targetRoot = targetResource.getContents().get(0);
targetModel = (Model) targetRoot;
}else {
targetRoot = obj.eResource().getContents().get(0);
targetModel = (Model) targetRoot;
}
boolean existingBlock = false;
// We look for the resource containing the Source block
SourcesContainer sourceBlockInTarget = null;
for (AbstractElement element : targetModel.getElements()) {
if (element instanceof SourcesContainer) {
sourceBlockInTarget = (SourcesContainer) element;
}
}
if (sourceBlockInTarget != null) {
sourceBlockInTarget.getDefinitions().add(newSource);
existingBlock = true;
}
if (!existingBlock) {
SourcesContainer newSourceBlock = MyDslFactory.eINSTANCE.createSourcesContainer();
newSourceBlock.getDefinitions().add(newSource);
targetModel.getElements().add(newSourceBlock);
}
};
});
}
private String extractMissingSourceName(String message) {
if (message.contains("Couldn't resolve reference to Source")) {
String sub = message.substring(message.indexOf("'") + 1, message.lastIndexOf("'"));
String subsub = sub.substring(sub.indexOf("'")+1, sub.length());
return subsub;
}
return null;
}
private Resource findSourceResource(ResourceSet resourceSet) {
// We look for the resource containing the source block
for (Resource resource : resourceSet.getResources()) {
if (resource.getURI().fileExtension().equals("mydsl") && !resource.getContents().isEmpty()) {
EObject root = resource.getContents().get(0);
if (root instanceof Model model) {
for (AbstractElement element : model.getElements()) {
if (element instanceof SourcesContainer) {
return resource;
}
}
}
}
}
return null;
}
}
the grammar:
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
Model:
elements+=AbstractElement*;
AbstractElement:
SourcesContainer | OtherElement;
SourcesContainer:
'sources' '{'
definitions+=Source (',' definitions+=Source)*
'}';
Source:
name=ID;
OtherElement:
'other' name=ID 'references' source=[Source|ID];
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the provided MyDslQuickfixProvider and its ResourceSet/resource-selection logic, then reproduce the quickfix using the test.mydsl and dict.mydsl examples against the supplied MyDsl grammar. Done means the quickfix creates the missing reference in the existing SourcesContainer in another file, while retaining the documented same-file behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100