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