package typesystemIntegration.languageChecker;
/*Generated by MPS */
import jetbrains.mps.nodeEditor.checking.BaseEditorChecker;
import jetbrains.mps.nodeEditor.checking.DisposableEditorChecker;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import java.util.Set;
import jetbrains.mps.checkers.AbstractNodeChecker;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.module.SRepository;
import jetbrains.mps.checkers.ConstraintsChecker;
import jetbrains.mps.checkers.StructureChecker;
import jetbrains.mps.checkers.TargetConceptChecker;
import jetbrains.mps.checkers.UsedLanguagesChecker;
import jetbrains.mps.nodeEditor.checking.EditorChecker;
import jetbrains.mps.typesystem.checking.TypesEditorChecker;
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.nodeEditor.EditorMessage;
import jetbrains.mps.typesystem.inference.TypeContextManager;
import jetbrains.mps.typesystem.inference.ITypechecking;
import jetbrains.mps.typesystem.inference.TypeCheckingContext;
import com.intellij.openapi.project.IndexNotReadyException;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.nodeEditor.inspector.InspectorEditorComponent;
import org.apache.log4j.Level;
import java.util.Collections;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.checkers.LanguageErrorsComponent;
import java.util.List;
import jetbrains.mps.baseLanguage.tuples.runtime.Tuples;
import jetbrains.mps.errors.QuickFix_Runtime;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import jetbrains.mps.errors.IErrorReporter;
import jetbrains.mps.checkers.ErrorReportUtil;
import jetbrains.mps.errors.MessageStatus;
import jetbrains.mps.nodeEditor.HighlighterMessage;
import jetbrains.mps.typesystem.checking.HighlightUtil;
import jetbrains.mps.util.NameUtil;
import jetbrains.mps.errors.QuickFixProvider;
import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.EditableSModel;
import jetbrains.mps.extapi.model.TransientSModel;
import jetbrains.mps.nodeEditor.EditorSettings;
public class LanguageEditorChecker extends BaseEditorChecker implements DisposableEditorChecker {
private static final Logger LOG = LogManager.getLogger(LanguageEditorChecker.class);
private boolean myMessagesChanged = false;
private boolean myForceRunQuickFixes = false;
private Set<AbstractNodeChecker> myRules = SetSequence.fromSet(new HashSet<AbstractNodeChecker>());
private final ErrorComponents myErrorComponents;
private RefScopeCheckerInEditor myScopeChecker;
public LanguageEditorChecker(@NotNull SRepository projectRepo) {
SetSequence.fromSet(myRules).addElement(new ConstraintsChecker());
SetSequence.fromSet(myRules).addElement(myScopeChecker = new RefScopeCheckerInEditor());
SetSequence.fromSet(myRules).addElement(new StructureChecker());
SetSequence.fromSet(myRules).addElement(new TargetConceptChecker());
SetSequence.fromSet(myRules).addElement(new UsedLanguagesChecker());
myErrorComponents = new ErrorComponents(projectRepo);
}
@Override
public void dispose() {
myErrorComponents.dispose();
}
@Override
public boolean isLaterThan(EditorChecker checker) {
if (checker instanceof TypesEditorChecker) {
return true;
}
if (checker instanceof AutoResolver) {
return true;
}
return false;
}
@NotNull
public UpdateResult update(final EditorComponent editorComponent, final boolean incremental, boolean applyQuickFixes, final Cancellable cancellable) {
final SNode node = editorComponent.getEditedNode();
try {
Set<EditorMessage> messages = TypeContextManager.getInstance().runTypeCheckingComputation(editorComponent.getTypecheckingContextOwner(), node, new ITypechecking.Computation<Set<EditorMessage>>() {
@Override
public Set<EditorMessage> compute(TypeCheckingContext typeCheckingContext) {
return doCreateMessages(node, incremental, editorComponent.getEditorContext(), typeCheckingContext, cancellable);
}
});
return new UpdateResult.Completed(myMessagesChanged, messages);
} catch (IndexNotReadyException e) {
myErrorComponents.clear(editorComponent);
throw e;
}
}
private Set<EditorMessage> doCreateMessages(SNode node, boolean wasCheckedOnce, EditorContext editorContext, TypeCheckingContext typeCheckingContext, Cancellable cancellable) {
EditorComponent editorComponent = (EditorComponent) editorContext.getEditorComponent();
boolean inspector = editorComponent instanceof InspectorEditorComponent;
myMessagesChanged = false;
SNode editedNode = editorComponent.getEditedNode();
if (editedNode == null) {
if (LOG.isEnabledFor(Level.ERROR)) {
LOG.error("edited node is null");
}
return Collections.emptySet();
}
if (node.getModel() == null || SNodeOperations.getModel(editedNode) == null) {
// descriptor is null for a replaced model
// after model is replaced but before it is disposed (this can happen asyncronously)
return Collections.emptySet();
}
LanguageErrorsComponent errorsComponent = myErrorComponents.getErrorsComponent(editorComponent);
if (errorsComponent == null) {
return Collections.emptySet();
}
if (!(wasCheckedOnce)) {
errorsComponent.clear();
}
myMessagesChanged = runChecks(inspector, errorsComponent, typeCheckingContext, node, editorContext, cancellable);
if (!(myMessagesChanged)) {
// skipping further processing if nothing was changed
return Collections.emptySet();
}
return createMessages(editorContext, inspector, errorsComponent, editedNode);
}
private boolean runChecks(boolean inspector, LanguageErrorsComponent errorsComponent, TypeCheckingContext typeCheckingContext, SNode node, EditorContext editorContext, Cancellable cancellable) {
if (inspector) {
return errorsComponent.checkInspector();
}
try {
if (typeCheckingContext != null) {
typeCheckingContext.setIsNonTypesystemComputation();
}
myScopeChecker.setEditorComponent((EditorComponent) editorContext.getEditorComponent());
return errorsComponent.check(SNodeOperations.getContainingRoot(((SNode) node)), myRules, editorContext.getRepository(), cancellable);
} finally {
if (typeCheckingContext != null) {
typeCheckingContext.resetIsNonTypesystemComputation();
}
}
}
private Set<EditorMessage> createMessages(final EditorContext editorContext, boolean inspector, LanguageErrorsComponent errorsComponent, SNode editedNode) {
Set<EditorMessage> result = SetSequence.fromSet(new HashSet<EditorMessage>());
boolean runQuickFixes = shouldRunQuickFixs(editorContext.getModel(), inspector);
final List<Tuples._2<QuickFix_Runtime, SNode>> quickFixesToExecute = ListSequence.fromList(new ArrayList<Tuples._2<QuickFix_Runtime, SNode>>());
for (IErrorReporter errorReporter : errorsComponent.getErrors()) {
// todo here should be processor-based architecture, like in other checkers
if (!(ErrorReportUtil.shouldReportError(errorReporter.getSNode()))) {
continue;
}
SNode nodeWithError = errorReporter.getSNode();
if (!(ListSequence.fromList(SNodeOperations.getNodeAncestors(nodeWithError, null, true)).contains(editedNode))) {
// in inspector skipping all messages for invisible nodes
continue;
}
MessageStatus status = errorReporter.getMessageStatus();
String errorString = errorReporter.reportError();
HighlighterMessage message = HighlightUtil.createHighlighterMessage(nodeWithError, NameUtil.capitalize(status.getPresentation()) + ": " + errorString, errorReporter, LanguageEditorChecker.this);
List<QuickFixProvider> intentionProviders = message.getIntentionProviders();
if (runQuickFixes && intentionProviders.size() == 1 && intentionProviders.get(0).isExecutedImmediately()) {
QuickFix_Runtime quickFix = intentionProviders.get(0).getQuickFix();
if (quickFix != null) {
ListSequence.fromList(quickFixesToExecute).addElement(MultiTuple.<QuickFix_Runtime,SNode>from(quickFix, nodeWithError));
}
}
SetSequence.fromSet(result).addElement(message);
}
if (inspector) {
return result;
}
// running quick fixes in main editor only
final boolean wasForceRunQuickFixes = myForceRunQuickFixes;
myForceRunQuickFixes = false;
if (ListSequence.fromList(quickFixesToExecute).isNotEmpty()) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
editorContext.getRepository().getModelAccess().executeUndoTransparentCommand(new Runnable() {
public void run() {
for (Tuples._2<QuickFix_Runtime, SNode> fix : quickFixesToExecute) {
if (SNodeOperations.getModel(fix._1()) != null) {
fix._0().execute(fix._1());
if (wasForceRunQuickFixes) {
// forcing to execute quickFixes for all errors reported on the modified model
myForceRunQuickFixes = true;
}
}
}
}
});
}
});
}
return result;
}
private boolean shouldRunQuickFixs(SModel model, boolean inspector) {
if (inspector || !(model instanceof EditableSModel) || model instanceof TransientSModel) {
return false;
}
return EditorSettings.getInstance().isAutoQuickFix() || myForceRunQuickFixes;
}
@Override
public void forceAutofix(EditorComponent component) {
myForceRunQuickFixes = true;
}
@Override
public boolean needsUpdate(EditorComponent component) {
return true;
}
}