package jetbrains.mps.vcs.annotate; /*Generated by MPS */ import jetbrains.mps.nodeEditor.leftHighlighter.AbstractLeftColumn; import java.awt.Color; import jetbrains.mps.openapi.editor.style.StyleRegistry; import java.awt.Font; import jetbrains.mps.nodeEditor.EditorSettings; import java.util.List; import jetbrains.mps.internal.collections.runtime.ListSequence; import java.util.ArrayList; import java.util.Map; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.HashMap; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.mps.openapi.model.EditableSModel; import jetbrains.mps.smodel.persistence.lines.LineContent; import jetbrains.mps.vcs.diff.changes.ModelChange; import java.util.Set; import com.intellij.util.messages.MessageBusConnection; import jetbrains.mps.nodeEditor.leftHighlighter.LeftEditorHighlighter; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeId; import jetbrains.mps.internal.collections.runtime.SetSequence; import java.util.HashSet; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations; import org.jetbrains.mps.openapi.language.SAbstractConcept; import jetbrains.mps.internal.collections.runtime.ISelector; import org.jetbrains.mps.openapi.model.SModel; import jetbrains.mps.baseLanguage.closures.runtime.Wrappers; import jetbrains.mps.smodel.persistence.def.ModelReadException; import jetbrains.mps.vcspersistence.VCSPersistenceSupport; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; import com.intellij.openapi.ui.MessageType; import jetbrains.mps.internal.collections.runtime.Sequence; import jetbrains.mps.internal.collections.runtime.IWhereFilter; import com.intellij.openapi.vcs.actions.AnnotationColors; import com.intellij.ui.ColorUtil; import jetbrains.mps.vcs.changesmanager.CurrentDifferenceRegistry; import jetbrains.mps.vcs.changesmanager.CurrentDifference; import jetbrains.mps.smodel.ModelAccess; import jetbrains.mps.internal.collections.runtime.IVisitor; import jetbrains.mps.nodeEditor.highlighter.EditorComponentCreateListener; import jetbrains.mps.vcs.diff.changes.SetPropertyChange; import jetbrains.mps.smodel.persistence.lines.PropertyLineContent; import jetbrains.mps.vcs.diff.changes.SetReferenceChange; import jetbrains.mps.smodel.persistence.lines.ReferenceLineContent; import jetbrains.mps.vcs.diff.changes.NodeGroupChange; import jetbrains.mps.util.IterableUtil; import jetbrains.mps.smodel.persistence.lines.NodeLineContent; import java.awt.Graphics; import jetbrains.mps.nodeEditor.EditorComponent; import java.awt.Graphics2D; import jetbrains.mps.nodeEditor.cells.FontRegistry; import java.awt.FontMetrics; import jetbrains.mps.internal.collections.runtime.ILeftCombinator; import org.jetbrains.annotations.Nullable; import jetbrains.mps.openapi.editor.cells.EditorCell; import jetbrains.mps.nodeEditor.messageTargets.CellFinder; import java.util.Collections; import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes; import java.util.Iterator; import jetbrains.mps.baseLanguage.closures.runtime.YieldingIterator; import java.awt.event.MouseEvent; import java.awt.Cursor; import com.intellij.openapi.vcs.annotate.LineAnnotationAspectAdapter; import javax.swing.JPopupMenu; import com.intellij.openapi.actionSystem.AnAction; import jetbrains.mps.workbench.action.BaseAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Separator; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.util.ui.TextTransferable; import com.intellij.openapi.actionSystem.DefaultActionGroup; import jetbrains.mps.workbench.action.ActionUtils; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.project.Project; import jetbrains.mps.vcs.changesmanager.CurrentDifferenceAdapter; import org.jetbrains.annotations.NotNull; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.vcs.changes.BackgroundFromStartOption; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.vcs.CommittedChangesProvider; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.FilePathImpl; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangesUtil; import java.io.File; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.fileTypes.FileType; import jetbrains.mps.fileTypes.MPSFileTypeFactory; import jetbrains.mps.vcspersistence.VCSPersistenceUtil; import jetbrains.mps.util.FileUtil; import jetbrains.mps.project.MPSExtentions; import jetbrains.mps.vcs.diff.merge.MergeTemporaryModel; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations; import com.intellij.diff.contents.DiffContent; import com.intellij.diff.DiffContentFactory; import com.intellij.diff.requests.DiffRequest; import com.intellij.diff.requests.SimpleDiffRequest; import jetbrains.mps.vcs.platform.integration.ModelDiffViewer; import com.intellij.diff.DiffManager; import com.intellij.openapi.vcs.VcsException; import jetbrains.mps.vcs.diff.ChangeSet; public class AnnotationColumn extends AbstractLeftColumn { private static final Color ANNOTATION_COLOR = StyleRegistry.getInstance().getColor("ANNOTATIONS_COLOR"); private Font myFont = EditorSettings.getInstance().getDefaultEditorFont(); private List<AnnotationAspectSubcolumn> myAspectSubcolumns = ListSequence.fromList(new ArrayList<AnnotationAspectSubcolumn>()); private List<Integer> myPseudoLinesY; private List<Integer> myPseudoLinesToFileLines; private int mySubcolumnInterval; private Map<String, Color> myAuthorsToColors = MapSequence.fromMap(new HashMap<String, Color>()); private FileAnnotation myFileAnnotation; private Map<VcsRevisionNumber, VcsFileRevision> myRevisionNumberToRevision = MapSequence.fromMap(new HashMap<VcsRevisionNumber, VcsFileRevision>()); private LineAnnotationAspect myAuthorAnnotationAspect; private AbstractVcs myVcs; private VirtualFile myVirtualFile; private EditableSModel myModel; private List<LineContent> myFileLineToContent; private Map<ModelChange, LineContent[]> myChangesToLineContents = MapSequence.fromMap(new HashMap<ModelChange, LineContent[]>()); private Set<Integer> myCurrentPseudoLines = null; private VcsRevisionRange myRevisionRange; private ViewActionGroup myViewActionGroup; private AnnotationColumn.MyDifferenceListener myDifferenceListener = new AnnotationColumn.MyDifferenceListener(); private boolean myShowAdditionalInfo = false; private MessageBusConnection myMessageBusConnection; public AnnotationColumn(LeftEditorHighlighter leftEditorHighlighter, SNode root, FileAnnotation fileAnnotation, final AbstractVcs vcs, VirtualFile virtualFile) { super(leftEditorHighlighter); Set<SNodeId> descendantIds = SetSequence.fromSetWithValues(new HashSet<SNodeId>(), ListSequence.fromList(SNodeOperations.getNodeDescendants(root, null, true, new SAbstractConcept[]{})).select(new ISelector<SNode, SNodeId>() { public SNodeId select(SNode n) { return n.getNodeId(); } })); SModel model = SNodeOperations.getModel(root); myFileAnnotation = fileAnnotation; for (VcsFileRevision rev : ListSequence.fromList(fileAnnotation.getRevisions())) { MapSequence.fromMap(myRevisionNumberToRevision).put(rev.getRevisionNumber(), rev); } final Wrappers._T<ModelReadException> mre = new Wrappers._T<ModelReadException>(null); try { myFileLineToContent = VCSPersistenceSupport.getLineToContentMap(myFileAnnotation.getAnnotatedContent()); } catch (ModelReadException e) { mre.value = e; } if (myFileLineToContent == null) { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { String msg = "Couldn't show annotation"; if (mre.value != null && mre.value.getCause() != null) { msg += ": " + mre.value.getCause().getMessage(); } ToolWindowManager.getInstance(vcs.getProject()).notifyByBalloon(ChangesViewContentManager.TOOLWINDOW_ID, MessageType.WARNING, msg); close(); } }); return; } myAuthorAnnotationAspect = Sequence.fromIterable(Sequence.fromArray(myFileAnnotation.getAspects())).findFirst(new IWhereFilter<LineAnnotationAspect>() { public boolean accept(LineAnnotationAspect a) { return LineAnnotationAspect.AUTHOR.equals(a.getId()); } }); Map<SNodeId, Integer> nodeIdToFileLine = MapSequence.fromMap(new HashMap<SNodeId, Integer>()); for (int line = 0; line < ListSequence.fromList(myFileLineToContent).count(); line++) { SNode node = null; SNodeId id = check_5mnya_a0b0k0v(ListSequence.fromList(myFileLineToContent).getElement(line)); if (id != null && SetSequence.fromSet(descendantIds).contains(id)) { node = model.getNode(id); } if (node == null) { continue; } if (MapSequence.fromMap(nodeIdToFileLine).containsKey(id)) { MapSequence.fromMap(nodeIdToFileLine).put(id, getFileLineWithMaxRevision(MapSequence.fromMap(nodeIdToFileLine).get(id), line)); } else { MapSequence.fromMap(nodeIdToFileLine).put(id, line); } } ListSequence.fromList(myAspectSubcolumns).addSequence(Sequence.fromIterable(Sequence.fromArray(fileAnnotation.getAspects())).select(new ISelector<LineAnnotationAspect, AnnotationAspectSubcolumn>() { public AnnotationAspectSubcolumn select(LineAnnotationAspect a) { return new AnnotationAspectSubcolumn(AnnotationColumn.this, a); } })); ListSequence.fromList(myAspectSubcolumns).addElement(new CommitNumberSubcolumn(this, myFileAnnotation)); for (VcsFileRevision revision : ListSequence.fromList(myFileAnnotation.getRevisions())) { String author = revision.getAuthor(); if (!(MapSequence.fromMap(myAuthorsToColors).containsKey(author))) { Color color = AnnotationColors.BG_COLORS[MapSequence.fromMap(myAuthorsToColors).count() % AnnotationColors.BG_COLORS.length]; if (StyleRegistry.getInstance().isDarkTheme()) { color = ColorUtil.shift(color, 0.3); } MapSequence.fromMap(myAuthorsToColors).put(author, color); } } myViewActionGroup = new ViewActionGroup(this, myAspectSubcolumns); myRevisionRange = new VcsRevisionRange(this, myFileAnnotation); ListSequence.fromList(myAspectSubcolumns).addElement(new HighlightRevisionSubcolumn(this, myRevisionRange)); myVirtualFile = virtualFile; myModel = (EditableSModel) model; myVcs = vcs; final CurrentDifferenceRegistry registry = CurrentDifferenceRegistry.getInstance(getProject()); registry.getCommandQueue().runTask(new Runnable() { public void run() { final CurrentDifference currentDifference = registry.getCurrentDifference(myModel); ModelAccess.instance().runReadAction(new Runnable() { public void run() { ListSequence.fromList(check_5mnya_a0a0a0a1a0a0v0v(currentDifference.getChangeSet())).visitAll(new IVisitor<ModelChange>() { public void visit(ModelChange ch) { saveChange(ch); } }); } }); currentDifference.addDifferenceListener(myDifferenceListener); } }); myMessageBusConnection = getProject().getMessageBus().connect(); myMessageBusConnection.subscribe(EditorComponentCreateListener.EDITOR_COMPONENT_CREATION, new AnnotationColumn.MyEditorComponentCreateListener()); } private void saveChange(ModelChange ch) { if (ch instanceof SetPropertyChange) { SetPropertyChange spc = (SetPropertyChange) ch; MapSequence.fromMap(myChangesToLineContents).put(ch, new LineContent[]{new PropertyLineContent(spc.getAffectedNodeId(), spc.getPropertyName())}); } else if (ch instanceof SetReferenceChange) { SetReferenceChange src = (SetReferenceChange) ch; MapSequence.fromMap(myChangesToLineContents).put(ch, new LineContent[]{new ReferenceLineContent(src.getAffectedNodeId(), src.getRole())}); } else if (ch instanceof NodeGroupChange) { NodeGroupChange ngc = (NodeGroupChange) ch; List<? extends SNode> newChildren = IterableUtil.asList(myModel.getNode(ngc.getParentNodeId()).getChildren(ngc.getRole())); MapSequence.fromMap(myChangesToLineContents).put(ch, ListSequence.fromList(newChildren).page(ngc.getResultBegin(), ngc.getResultEnd()).select(new ISelector<SNode, NodeLineContent>() { public NodeLineContent select(SNode n) { return new NodeLineContent(n.getNodeId()); } }).toGenericArray(NodeLineContent.class)); } } private void calculateCurrentPseudoLinesLater() { ModelAccess.instance().runReadInEDT(new Runnable() { public void run() { myCurrentPseudoLines = SetSequence.fromSet(new HashSet<Integer>()); for (LineContent[] lineContents : MapSequence.fromMap(myChangesToLineContents).values()) { for (LineContent lc : lineContents) { SetSequence.fromSet(myCurrentPseudoLines).addSequence(Sequence.fromIterable(getPseudoLinesForContent(lc))); } } getLeftEditorHighlighter().repaint(); } }); } @Override public String getName() { return "Annotations"; } @Override public void paint(Graphics graphics) { graphics.setFont(myFont); EditorComponent.turnOnAliasingIfPossible((Graphics2D) graphics); Map<AnnotationAspectSubcolumn, Integer> subcolumnToX = MapSequence.fromMap(new HashMap<AnnotationAspectSubcolumn, Integer>()); int x = getX() + 1; for (AnnotationAspectSubcolumn subcolumn : ListSequence.fromList(myAspectSubcolumns)) { MapSequence.fromMap(subcolumnToX).put(subcolumn, x); if (subcolumn.isEnabled() || myShowAdditionalInfo) { x += subcolumn.getWidth() + mySubcolumnInterval; } } for (int pseudoLine = 0; pseudoLine < ListSequence.fromList(myPseudoLinesY).count(); pseudoLine++) { if (SetSequence.fromSet(myCurrentPseudoLines).contains(pseudoLine)) { continue; } int fileLine = ListSequence.fromList(myPseudoLinesToFileLines).getElement(pseudoLine); int height = (pseudoLine == ListSequence.fromList(myPseudoLinesY).count() - 1 ? getEditorComponent().getHeight() - ListSequence.fromList(myPseudoLinesY).last() : ListSequence.fromList(myPseudoLinesY).getElement(pseudoLine + 1) - ListSequence.fromList(myPseudoLinesY).getElement(pseudoLine)); if (myAuthorAnnotationAspect != null && ViewAction.isSet(ViewAction.COLORS)) { String author = myAuthorAnnotationAspect.getValue(fileLine); graphics.setColor(MapSequence.fromMap(myAuthorsToColors).get(author)); graphics.fillRect(getX(), ListSequence.fromList(myPseudoLinesY).getElement(pseudoLine), getWidth(), height); } graphics.setColor(ANNOTATION_COLOR); if (myRevisionRange.isFileLineHighlighted(fileLine)) { graphics.setFont(FontRegistry.getInstance().getFont(myFont.getName(), myFont.getStyle() | Font.BOLD, myFont.getSize())); } FontMetrics metrics = graphics.getFontMetrics(); if (height < metrics.getHeight()) { continue; } for (AnnotationAspectSubcolumn subcolumn : ListSequence.fromList(myAspectSubcolumns).where(new IWhereFilter<AnnotationAspectSubcolumn>() { public boolean accept(AnnotationAspectSubcolumn s) { return myShowAdditionalInfo || s.isEnabled(); } })) { String text = subcolumn.getTextForFileLine(fileLine); int textX = MapSequence.fromMap(subcolumnToX).get(subcolumn); if (subcolumn.isRightAligned()) { textX += subcolumn.getWidth() - metrics.stringWidth(text); } graphics.drawString(text, textX, metrics.getAscent() + ListSequence.fromList(myPseudoLinesY).getElement(pseudoLine)); } } } @Override public int getWidth() { return (ListSequence.fromList(myAspectSubcolumns).isEmpty() ? 0 : ListSequence.fromList(myAspectSubcolumns).select(new ISelector<AnnotationAspectSubcolumn, Integer>() { public Integer select(AnnotationAspectSubcolumn s) { return (s.isEnabled() || myShowAdditionalInfo ? s.getWidth() : 0); } }).reduceLeft(new ILeftCombinator<Integer, Integer>() { public Integer combine(Integer a, Integer b) { return a + mySubcolumnInterval + b; } }) + 1 + mySubcolumnInterval / 2); } @Nullable private EditorCell findCellForContent(@Nullable LineContent content) { if (content == null) { return null; } EditorComponent editor = getEditorComponent(); SNode editedNode = editor.getEditedNode(); SNode node = editedNode.getModel().getNode(content.getNodeId()); if (node == null || !(ListSequence.fromList(SNodeOperations.getNodeAncestors(node, null, true)).contains(editedNode))) { return null; } if (content instanceof NodeLineContent) { return editor.getBigValidCellForNode(node); } else if (content instanceof PropertyLineContent) { return CellFinder.getCellForProperty(editor, node, ((PropertyLineContent) content).getName()); } else if (content instanceof ReferenceLineContent) { return CellFinder.getCellForReference(editor, node, ((ReferenceLineContent) content).getRole()); } else { return null; } } private Iterable<Integer> getPseudoLinesForContent(@Nullable LineContent content) { EditorCell cell = findCellForContent(content); if (cell == null) { return Sequence.fromIterable(Collections.<Integer>emptyList()); } final int startPseudoLine = Collections.binarySearch((List) myPseudoLinesY, cell.getY()); final Wrappers._int endPseudoLine = new Wrappers._int(Collections.binarySearch((List) myPseudoLinesY, cell.getY() + cell.getHeight())); if (endPseudoLine.value < 0) { endPseudoLine.value = -endPseudoLine.value - 1; } return new _FunctionTypes._return_P0_E0<Iterable<Integer>>() { public Iterable<Integer> invoke() { return new Iterable<Integer>() { public Iterator<Integer> iterator() { return new YieldingIterator<Integer>() { private int __CP__ = 0; protected boolean moveToNext() { __loop__: do { __switch__: switch (this.__CP__) { case -1: assert false : "Internal error"; return false; case 2: this._2_pseudoLine = startPseudoLine; case 3: if (!(_2_pseudoLine < endPseudoLine.value)) { this.__CP__ = 1; break; } this.__CP__ = 4; break; case 5: _2_pseudoLine++; this.__CP__ = 3; break; case 6: this.__CP__ = 5; this.yield(_2_pseudoLine); return true; case 0: this.__CP__ = 2; break; case 4: this.__CP__ = 6; break; default: break __loop__; } } while (true); return false; } private int _2_pseudoLine; }; } }; } }.invoke(); } @Override public void relayout() { EditorComponent editor = getEditorComponent(); if (editor == null || editor.isDisposed()) { return; } Iterable<EditorCell> nonTrivialCells = Sequence.fromIterable(EditorUtils.getCellDescendants(editor.getRootCell())).where(new IWhereFilter<EditorCell>() { public boolean accept(EditorCell cell) { return cell.getWidth() * cell.getHeight() != 0; } }); Set<Integer> yCoordinatesSet = SetSequence.fromSetWithValues(new HashSet<Integer>(), Sequence.fromIterable(nonTrivialCells).select(new ISelector<EditorCell, Integer>() { public Integer select(EditorCell cell) { return cell.getY(); } })); myPseudoLinesY = SetSequence.fromSet(yCoordinatesSet).sort(new ISelector<Integer, Integer>() { public Integer select(Integer y) { return y; } }, true).toListSequence(); myPseudoLinesToFileLines = ListSequence.fromList(new ArrayList<Integer>()); ListSequence.fromList(myPseudoLinesY).visitAll(new IVisitor<Integer>() { public void visit(Integer t) { ListSequence.fromList(myPseudoLinesToFileLines).addElement(-1); } }); ModelAccess.instance().runReadAction(new Runnable() { public void run() { for (int fileLine = 0; fileLine < ListSequence.fromList(myFileLineToContent).count(); fileLine++) { for (int pseudoLine : getPseudoLinesForContent(ListSequence.fromList(myFileLineToContent).getElement(fileLine))) { int currentFileLine = ListSequence.fromList(myPseudoLinesToFileLines).getElement(pseudoLine); ListSequence.fromList(myPseudoLinesToFileLines).setElement(pseudoLine, getFileLineWithMaxRevision(currentFileLine, fileLine)); } } } }); FontMetrics metrics = FontRegistry.getInstance().getFontMetrics(myFont); for (AnnotationAspectSubcolumn aspectSubcolumn : ListSequence.fromList(myAspectSubcolumns)) { aspectSubcolumn.computeWidth(metrics, myPseudoLinesToFileLines); } mySubcolumnInterval = metrics.stringWidth(" "); calculateCurrentPseudoLinesLater(); } @Override public String getTooltipText(MouseEvent event) { int fileLine = findFileLineByY(event.getY()); if (fileLine == -1) { return null; } else { return myFileAnnotation.getToolTip(fileLine); } } @Nullable @Override public Cursor getCursor(MouseEvent event) { return (findFileLineByY(event.getY()) == -1 ? null : new Cursor(Cursor.HAND_CURSOR)); } @Override public void mousePressed(MouseEvent event) { if (event.getButton() == MouseEvent.BUTTON1 && event.getID() == MouseEvent.MOUSE_RELEASED) { event.consume(); int fileLine = findFileLineByY(event.getY()); ((LineAnnotationAspectAdapter) myFileAnnotation.getAspects()[0]).doAction(fileLine); } else { super.mousePressed(event); } } @Override public void dispose() { myMessageBusConnection.disconnect(); myFileAnnotation.dispose(); final CurrentDifferenceRegistry registry = CurrentDifferenceRegistry.getInstance(getProject()); registry.getCommandQueue().runTask(new Runnable() { public void run() { registry.getCurrentDifference(myModel).removeDifferenceListener(myDifferenceListener); } }); } public void close() { getLeftEditorHighlighter().removeLeftColumn(this); dispose(); } private int findPseudoLineByY(int y) { int pseudoLine = Collections.binarySearch((List) myPseudoLinesY, y); if (pseudoLine < 0) { pseudoLine = -pseudoLine - 2; } if (pseudoLine < 0 || pseudoLine >= ListSequence.fromList(myPseudoLinesToFileLines).count()) { return -1; } return pseudoLine; } private int findFileLineByY(int y) { int pseudoLine = findPseudoLineByY(y); if (pseudoLine == -1) { return -1; } else { if (SetSequence.fromSet(myCurrentPseudoLines).contains(pseudoLine)) { return -1; } return ListSequence.fromList(myPseudoLinesToFileLines).getElement(pseudoLine); } } @Override public JPopupMenu getPopupMenu(MouseEvent event) { List<AnAction> actions = ListSequence.fromList(new ArrayList<AnAction>()); final int fileLine = findFileLineByY(event.getY()); ListSequence.fromList(actions).addElement(new BaseAction("Close Annotations") { @Override protected void doExecute(AnActionEvent e, Map<String, Object> _params) { close(); } }); ListSequence.fromList(actions).addElement(Separator.getInstance()); ListSequence.fromList(actions).addElement(myViewActionGroup); if (fileLine != -1) { ListSequence.fromList(actions).addElement(new AnnotationColumn.ShowDiffFromAnnotationAction(fileLine)); ListSequence.fromList(actions).addElement(new BaseAction("Copy revision number") { @Override protected void doExecute(AnActionEvent e, Map<String, Object> params) { String asString = myFileAnnotation.getLineRevisionNumber(fileLine).asString(); CopyPasteManager.getInstance().setContents(new TextTransferable(asString, asString)); } }); } ListSequence.fromList(actions).addElement(Separator.getInstance()); ListSequence.fromList(actions).addElement(myRevisionRange); ListSequence.fromList(actions).addElement(new ShowAdditionalInfoAction(this)); DefaultActionGroup actionGroup = ActionUtils.groupFromActions(ListSequence.fromList(actions).toGenericArray(AnAction.class)); return ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, actionGroup).getComponent(); } private int getFileLineWithMaxRevision(int a, int b) { if (b == -1) { return a; } if (a == -1) { return b; } VcsRevisionNumber aRevision = myFileAnnotation.getLineRevisionNumber(a); VcsRevisionNumber bRevision = myFileAnnotation.getLineRevisionNumber(b); if (bRevision == null) { return a; } if (aRevision == null) { return b; } int c = aRevision.compareTo(bRevision); if (MapSequence.fromMap(myRevisionNumberToRevision).get(aRevision) != null && MapSequence.fromMap(myRevisionNumberToRevision).get(bRevision) != null) { c = MapSequence.fromMap(myRevisionNumberToRevision).get(aRevision).getRevisionDate().compareTo(MapSequence.fromMap(myRevisionNumberToRevision).get(bRevision).getRevisionDate()); } if (c < 0) { return b; } return a; } public void invalidateLayout() { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { getLeftEditorHighlighter().relayout(false); } }); } public boolean isShowAdditionalInfo() { return myShowAdditionalInfo; } public void setShowAdditionalInfo(boolean showAdditionalInfo) { myShowAdditionalInfo = showAdditionalInfo; invalidateLayout(); } public List<VcsFileRevision> getRevisions() { return myFileAnnotation.getRevisions(); } public Project getProject() { return myVcs.getProject(); } private class MyDifferenceListener extends CurrentDifferenceAdapter { public MyDifferenceListener() { } @Override public void changeUpdateFinished() { calculateCurrentPseudoLinesLater(); } @Override public void changeRemoved(@NotNull ModelChange change) { MapSequence.fromMap(myChangesToLineContents).removeKey(change); } @Override public void changeAdded(@NotNull ModelChange change) { saveChange(change); } } private class ShowDiffFromAnnotationAction extends AnAction { private int myFileLine; public ShowDiffFromAnnotationAction(int fileLine) { super("Show Diff"); myFileLine = fileLine; } @Override public void actionPerformed(AnActionEvent event) { final VcsRevisionNumber revisionNumber = myFileAnnotation.getLineRevisionNumber(myFileLine); if (revisionNumber == null) { return; } final Project project = getProject(); ProgressManager.getInstance().run(new Task.Backgroundable(project, "Loading revision " + revisionNumber.asString() + " contents", true, BackgroundFromStartOption.getInstance()) { @Override public void run(@NotNull ProgressIndicator pi) { CommittedChangesProvider<CommittedChangeList, ChangeBrowserSettings> provider = myVcs.getCommittedChangesProvider(); try { Pair<CommittedChangeList, FilePath> pair = null; if (provider != null) { pair = provider.getOneList(myVirtualFile, revisionNumber); } FilePath targetPath = (check_5mnya_a0a0c0c0a0a0a0e0c54(pair) == null ? new FilePathImpl(myVirtualFile) : check_5mnya_a0a2a2a0a0a0a4a2tb(pair)); CommittedChangeList cl = check_5mnya_a0d0c0a0a0a0e0c54(pair); if (cl == null) { VcsBalloonProblemNotifier.showOverChangesView(project, "Cannot load data for showing diff", MessageType.ERROR); return; } List<Change> changes = Sequence.fromIterable(((Iterable<Change>) cl.getChanges())).sort(new ISelector<Change, String>() { public String select(Change c) { return ChangesUtil.getFilePath(c).getName().toLowerCase(); } }, true).toListSequence(); final File ioFile = targetPath.getIOFile(); Change change = ListSequence.fromList(changes).findFirst(new IWhereFilter<Change>() { public boolean accept(Change c) { return c.getAfterRevision() != null && c.getAfterRevision().getFile().getIOFile().equals(ioFile); } }); if (change != null) { final String name = ioFile.getName(); change = ListSequence.fromList(changes).findFirst(new IWhereFilter<Change>() { public boolean accept(Change c) { return c.getAfterRevision() != null && c.getAfterRevision().getFile().getName().equals(name); } }); ContentRevision before = change.getBeforeRevision(); ContentRevision after = change.getAfterRevision(); if (pi.isCanceled()) { return; } pi.setText("Loading model after change"); assert after != null; FileType[] filetypes = {(before == null ? null : before.getFile().getFileType()), after.getFile().getFileType()}; boolean isPerRoot = MPSFileTypeFactory.MPS_ROOT_FILE_TYPE.equals(filetypes[1]) || MPSFileTypeFactory.MPS_HEADER_FILE_TYPE.equals(filetypes[1]); final SModel afterModel = VCSPersistenceUtil.loadModel(after.getContent().getBytes(FileUtil.DEFAULT_CHARSET), (isPerRoot ? MPSExtentions.MODEL : filetypes[1].getDefaultExtension())); if (pi.isCanceled()) { return; } pi.setText("Loading model before change"); final Wrappers._T<SModel> beforeModel = new Wrappers._T<SModel>(); if (before == null) { beforeModel.value = new MergeTemporaryModel(myModel.getReference(), true); } else { beforeModel.value = VCSPersistenceUtil.loadModel(before.getContent().getBytes(FileUtil.DEFAULT_CHARSET), (isPerRoot ? MPSExtentions.MODEL : filetypes[0].getDefaultExtension())); } final Wrappers._T<SNodeId> rootId = new Wrappers._T<SNodeId>(); final Wrappers._T<String> rootName = new Wrappers._T<String>(); ProjectHelper.fromIdeaProject(project).getModelAccess().runReadAction(new _Adapters._return_P0_E0_to_Runnable_adapter(new _FunctionTypes._return_P0_E0<String>() { public String invoke() { SNodeId nodeId = check_5mnya_a0a0a0a22a8a2a0a0a0a4a2tb(ListSequence.fromList(myFileLineToContent).getElement(myFileLine)); SNode node = afterModel.getNode(nodeId); if ((node == null)) { node = beforeModel.value.getNode(nodeId); } SNode root = SNodeOperations.getContainingRoot(node); rootId.value = check_5mnya_a0e0a0a22a8a2a0a0a0a4a2tb(root); return rootName.value = (((root == null) ? "" : root.getName())) + " (" + SModelOperations.getModelName(afterModel) + ")"; } })); List<DiffContent> contents = ListSequence.fromListAndArray(new ArrayList<DiffContent>(), DiffContentFactory.getInstance().create((before == null ? "" : before.getContent()), filetypes[0]), DiffContentFactory.getInstance().create(after.getContent(), filetypes[1])); List<String> titles = ListSequence.fromListAndArray(new ArrayList<String>(), (before == null ? "<no revision>" : before.getRevisionNumber().asString()), after.getRevisionNumber().asString()); final DiffRequest request = new SimpleDiffRequest(rootName.value, contents, titles); // put hint to show only one root request.putUserData(ModelDiffViewer.DIFF_SHOW_ROOTID, rootId.value); ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { DiffManager.getInstance().showDiff(project, request); } }); } } catch (final VcsException ve) { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { VcsBalloonProblemNotifier.showOverChangesView(project, "Cannot show diff: " + ve.getMessage(), MessageType.ERROR); } }); } } }); } } private class MyEditorComponentCreateListener implements EditorComponentCreateListener { public MyEditorComponentCreateListener() { } @Override public void editorComponentCreated(@NotNull EditorComponent ec) { } @Override public void editorComponentDisposed(@NotNull EditorComponent ec) { if (ec == getEditorComponent()) { close(); } } } private static SNodeId check_5mnya_a0b0k0v(LineContent checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getNodeId(); } return null; } private static List<ModelChange> check_5mnya_a0a0a0a1a0a0v0v(ChangeSet checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getModelChanges(); } return null; } private static FilePath check_5mnya_a0a2a2a0a0a0a4a2tb(Pair<CommittedChangeList, FilePath> checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getSecond(); } return null; } private static FilePath check_5mnya_a0a0c0c0a0a0a0e0c54(Pair<CommittedChangeList, FilePath> checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getSecond(); } return null; } private static CommittedChangeList check_5mnya_a0d0c0a0a0a0e0c54(Pair<CommittedChangeList, FilePath> checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getFirst(); } return null; } private static SNodeId check_5mnya_a0a0a0a22a8a2a0a0a0a4a2tb(LineContent checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getNodeId(); } return null; } private static SNodeId check_5mnya_a0e0a0a22a8a2a0a0a0a4a2tb(SNode checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getNodeId(); } return null; } }