package jetbrains.mps.debugger.core;
/*Generated by MPS */
import com.intellij.openapi.fileEditor.FileEditorManager;
import java.util.Map;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import com.intellij.openapi.project.Project;
import jetbrains.mps.nodeEditor.highlighter.EditorComponentCreateListener;
import com.intellij.util.messages.MessageBusConnection;
import jetbrains.mps.smodel.CommandListenerAdapter;
import jetbrains.mps.ide.project.ProjectHelper;
import java.util.Collection;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import jetbrains.mps.internal.collections.runtime.Sequence;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.nodeEditor.EditorComponent;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.ide.editor.util.EditorComponentUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SNodeReference;
import jetbrains.mps.openapi.navigation.NavigationSupport;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import jetbrains.mps.nodeEditor.AdditionalPainter;
public abstract class CurrentLinePositionComponentEx<S> {
private FileEditorManager myFileEditorManager;
private final Map<S, CurrentLinePainter> mySessionToContextPainterMap = MapSequence.fromMap(new HashMap<S, CurrentLinePainter>());
protected final Project myProject;
private final EditorComponentCreateListener myEditorComponentCreationHandler = new CurrentLinePositionComponentEx.MyEditorComponentCreateListener();
private MessageBusConnection myMessageBusConnection;
private final CommandListenerAdapter myCommandListener = new CurrentLinePositionComponentEx.MyRepositoryListener();
public CurrentLinePositionComponentEx(Project project, FileEditorManager fileEditorManager) {
myProject = project;
myFileEditorManager = fileEditorManager;
}
protected void init() {
myMessageBusConnection = myProject.getMessageBus().connect();
myMessageBusConnection.subscribe(EditorComponentCreateListener.EDITOR_COMPONENT_CREATION, myEditorComponentCreationHandler);
ProjectHelper.getModelAccess(myProject).addCommandListener(myCommandListener);
}
protected void dispose() {
ProjectHelper.getModelAccess(myProject).removeCommandListener(myCommandListener);
myMessageBusConnection.disconnect();
}
protected abstract S getCurrentSession();
protected abstract Collection<? extends S> getAllSessions();
private List<CurrentLinePainter> getAllPainters() {
synchronized (mySessionToContextPainterMap) {
List<CurrentLinePainter> painters = ListSequence.fromList(new ArrayList<CurrentLinePainter>());
ListSequence.fromList(painters).addSequence(Sequence.fromIterable(MapSequence.fromMap(mySessionToContextPainterMap).values()));
return painters;
}
}
/**
* Expects EDT and model read access
*/
/*package*/ void attach(@NotNull final CurrentLinePainter painter, @NotNull final EditorComponent editorComponent) {
ApplicationManager.getApplication().assertIsDispatchThread();
final SRepository repo = editorComponent.getEditorContext().getRepository();
SNode node = (painter.getSNode() == null ? null : painter.getSNode().resolve(repo));
if (node != null && EditorComponentUtil.isNodeShownInTheComponent(editorComponent, node)) {
editorComponent.addAdditionalPainter(painter);
editorComponent.repaintExternalComponent();
}
}
/**
* Expects EDT and model read access
*/
/*package*/ void detach(@NotNull final CurrentLinePainter painter, @NotNull final EditorComponent editorComponent) {
ApplicationManager.getApplication().assertIsDispatchThread();
final SRepository repo = editorComponent.getEditorContext().getRepository();
SNode node = (painter.getSNode() == null ? null : painter.getSNode().resolve(repo));
if (node == null || EditorComponentUtil.isNodeShownInTheComponent(editorComponent, node)) {
editorComponent.removeAdditionalPainter(painter);
editorComponent.repaintExternalComponent();
}
}
/**
*
* @return Runnable to execute with model write access and inside EDT
*/
@Nullable
/*package*/ Runnable attachPainterRunnable(final S debugSession, final boolean focus) {
SNodeReference node = getNode(debugSession);
final CurrentLinePainter newPainter = (node == null ? null : new CurrentLinePainter(node));
if (newPainter != null) {
final boolean visible = getCurrentSession() == null || getCurrentSession() == debugSession;
newPainter.setVisible(visible);
// we lock here, since we do not want to acquire read lock inside while having mySessionToContextPainterMap
synchronized (mySessionToContextPainterMap) {
MapSequence.fromMap(mySessionToContextPainterMap).put(debugSession, newPainter);
return new Runnable() {
@Override
public void run() {
final jetbrains.mps.project.Project mpsProject = ProjectHelper.fromIdeaProject(myProject);
mpsProject.getModelAccess().checkWriteAccess();
SNode node = (newPainter.getSNode() == null ? null : newPainter.getSNode().resolve(mpsProject.getRepository()));
if (node != null) {
if (visible && focus) {
jetbrains.mps.openapi.editor.EditorComponent currentEditorComponent = NavigationSupport.getInstance().openNode(mpsProject, node, true, false).getCurrentEditorComponent();
currentEditorComponent = EditorComponentUtil.scrollToNode(node, currentEditorComponent);
if (currentEditorComponent instanceof EditorComponent) {
attach(newPainter, (EditorComponent) currentEditorComponent);
}
}
List<EditorComponent> components = EditorComponentUtil.findComponentForNode(node, myFileEditorManager);
for (EditorComponent component : ListSequence.fromList(components)) {
attach(newPainter, component);
}
}
}
};
}
}
return null;
}
@Nullable
protected abstract SNodeReference getNode(S session);
/**
*
* @return Runnable to execute with model read and inside EDT
*/
@Nullable
/*package*/ Runnable detachPainterRunnable(S session) {
final CurrentLinePainter painter;
synchronized (mySessionToContextPainterMap) {
painter = MapSequence.fromMap(mySessionToContextPainterMap).get(session);
MapSequence.fromMap(mySessionToContextPainterMap).removeKey(session);
}
if (painter != null) {
return new Runnable() {
@Override
public void run() {
for (EditorComponent editor : EditorComponentUtil.getAllEditorComponents(myFileEditorManager, true)) {
detach(painter, editor);
}
}
};
}
return null;
}
protected void detachPainter(S session) {
Runnable detachPainterRunnable = detachPainterRunnable(session);
if (detachPainterRunnable == null) {
return;
}
ProjectHelper.getModelAccess(myProject).runReadInEDT(detachPainterRunnable);
}
protected void reAttachPainter(S session, boolean focus) {
final Runnable detachSession = detachPainterRunnable(session);
final Runnable attachSession = attachPainterRunnable(session, focus);
if (detachSession != null || attachSession != null) {
ProjectHelper.getModelAccess(myProject).runWriteInEDT(new Runnable() {
@Override
public void run() {
if (detachSession != null) {
detachSession.run();
}
if (attachSession != null) {
attachSession.run();
}
}
});
}
}
protected void currentSessionChanged(S newSession) {
synchronized (mySessionToContextPainterMap) {
for (S session : SetSequence.fromSet(MapSequence.fromMap(mySessionToContextPainterMap).keySet())) {
CurrentLinePainter painter = MapSequence.fromMap(mySessionToContextPainterMap).get(session);
if (painter != null) {
painter.setVisible(newSession != null && newSession == session);
}
}
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
for (EditorComponent editorComponent : EditorComponentUtil.getAllEditorComponents(myFileEditorManager, true)) {
editorComponent.repaintExternalComponent();
}
}
});
}
private class MyEditorComponentCreateListener implements EditorComponentCreateListener {
private MyEditorComponentCreateListener() {
}
@Override
public void editorComponentCreated(@NotNull EditorComponent editorComponent) {
for (CurrentLinePainter p : ListSequence.fromList(getAllPainters())) {
attach(p, editorComponent);
}
}
@Override
public void editorComponentDisposed(@NotNull EditorComponent editorComponent) {
List<AdditionalPainter> additionalPainters = editorComponent.getAdditionalPainters();
for (AdditionalPainter painter : ListSequence.fromList(additionalPainters)) {
if (painter instanceof CurrentLinePainter) {
editorComponent.removeAdditionalPainter(painter);
}
}
}
}
/**
* It used to be SRepositoryListener with commandFinished that has NOT been invoked
* for couple of releases with no apparent defect (commandFinished of SRepositoryListener was deprecated and not invoked)
* so I wonder if there's a reason to keep it?
*/
private class MyRepositoryListener extends CommandListenerAdapter {
@Override
public void commandFinished() {
for (S session : getAllSessions()) {
reAttachPainter(session, false);
}
}
}
}