mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-15 08:18:55 +00:00
Improve performance of Xtext editor with large grammars (#1562)
* Use EPackage.getEClassifier during grammar linking * Avoid redundant validations of predicates that cover unordered groups * Reuse previously collected information in the rule inspector for overridden values closes #1561
This commit is contained in:
parent
8a6605a018
commit
dc49298b09
5 changed files with 70 additions and 30 deletions
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009 itemis AG (http://www.itemis.eu) and others.
|
||||
* Copyright (c) 2009, 2020 itemis AG (http://www.itemis.eu) and others.
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0.
|
||||
|
@ -10,6 +10,8 @@ package org.eclipse.xtext.xtext;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.xtext.AbstractElement;
|
||||
|
@ -25,6 +27,7 @@ import org.eclipse.xtext.RuleCall;
|
|||
import org.eclipse.xtext.TerminalRule;
|
||||
import org.eclipse.xtext.validation.ValidationMessageAcceptor;
|
||||
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
@ -40,16 +43,19 @@ public class OverriddenValueInspector extends XtextRuleInspector<Boolean, Parser
|
|||
private Multimap<String, AbstractElement> assignedFeatures;
|
||||
|
||||
/**
|
||||
* Remember all visited unassigned rule calls in case the grammar is broken, e.g. has
|
||||
* Remember all visited rules in case the grammar is broken, e.g. has
|
||||
* no assignments (yet).
|
||||
*/
|
||||
private Set<AbstractRule> permanentlyVisited;
|
||||
|
||||
private Set<RuleCall> fragmentStack;
|
||||
|
||||
private Map<AbstractRule, ImmutableMultimap<String, AbstractElement>> assignedFeaturesAtEnd;
|
||||
|
||||
public OverriddenValueInspector(ValidationMessageAcceptor acceptor) {
|
||||
super(acceptor);
|
||||
assignedFeatures = newMultimap();
|
||||
assignedFeaturesAtEnd = new HashMap<>();
|
||||
permanentlyVisited = Sets.newHashSet();
|
||||
fragmentStack = Sets.newHashSet();
|
||||
}
|
||||
|
@ -73,6 +79,10 @@ public class OverriddenValueInspector extends XtextRuleInspector<Boolean, Parser
|
|||
|
||||
@Override
|
||||
protected Boolean doInspect(ParserRule rule) {
|
||||
permanentlyVisited.clear();
|
||||
fragmentStack.clear();
|
||||
assignedFeatures.clear();
|
||||
visitedRules.clear();
|
||||
return doSwitch(rule.getAlternatives());
|
||||
}
|
||||
|
||||
|
@ -160,9 +170,16 @@ public class OverriddenValueInspector extends XtextRuleInspector<Boolean, Parser
|
|||
if (!addVisited(parserRule))
|
||||
return Boolean.FALSE;
|
||||
Multimap<String, AbstractElement> prevAssignedFeatures = assignedFeatures;
|
||||
assignedFeatures = newMultimap();
|
||||
doSwitch(parserRule.getAlternatives());
|
||||
for (String feature : assignedFeatures.keySet())
|
||||
|
||||
// Cannot use #computeIfAbsent since we will call this recursively and that causes ConcurrentModificationExceptions
|
||||
ImmutableMultimap<String, AbstractElement> assignedInCalledRule = assignedFeaturesAtEnd.get(parserRule);
|
||||
if (assignedInCalledRule == null) {
|
||||
assignedFeatures = newMultimap();
|
||||
doSwitch(parserRule.getAlternatives());
|
||||
assignedInCalledRule = ImmutableMultimap.copyOf(assignedFeatures);
|
||||
assignedFeaturesAtEnd.put(parserRule, assignedInCalledRule);
|
||||
}
|
||||
for (String feature : assignedInCalledRule.keySet())
|
||||
prevAssignedFeatures.put(feature, object);
|
||||
assignedFeatures = prevAssignedFeatures;
|
||||
removeVisited(parserRule);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others.
|
||||
* Copyright (c) 2011, 2020 itemis AG (http://www.itemis.eu) and others.
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0.
|
||||
|
@ -63,7 +63,7 @@ public class PredicateUsesUnorderedGroupInspector extends XtextSwitch<Boolean> i
|
|||
validatedRules = Sets.newHashSet();
|
||||
doSwitch(rule);
|
||||
storedRules.addAll(validatedRules);
|
||||
storedRules = validatedRules;
|
||||
validatedRules = storedRules;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009 itemis AG (http://www.itemis.eu) and others.
|
||||
* Copyright (c) 2009, 2020 itemis AG (http://www.itemis.eu) and others.
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0.
|
||||
|
@ -28,7 +28,7 @@ public abstract class XtextRuleInspector<Result, RuleType extends AbstractRule>
|
|||
|
||||
private final ValidationMessageAcceptor acceptor;
|
||||
|
||||
private Collection<AbstractRule> visitedRules;
|
||||
Collection<AbstractRule> visitedRules;
|
||||
|
||||
public XtextRuleInspector(ValidationMessageAcceptor acceptor) {
|
||||
this.acceptor = acceptor;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2008 itemis AG (http://www.itemis.eu) and others.
|
||||
* Copyright (c) 2008, 2020 itemis AG (http://www.itemis.eu) and others.
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0.
|
||||
|
@ -9,7 +9,6 @@
|
|||
package org.eclipse.xtext.xtext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -49,6 +48,7 @@ import org.eclipse.xtext.scoping.impl.SimpleScope;
|
|||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.inject.Inject;
|
||||
|
@ -73,7 +73,7 @@ public class XtextScopeProvider extends AbstractScopeProvider {
|
|||
if (metaModel != null) {
|
||||
EPackage pack = metaModel.getEPackage();
|
||||
if (pack != null)
|
||||
return createClassifierScope(pack.getEClassifiers());
|
||||
return createClassifierScope(Collections.singletonList(pack));
|
||||
} else {
|
||||
return createReferencedPackagesScope(GrammarUtil.getGrammar(context));
|
||||
}
|
||||
|
@ -123,31 +123,50 @@ public class XtextScopeProvider extends AbstractScopeProvider {
|
|||
}
|
||||
|
||||
protected IScope createEnumLiteralsScope(EEnum eEnum) {
|
||||
return new SimpleScope(IScope.NULLSCOPE,Iterables.transform(eEnum.getELiterals(), new Function<EEnumLiteral, IEObjectDescription>() {
|
||||
@Override
|
||||
public IEObjectDescription apply(EEnumLiteral param) {
|
||||
return EObjectDescription.create(QualifiedName.create(param.getName()), param);
|
||||
}
|
||||
}));
|
||||
return new SimpleScope(IScope.NULLSCOPE, Iterables.transform(eEnum.getELiterals(), new Function<EEnumLiteral, IEObjectDescription>() {
|
||||
@Override
|
||||
public IEObjectDescription apply(EEnumLiteral param) {
|
||||
return EObjectDescription.create(QualifiedName.create(param.getName()), param);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
protected IScope createClassifierScope(Iterable<EClassifier> classifiers) {
|
||||
protected IScope createClassifierScope(List<EPackage> packages) {
|
||||
return new SimpleScope(
|
||||
IScope.NULLSCOPE,Iterables.transform(classifiers, new Function<EClassifier, IEObjectDescription>() {
|
||||
@Override
|
||||
public IEObjectDescription apply(EClassifier param) {
|
||||
return EObjectDescription.create(QualifiedName.create(param.getName()), param);
|
||||
IScope.NULLSCOPE,
|
||||
FluentIterable.from(packages)
|
||||
.transformAndConcat(EPackage::getEClassifiers)
|
||||
.transform(classifier->EObjectDescription.create(QualifiedName.create(classifier.getName()), classifier))) {
|
||||
|
||||
@Override
|
||||
protected IEObjectDescription getSingleLocalElementByName(QualifiedName name) {
|
||||
if (name.getSegmentCount() == 1) {
|
||||
EClassifier candidate = null;
|
||||
for(EPackage pack: packages) {
|
||||
EClassifier classifier = pack.getEClassifier(name.getFirstSegment());
|
||||
if (classifier != null) {
|
||||
if (candidate != null) {
|
||||
return null;
|
||||
}
|
||||
candidate = classifier;
|
||||
}
|
||||
}
|
||||
}));
|
||||
if (candidate != null) {
|
||||
return EObjectDescription.create(name, candidate);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected IScope createReferencedPackagesScope(Grammar g) {
|
||||
final Collection<EClassifier> allClassifiers = new ArrayList<EClassifier>();
|
||||
List<EPackage> allPackages = new ArrayList<EPackage>();
|
||||
for(AbstractMetamodelDeclaration decl: g.getMetamodelDeclarations()) {
|
||||
if (decl.getEPackage() != null)
|
||||
allClassifiers.addAll(decl.getEPackage().getEClassifiers());
|
||||
allPackages.add(decl.getEPackage());
|
||||
}
|
||||
return createClassifierScope(allClassifiers);
|
||||
return createClassifierScope(allPackages);
|
||||
}
|
||||
|
||||
protected IScope createScope(Resource resource, EClass type, IScope parent) {
|
||||
|
@ -164,7 +183,7 @@ public class XtextScopeProvider extends AbstractScopeProvider {
|
|||
if (EcorePackage.Literals.EPACKAGE == type) {
|
||||
return createEPackageScope(grammar);
|
||||
} else if (AbstractMetamodelDeclaration.class.isAssignableFrom(type.getInstanceClass())) {
|
||||
return new SimpleScope(IScope.NULLSCOPE,Iterables.transform(grammar.getMetamodelDeclarations(),
|
||||
return new SimpleScope(IScope.NULLSCOPE, Iterables.transform(grammar.getMetamodelDeclarations(),
|
||||
new Function<AbstractMetamodelDeclaration,IEObjectDescription>(){
|
||||
@Override
|
||||
public IEObjectDescription apply(AbstractMetamodelDeclaration from) {
|
||||
|
|
|
@ -1062,9 +1062,13 @@ public class XtextValidator extends AbstractDeclarativeValidator {
|
|||
}
|
||||
|
||||
@Check
|
||||
public void checkForOverriddenValue(final ParserRule rule) {
|
||||
public void checkForOverriddenValue(final Grammar grammar) {
|
||||
OverriddenValueInspector inspector = new OverriddenValueInspector(this);
|
||||
inspector.inspect(rule);
|
||||
for(AbstractRule rule: grammar.getRules()) {
|
||||
if (rule instanceof ParserRule) {
|
||||
inspector.inspect((ParserRule) rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Check
|
||||
|
|
Loading…
Reference in a new issue