package jetbrains.mps.vcs.platform.integration; /*Generated by MPS */ import com.intellij.diff.FrameDiffTool; import com.intellij.openapi.util.Key; import org.jetbrains.mps.openapi.model.SNodeId; import jetbrains.mps.vcs.diff.ui.common.Bounds; import jetbrains.mps.vcs.diff.ui.ModelDifferenceViewer; import org.jetbrains.annotations.NotNull; import com.intellij.diff.DiffContext; import com.intellij.diff.requests.ContentDiffRequest; import jetbrains.mps.project.MPSProject; import java.util.List; import com.intellij.diff.contents.DiffContent; import com.intellij.openapi.fileTypes.FileType; import jetbrains.mps.fileTypes.MPSFileTypeFactory; import jetbrains.mps.baseLanguage.tuples.runtime.Tuples; import org.jetbrains.mps.openapi.model.SModel; import com.intellij.diff.requests.DiffRequest; import com.intellij.diff.contents.EmptyContent; import com.intellij.diff.contents.DocumentContent; import com.intellij.diff.contents.FileContent; import org.jetbrains.annotations.Nullable; import javax.swing.JComponent; import jetbrains.mps.vcs.diff.merge.MergeTemporaryModel; import jetbrains.mps.smodel.SModelReference; import jetbrains.mps.smodel.SModelId; import jetbrains.mps.vfs.IFile; import jetbrains.mps.ide.vfs.VirtualFileUtils; import jetbrains.mps.vcspersistence.VCSPersistenceUtil; import jetbrains.mps.project.Project; import jetbrains.mps.smodel.SModelFileTracker; import jetbrains.mps.baseLanguage.closures.runtime.Wrappers; import jetbrains.mps.smodel.ModelAccessHelper; import jetbrains.mps.util.Computable; import jetbrains.mps.internal.collections.runtime.ListSequence; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations; import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple; public class ModelDiffViewer implements FrameDiffTool.DiffViewer { public static Key<SNodeId> DIFF_SHOW_ROOTID = new Key<SNodeId>("MPS.diff.rootid"); public static Key<Bounds> DIFF_NAVIGATE_TO = new Key<Bounds>("MPS.diff.navigateto"); private ModelDifferenceViewer myViewer; public ModelDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request) { MPSProject mpsProject = context.getProject().getComponent(MPSProject.class); List<DiffContent> contents = request.getContents(); FileType type = (contents.get(0).getContentType() != null ? contents.get(0).getContentType() : contents.get(1).getContentType()); if (MPSFileTypeFactory.MPS_ROOT_FILE_TYPE.equals(type) || MPSFileTypeFactory.MPS_HEADER_FILE_TYPE.equals(type)) { Tuples._2<SModel, SNodeId> oldModel = getModelAndRoot(mpsProject, contents.get(0), type); Tuples._2<SModel, SNodeId> newModel = getModelAndRoot(mpsProject, contents.get(1), type); SNodeId rootId = (newModel._1() != null ? newModel._1() : oldModel._1()); myViewer = new ModelDifferenceViewer(mpsProject, oldModel._0(), newModel._0(), rootId, false); } else { SModel oldModel = ModelDiffViewer.getModel(mpsProject, contents.get(0), type); SModel newModel = ModelDiffViewer.getModel(mpsProject, contents.get(1), type); // show one root only if requested SNodeId rootId = request.getUserData(DIFF_SHOW_ROOTID); myViewer = new ModelDifferenceViewer(mpsProject, oldModel, newModel, rootId, true); // navigate to specific place in editor if requested Bounds scrollTo = request.getUserData(DIFF_NAVIGATE_TO); if (scrollTo != null) { myViewer.navigate(scrollTo); } } List<String> titles = request.getContentTitles(); myViewer.setContentTitles(titles.get(0), titles.get(1)); } public static boolean canShow(@NotNull DiffContext context, @NotNull DiffRequest request) { if (!((request instanceof ContentDiffRequest))) { return false; } List<DiffContent> contents = ((ContentDiffRequest) request).getContents(); if (contents.size() != 2) { return false; } if (!((canShowContent(contents.get(0)) && canShowContent(contents.get(1))))) { return false; } if (contents.get(0) instanceof EmptyContent && contents.get(1) instanceof EmptyContent) { return false; } if (contents.get(0) instanceof ModelDiffContent || contents.get(1) instanceof ModelDiffContent) { return true; } for (FileType type : ModelDiffTool.DIFF_SUPPORTED_TYPES) { if (sameTypes(type, contents.get(0).getContentType(), contents.get(1).getContentType())) { return true; } } return false; } private static boolean canShowContent(@NotNull DiffContent content) { return content instanceof ModelDiffContent || content instanceof EmptyContent || content instanceof DocumentContent || content instanceof FileContent; } private static boolean sameTypes(@NotNull FileType baseType, @Nullable FileType type1, @Nullable FileType type2) { if (type1 != null && !(baseType.equals(type1))) { return false; } if (type2 != null && !(baseType.equals(type2))) { return false; } if (type1 == null && type2 == null) { return false; } return true; } @NotNull public JComponent getComponent() { return myViewer.getComponent(); } @Nullable public JComponent getPreferredFocusedComponent() { return myViewer.getPreferredFocusedComponent(); } @NotNull public FrameDiffTool.ToolbarComponents init() { return new FrameDiffTool.ToolbarComponents(); } public void dispose() { if (myViewer != null) { // in EDT? myViewer.dispose(); } } @Nullable private static SModel readModel(DiffContent content, FileType type) { if (content instanceof EmptyContent) { return new MergeTemporaryModel(new SModelReference(null, SModelId.generate(), "<empty merge model>"), true); } if (content instanceof FileContent) { IFile file = VirtualFileUtils.toIFile(((FileContent) content).getFile()); return VCSPersistenceUtil.loadModel(file); } if (content instanceof DocumentContent) { String text = ((DocumentContent) content).getDocument().getText(); return VCSPersistenceUtil.loadModel(text.getBytes(), type.getDefaultExtension()); } return null; } @Nullable private static IFile getFileByContent(@NotNull DiffContent content) { if (content instanceof FileContent) { return VirtualFileUtils.toIFile(((FileContent) content).getFile()); } return null; } @Nullable private static SModel getModel(Project mpsProject, @NotNull DiffContent content, FileType type) { // already a model? if (content instanceof ModelDiffContent) { return ((ModelDiffContent) content).getModel(); } // try to find model in repository IFile file = getFileByContent(content); if (file != null) { SModel model = SModelFileTracker.getInstance(mpsProject.getRepository()).findModel(file); if (model != null) { return model; } } // read model from content return readModel(content, type); } @Nullable private static Tuples._2<SModel, SNodeId> getModelAndRoot(Project mpsProject, DiffContent content, FileType type) { final Wrappers._T<SModel> model = new Wrappers._T<SModel>(null); // first try to find model in repository IFile file = getFileByContent(content); if (file != null) { model.value = SModelFileTracker.getInstance(mpsProject.getRepository()).findModel(file.getParent()); } if (model.value == null) { model.value = readModel(content, type); } if (model.value == null) { return null; } SNodeId nodeId = new ModelAccessHelper(mpsProject.getModelAccess()).runReadAction(new Computable<SNodeId>() { public SNodeId compute() { // todo: find root for models in repository by filename (important when new root added int per-root persistence) if (ListSequence.fromList(SModelOperations.roots(model.value, null)).count() == 1) { return ListSequence.fromList(SModelOperations.roots(model.value, null)).getElement(0).getNodeId(); } return null; } }); return MultiTuple.<SModel,SNodeId>from(model.value, nodeId); } }