blob: 6ef4ca6a1252326c16099387d1c222a53aa5fb99 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.xdebugger.impl;
import com.intellij.AppTopics;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.Executor;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.runners.RunContentBuilder;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.execution.ui.RunContentManager;
import com.intellij.execution.ui.RunContentWithExecutorListener;
import com.intellij.ide.DataManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.SmartList;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.xmlb.annotations.Property;
import com.intellij.xdebugger.*;
import com.intellij.xdebugger.breakpoints.XBreakpoint;
import com.intellij.xdebugger.breakpoints.XBreakpointAdapter;
import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
import com.intellij.xdebugger.impl.breakpoints.XBreakpointBase;
import com.intellij.xdebugger.impl.breakpoints.XBreakpointManagerImpl;
import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager;
import com.intellij.xdebugger.impl.ui.ExecutionPointHighlighter;
import com.intellij.xdebugger.impl.ui.XDebugSessionData;
import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
/**
* @author nik
*/
@State(
name = XDebuggerManagerImpl.COMPONENT_NAME,
storages = {@Storage(
file = StoragePathMacros.WORKSPACE_FILE)})
public class XDebuggerManagerImpl extends XDebuggerManager
implements NamedComponent, PersistentStateComponent<XDebuggerManagerImpl.XDebuggerState> {
@NonNls public static final String COMPONENT_NAME = "XDebuggerManager";
private final Project myProject;
private final XBreakpointManagerImpl myBreakpointManager;
private final XDebuggerWatchesManager myWatchesManager;
private final Map<ProcessHandler, XDebugSessionImpl> mySessions;
private final ExecutionPointHighlighter myExecutionPointHighlighter;
private XDebugSessionImpl myActiveSession;
public XDebuggerManagerImpl(final Project project, final StartupManager startupManager, MessageBus messageBus) {
myProject = project;
myBreakpointManager = new XBreakpointManagerImpl(project, this, startupManager);
myWatchesManager = new XDebuggerWatchesManager();
mySessions = new LinkedHashMap<ProcessHandler, XDebugSessionImpl>();
myExecutionPointHighlighter = new ExecutionPointHighlighter(project);
MessageBusConnection messageBusConnection = messageBus.connect();
messageBusConnection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() {
@Override
public void fileContentLoaded(@NotNull VirtualFile file, @NotNull Document document) {
updateExecutionPoint(file);
}
@Override
public void fileContentReloaded(@NotNull VirtualFile file, @NotNull Document document) {
updateExecutionPoint(file);
}
private void updateExecutionPoint(@NotNull VirtualFile file) {
if (file.equals(myExecutionPointHighlighter.getCurrentFile())) {
myExecutionPointHighlighter.update();
}
}
});
myBreakpointManager.addBreakpointListener(new XBreakpointAdapter<XBreakpoint<?>>() {
@Override
public void breakpointChanged(@NotNull XBreakpoint<?> breakpoint) {
if (!(breakpoint instanceof XLineBreakpoint)) {
final XDebugSessionImpl session = getCurrentSession();
if (session != null && breakpoint.equals(session.getActiveNonLineBreakpoint())) {
final XBreakpointBase breakpointBase = (XBreakpointBase)breakpoint;
breakpointBase.clearIcon();
myExecutionPointHighlighter.updateGutterIcon(breakpointBase.createGutterIconRenderer());
}
}
}
});
messageBusConnection.subscribe(RunContentManager.TOPIC, new RunContentWithExecutorListener() {
@Override
public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
if (descriptor != null && executor.equals(DefaultDebugExecutor.getDebugExecutorInstance())) {
XDebugSessionImpl session = mySessions.get(descriptor.getProcessHandler());
if (session != null) {
session.activateSession();
}
else {
setActiveSession(null, null, false, null);
}
}
}
@Override
public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
if (descriptor != null && executor.equals(DefaultDebugExecutor.getDebugExecutorInstance())) {
mySessions.remove(descriptor.getProcessHandler());
}
}
});
}
@Override
@NotNull
public XBreakpointManagerImpl getBreakpointManager() {
return myBreakpointManager;
}
public XDebuggerWatchesManager getWatchesManager() {
return myWatchesManager;
}
public Project getProject() {
return myProject;
}
@NotNull
@Override
public String getComponentName() {
return COMPONENT_NAME;
}
@Override
@NotNull
public XDebugSession startSession(@NotNull ProgramRunner runner,
@NotNull ExecutionEnvironment environment,
@Nullable RunContentDescriptor contentToReuse,
@NotNull XDebugProcessStarter processStarter) throws ExecutionException {
return startSession(contentToReuse, processStarter, new XDebugSessionImpl(RunContentBuilder.fix(environment, runner), this));
}
@Override
@NotNull
public XDebugSession startSession(@NotNull ExecutionEnvironment environment, @NotNull XDebugProcessStarter processStarter) throws ExecutionException {
return startSession(environment.getContentToReuse(), processStarter, new XDebugSessionImpl(environment, this));
}
@Override
@NotNull
public XDebugSession startSessionAndShowTab(@NotNull String sessionName, @Nullable RunContentDescriptor contentToReuse,
@NotNull XDebugProcessStarter starter) throws ExecutionException {
return startSessionAndShowTab(sessionName, contentToReuse, false, starter);
}
@NotNull
@Override
public XDebugSession startSessionAndShowTab(@NotNull String sessionName, @Nullable RunContentDescriptor contentToReuse,
boolean showToolWindowOnSuspendOnly,
@NotNull XDebugProcessStarter starter) throws ExecutionException {
return startSessionAndShowTab(sessionName, null, contentToReuse, showToolWindowOnSuspendOnly, starter);
}
@NotNull
@Override
public XDebugSession startSessionAndShowTab(@NotNull String sessionName,
Icon icon,
@Nullable RunContentDescriptor contentToReuse,
boolean showToolWindowOnSuspendOnly,
@NotNull XDebugProcessStarter starter) throws ExecutionException {
XDebugSessionImpl session = startSession(contentToReuse, starter, new XDebugSessionImpl(null, this, sessionName,
icon, showToolWindowOnSuspendOnly));
if (!showToolWindowOnSuspendOnly) {
session.showSessionTab();
}
ProcessHandler handler = session.getDebugProcess().getProcessHandler();
handler.startNotify();
return session;
}
private XDebugSessionImpl startSession(@Nullable RunContentDescriptor contentToReuse,
@NotNull XDebugProcessStarter processStarter,
@NotNull XDebugSessionImpl session) throws ExecutionException {
XDebugProcess process = processStarter.start(session);
myProject.getMessageBus().syncPublisher(TOPIC).processStarted(process);
XDebugSessionData oldSessionData = null;
if (contentToReuse != null) {
JComponent component = contentToReuse.getComponent();
if (component != null) {
oldSessionData = XDebugSessionData.DATA_KEY.getData(DataManager.getInstance().getDataContext(component));
}
}
if (oldSessionData == null) {
oldSessionData = new XDebugSessionData(session.getWatchExpressions());
}
// Perform custom configuration of session data for XDebugProcessConfiguratorStarter classes
if (processStarter instanceof XDebugProcessConfiguratorStarter) {
session.activateSession();
((XDebugProcessConfiguratorStarter)processStarter).configure(oldSessionData);
}
session.init(process, oldSessionData, contentToReuse);
mySessions.put(session.getDebugProcess().getProcessHandler(), session);
return session;
}
public void removeSession(@NotNull final XDebugSessionImpl session) {
XDebugSessionTab sessionTab = session.getSessionTab();
mySessions.remove(session.getDebugProcess().getProcessHandler());
if (sessionTab != null) {
RunContentDescriptor descriptor = sessionTab.getRunContentDescriptor();
if (descriptor != null) {
// in test-mode RunContentWithExecutorListener.contentRemoved events are not sent (see RunContentManagerImpl.showRunContent)
// so we make sure the mySessions and mySessionData are cleared correctly when session is disposed
Disposer.register(descriptor, new Disposable() {
@Override
public void dispose() {
mySessions.remove(session.getDebugProcess().getProcessHandler());
}
});
}
if (!myProject.isDisposed() && !ApplicationManager.getApplication().isUnitTestMode() && XDebuggerSettingsManager.getInstanceImpl().getGeneralSettings().isHideDebuggerOnProcessTermination()) {
ExecutionManager.getInstance(myProject).getContentManager().hideRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), descriptor);
}
}
if (myActiveSession == session) {
myActiveSession = null;
onActiveSessionChanged();
}
}
public void setActiveSession(@Nullable XDebugSessionImpl session, @Nullable XSourcePosition position, boolean useSelection,
final @Nullable GutterIconRenderer gutterIconRenderer) {
boolean sessionChanged = myActiveSession != session;
myActiveSession = session;
updateExecutionPoint(position, useSelection, gutterIconRenderer);
if (sessionChanged) {
onActiveSessionChanged();
}
}
public void updateExecutionPoint(XSourcePosition position, boolean useSelection, @Nullable GutterIconRenderer gutterIconRenderer) {
if (position != null) {
myExecutionPointHighlighter.show(position, useSelection, gutterIconRenderer);
}
else {
myExecutionPointHighlighter.hide();
}
}
private void onActiveSessionChanged() {
myBreakpointManager.getLineBreakpointManager().queueAllBreakpointsUpdate();
}
@Override
@NotNull
public XDebugSession[] getDebugSessions() {
final Collection<XDebugSessionImpl> sessions = mySessions.values();
return sessions.toArray(new XDebugSessionImpl[sessions.size()]);
}
@Override
@Nullable
public XDebugSession getDebugSession(@NotNull ExecutionConsole executionConsole) {
for (final XDebugSessionImpl debuggerSession : mySessions.values()) {
XDebugSessionTab sessionTab = debuggerSession.getSessionTab();
if (sessionTab != null) {
RunContentDescriptor contentDescriptor = sessionTab.getRunContentDescriptor();
if (contentDescriptor != null && executionConsole == contentDescriptor.getExecutionConsole()) {
return debuggerSession;
}
}
}
return null;
}
@NotNull
@Override
public <T extends XDebugProcess> List<? extends T> getDebugProcesses(Class<T> processClass) {
List<T> list = null;
for (XDebugSessionImpl session : mySessions.values()) {
final XDebugProcess process = session.getDebugProcess();
if (processClass.isInstance(process)) {
if (list == null) {
list = new SmartList<T>();
}
list.add(processClass.cast(process));
}
}
return list == null ? Collections.<T>emptyList() : list;
}
@Override
@Nullable
public XDebugSessionImpl getCurrentSession() {
return myActiveSession;
}
@Override
public XDebuggerState getState() {
return new XDebuggerState(myBreakpointManager.getState(), myWatchesManager.getState());
}
@Override
public void loadState(final XDebuggerState state) {
myBreakpointManager.loadState(state.myBreakpointManagerState);
myWatchesManager.loadState(state.myWatchesManagerState);
}
public void showExecutionPosition() {
myExecutionPointHighlighter.navigateTo();
}
@SuppressWarnings("UnusedDeclaration")
public static class XDebuggerState {
private XBreakpointManagerImpl.BreakpointManagerState myBreakpointManagerState;
private XDebuggerWatchesManager.WatchesManagerState myWatchesManagerState;
public XDebuggerState() {
}
public XDebuggerState(final XBreakpointManagerImpl.BreakpointManagerState breakpointManagerState, XDebuggerWatchesManager.WatchesManagerState watchesManagerState) {
myBreakpointManagerState = breakpointManagerState;
myWatchesManagerState = watchesManagerState;
}
@Property(surroundWithTag = false)
public XBreakpointManagerImpl.BreakpointManagerState getBreakpointManagerState() {
return myBreakpointManagerState;
}
public void setBreakpointManagerState(final XBreakpointManagerImpl.BreakpointManagerState breakpointManagerState) {
myBreakpointManagerState = breakpointManagerState;
}
@Property(surroundWithTag = false)
public XDebuggerWatchesManager.WatchesManagerState getWatchesManagerState() {
return myWatchesManagerState;
}
public void setWatchesManagerState(XDebuggerWatchesManager.WatchesManagerState watchesManagerState) {
myWatchesManagerState = watchesManagerState;
}
}
}