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()); } }