如何针对不同类型的目标以不同方式突出显示 Xtext 交叉引用?

How to highlight an Xtext cross-reference differently for targets of different types?

我有一个 Xtext 语法(部分):

grammar mm.ecxt.MMLanguage hidden(WS, COMMENT)

import "http://www.eclipse.org/emf/2002/Ecore" as ecore

...

Statement:
    ConstantStatement |
    VariableStatement |
    LabeledStatement |
    ...

LabeledStatement:
    EssentialHypothesisStatement |
    ...

ConstantStatement:
    DOLLAR_C (constants+=ConstDecl)+ DOLLAR_DOT;

VariableStatement:
    DOLLAR_V (variables+=VarDecl)+ DOLLAR_DOT;

EssentialHypothesisStatement:
    name=LABEL DOLLAR_E (symbols+=[Decl|MATHSYMBOL])+ DOLLAR_DOT;

Decl: ConstDecl | VarDecl;

ConstDecl returns ConstDecl: name=MATHSYMBOL;

VarDecl returns VarDecl: name=MATHSYMBOL;

MATHSYMBOL: PARENOPEN | PARENCLOSE | QUESTIONMARK | COMPRESSED | TLABEL | WORD;
...

(完整语法为MMLanguage.xtext from current commit 328a5e7 of https://github.com/marnix/metamath-eclipse-xtext/。)

我的问题: 如何通过对常量和变量使用不同的颜色来突出显示 EssentialHypothesisStatement 中的 symbols?因此,如果 MATHSYMBOL 指的是 ConstDecl,那么它应该以一种方式突出显示,而 VarDecl.

则应以其他方式突出显示

我已经尝试以各种方式创建 ISemanticHighlightingCalculator,但我似乎无法检测到实际的引用类型是什么,无论是通过节点模型还是通过 Ecore 模型。一方面,与语法相关的方法只告诉我引用指向 Decl。另一方面,Ecore 模型的 EReferences 告诉我目标是 ConstDecl 还是 VarDecl,但我找不到源 MATHSYMBOL 的位置].

请注意,我更喜欢使用节点模型(而不是 Ecore 模型),因为我还想突出显示评论,并且出于性能原因,我无法承受对文档进行多次传递。

什么是 good/canonical/efficient/simple 实现此目的的方法?

从 EObject 的角度看一下 org.eclipse.xtext.nodemodel.util.NodeModelUtils.findNodesForFeature(EObject, EStructuralFeature) 从节点模型的角度来看,您可以使用 EObjectAtOffsetHelper

示例语法

Model:
    defs+=Def*
    uses+=Use*
    ;

Def:
    ADef | BDef;

ADef:
    "adef" name=ID
;

BDef: 
    "bdef" name=ID
;

Use:
    "use" def=[Def]
;

这里是 Impl

public class MyDslSemanticHighlightingCalculator implements ISemanticHighlightingCalculator {

    @Inject
    private MyDslGrammarAccess ga;

    @Inject
    private EObjectAtOffsetHelper helper;

    @Override
    public void provideHighlightingFor(XtextResource resource, IHighlightedPositionAcceptor accoptor, CancelIndicator cancelIndicator) {
        if (resource == null)
            return;
        IParseResult parseResult = resource.getParseResult();
        if (parseResult == null || parseResult.getRootNode() == null)
            return;
        BidiTreeIterable<INode> tree = parseResult.getRootNode().getAsTreeIterable();
        for (INode node : tree) {
            if (cancelIndicator.isCanceled()) {
                return;
            }
            if (node.getGrammarElement() instanceof CrossReference) {
                if (ga.getUseAccess().getDefDefCrossReference_1_0() == node.getGrammarElement()) {
                    EObject target = helper.resolveElementAt(resource, node.getOffset());
                    if (target instanceof ADef) {
                        accoptor.addPosition(node.getOffset(), node.getLength(), HighlightingStyles.COMMENT_ID);
                    } else if (target instanceof BDef) {
                        accoptor.addPosition(node.getOffset(), node.getLength(), HighlightingStyles.STRING_ID);
                    }
                }
            }
        }


    }

}