eclipse-xtext / eclipse-xtext/xtext

Quick fix across files

Open
#3,447 12 comments 0 reactions 0 assignees View on GitHub

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.