package typesystemIntegration.languageChecker;
/*Generated by MPS */
import jetbrains.mps.nodeEditor.checking.BaseEventProcessingEditorChecker;
import jetbrains.mps.project.MPSProject;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.nodeEditor.checking.UpdateResult;
import jetbrains.mps.nodeEditor.EditorComponent;
import jetbrains.mps.util.Cancellable;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import java.util.Set;
import jetbrains.mps.nodeEditor.EditorMessage;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.LinkedHashSet;
import org.jetbrains.mps.openapi.model.SReference;
import jetbrains.mps.typesystem.checking.HighlightUtil;
import jetbrains.mps.nodeEditor.cells.EditorCell;
import jetbrains.mps.openapi.editor.EditorContext;
import java.util.HashSet;
import jetbrains.mps.openapi.editor.EditorComponentState;
import jetbrains.mps.resolve.ResolverComponent;
import jetbrains.mps.resolve.ReferenceResolverUtils;
import jetbrains.mps.openapi.editor.cells.EditorCell_Label;
import org.jetbrains.mps.openapi.model.SNodeUtil;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.model.EditableSModel;
import jetbrains.mps.extapi.model.TransientSModel;
import jetbrains.mps.nodeEditor.EditorSettings;
import jetbrains.mps.nodeEditor.checking.EditorChecker;
import jetbrains.mps.typesystem.checking.TypesEditorChecker;
import jetbrains.mps.smodel.event.SModelPropertyEvent;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
public class AutoResolver extends BaseEventProcessingEditorChecker {
private boolean myForceAutofix = false;
private final MPSProject myProject;
public AutoResolver(@NotNull MPSProject project) {
myProject = project;
}
@NotNull
@Override
public UpdateResult update(EditorComponent editorComponent, boolean incremental, boolean allowQuickFixes, Cancellable cancellable) {
SNode rootNode = editorComponent.getEditedNode();
if (SNodeOperations.getModel(rootNode) == null || SNodeOperations.getModel(rootNode).getModule() == null) {
return UpdateResult.CANCELLED;
}
Set<EditorMessage> messages = SetSequence.fromSet(new LinkedHashSet<EditorMessage>());
// TODO: use same settings as in LanguageEditorChecker
Set<SReference> badReferences = collectBadReferences(rootNode);
for (SReference ref : SetSequence.fromSet(badReferences)) {
EditorMessage message = HighlightUtil.createHighlighterMessage(ref.getSourceNode(), "Unresolved reference", this);
SetSequence.fromSet(messages).addElement(message);
}
Set<EditorCell> editorErrorCells = editorComponent.getCellTracker().getErrorCells();
boolean hasWork = SetSequence.fromSet(badReferences).isNotEmpty() || !(editorErrorCells.isEmpty());
if (hasWork && isAutofix(SNodeOperations.getModel(rootNode), editorComponent.getEditorContext().getRepository())) {
runAutofix(badReferences, editorComponent.getEditorContext());
} else {
myForceAutofix = false;
}
return new UpdateResult.Completed(true, messages);
}
private void runAutofix(final Set<SReference> badReferences, final EditorContext editorContext) {
final EditorComponent editorComponent = (EditorComponent) editorContext.getEditorComponent();
Set<EditorCell> editorErrorCells = editorComponent.getCellTracker().getErrorCells();
final Set<EditorCell> errorCells = SetSequence.fromSetWithValues(new HashSet<EditorCell>(), editorErrorCells);
final boolean wasForceAutofix = myForceAutofix;
myForceAutofix = false;
myProject.getModelAccess().runWriteInEDT(new Runnable() {
@Override
public void run() {
myProject.getModelAccess().executeUndoTransparentCommand(new Runnable() {
@Override
public void run() {
EditorComponentState state = editorContext.getEditorComponentState();
// in case this becomes a performance bottleneck, consider reusing the editor's typechecking context
boolean doRecheckEditor = false;
// Trying to resolve all broken references using scope and then using substitute actions.
for (SReference brokenRef : SetSequence.fromSet(badReferences)) {
boolean resolvedByScope = ResolverComponent.getInstance().resolveScopesOnly(brokenRef, editorContext.getRepository());
if (resolvedByScope) {
doRecheckEditor = true;
}
SNode sourceNode = brokenRef.getSourceNode();
if (sourceNode == null) {
continue;
}
String referenceRole = brokenRef.getRole();
EditorCell cellWithRole = editorComponent.findNodeCellWithRole(sourceNode, referenceRole);
if (!(resolvedByScope)) {
if (cellWithRole == null) {
continue;
}
String resolveInfo = ReferenceResolverUtils.getResolveInfo(brokenRef, sourceNode);
if (resolveInfo == null) {
continue;
}
if (EditorBasedReferenceResolverUtils.substituteCell(cellWithRole, resolveInfo, editorContext)) {
doRecheckEditor = true;
}
}
// excluding reference cell which was substituted from the set of error cells
SetSequence.fromSet(errorCells).removeElement(cellWithRole);
}
// Trying to substitute all other error cells by using substitute actions.
for (EditorCell errorCell : SetSequence.fromSet(errorCells)) {
if (!(errorCell instanceof EditorCell_Label)) {
continue;
}
EditorCell_Label labelErrorCell = (EditorCell_Label) errorCell;
String errorText = labelErrorCell.getText();
if ((errorText == null || errorText.length() == 0)) {
continue;
}
if (EditorBasedReferenceResolverUtils.substituteCell(labelErrorCell, errorText, editorContext)) {
doRecheckEditor = true;
}
}
if (doRecheckEditor) {
// Something has changed in the editor, restore the previous state to avoid selection jump if possible
editorContext.restoreEditorComponentState(state);
if (wasForceAutofix) {
// re-running next checker in force autofix mode
myForceAutofix = true;
}
}
}
});
}
});
}
private Set<SReference> collectBadReferences(SNode cellNode) {
boolean needToEnableLogging = jetbrains.mps.smodel.SReference.disableLogging();
try {
Set<SReference> result = SetSequence.fromSet(new LinkedHashSet<SReference>());
for (SNode node : SNodeUtil.getDescendants(cellNode)) {
for (SReference ref : SNodeOperations.getReferences(node)) {
if (jetbrains.mps.util.SNodeOperations.getTargetNodeSilently(ref) == null) {
SetSequence.fromSet(result).addElement(ref);
}
}
}
return result;
} finally {
if (needToEnableLogging) {
jetbrains.mps.smodel.SReference.enableLogging();
}
}
}
private boolean isAutofix(SModel model, SRepository repository) {
return model instanceof EditableSModel && !(model instanceof TransientSModel) && ReferenceResolverUtils.canExecuteImmediately(model, repository) && (EditorSettings.getInstance().isAutoQuickFix() || myForceAutofix);
}
@Override
public boolean isLaterThan(EditorChecker editorChecker) {
return editorChecker instanceof TypesEditorChecker;
}
@Override
public void forceAutofix(EditorComponent editorComponent) {
super.forceAutofix(editorComponent);
myForceAutofix = true;
}
@Override
public boolean needsUpdateAfterPropertyEvent(SModelPropertyEvent event) {
return EditorSettings.getInstance().isAutoQuickFix() && MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name").getName().equals(event.getPropertyName());
}
}