IDEA-139797 Default Theme doesn't allow to apply font size to evaluate expression...
[idea/community.git] / platform / xdebugger-impl / src / com / intellij / xdebugger / impl / XDebuggerManagerImpl.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.xdebugger.impl;
17
18 import com.intellij.AppTopics;
19 import com.intellij.execution.ExecutionException;
20 import com.intellij.execution.ExecutionManager;
21 import com.intellij.execution.Executor;
22 import com.intellij.execution.executors.DefaultDebugExecutor;
23 import com.intellij.execution.process.ProcessHandler;
24 import com.intellij.execution.runners.ExecutionEnvironment;
25 import com.intellij.execution.ui.ExecutionConsole;
26 import com.intellij.execution.ui.RunContentDescriptor;
27 import com.intellij.execution.ui.RunContentManager;
28 import com.intellij.execution.ui.RunContentWithExecutorListener;
29 import com.intellij.ide.DataManager;
30 import com.intellij.openapi.Disposable;
31 import com.intellij.openapi.application.ApplicationManager;
32 import com.intellij.openapi.components.*;
33 import com.intellij.openapi.editor.Document;
34 import com.intellij.openapi.editor.markup.GutterIconRenderer;
35 import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
36 import com.intellij.openapi.fileEditor.FileEditorManager;
37 import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
38 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
39 import com.intellij.openapi.project.Project;
40 import com.intellij.openapi.startup.StartupManager;
41 import com.intellij.openapi.util.Disposer;
42 import com.intellij.openapi.vfs.VirtualFile;
43 import com.intellij.util.SmartList;
44 import com.intellij.util.messages.MessageBus;
45 import com.intellij.util.messages.MessageBusConnection;
46 import com.intellij.util.xmlb.annotations.Property;
47 import com.intellij.xdebugger.*;
48 import com.intellij.xdebugger.breakpoints.XBreakpoint;
49 import com.intellij.xdebugger.breakpoints.XBreakpointAdapter;
50 import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
51 import com.intellij.xdebugger.impl.breakpoints.XBreakpointBase;
52 import com.intellij.xdebugger.impl.breakpoints.XBreakpointManagerImpl;
53 import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager;
54 import com.intellij.xdebugger.impl.ui.ExecutionPointHighlighter;
55 import com.intellij.xdebugger.impl.ui.XDebugSessionData;
56 import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
57 import org.jetbrains.annotations.NonNls;
58 import org.jetbrains.annotations.NotNull;
59 import org.jetbrains.annotations.Nullable;
60
61 import javax.swing.*;
62 import java.util.*;
63 import java.util.concurrent.atomic.AtomicReference;
64
65 /**
66  * @author nik
67  */
68 @State(
69   name = XDebuggerManagerImpl.COMPONENT_NAME,
70   storages = {@Storage(
71     file = StoragePathMacros.WORKSPACE_FILE)})
72 public class XDebuggerManagerImpl extends XDebuggerManager
73   implements NamedComponent, PersistentStateComponent<XDebuggerManagerImpl.XDebuggerState> {
74   @NonNls public static final String COMPONENT_NAME = "XDebuggerManager";
75   private final Project myProject;
76   private final XBreakpointManagerImpl myBreakpointManager;
77   private final XDebuggerWatchesManager myWatchesManager;
78   private final Map<ProcessHandler, XDebugSessionImpl> mySessions;
79   private final ExecutionPointHighlighter myExecutionPointHighlighter;
80   private final AtomicReference<XDebugSessionImpl> myActiveSession = new AtomicReference<XDebugSessionImpl>();
81
82   public XDebuggerManagerImpl(final Project project, final StartupManager startupManager, MessageBus messageBus) {
83     myProject = project;
84     myBreakpointManager = new XBreakpointManagerImpl(project, this, startupManager);
85     myWatchesManager = new XDebuggerWatchesManager();
86     mySessions = new LinkedHashMap<ProcessHandler, XDebugSessionImpl>();
87     myExecutionPointHighlighter = new ExecutionPointHighlighter(project);
88
89     MessageBusConnection messageBusConnection = messageBus.connect();
90     messageBusConnection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() {
91       @Override
92       public void fileContentLoaded(@NotNull VirtualFile file, @NotNull Document document) {
93         updateExecutionPoint(file, true);
94       }
95
96       @Override
97       public void fileContentReloaded(@NotNull VirtualFile file, @NotNull Document document) {
98         updateExecutionPoint(file, true);
99       }
100     });
101     messageBusConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() {
102       @Override
103       public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
104         updateExecutionPoint(file, false);
105       }
106     });
107     myBreakpointManager.addBreakpointListener(new XBreakpointAdapter<XBreakpoint<?>>() {
108       @Override
109       public void breakpointChanged(@NotNull XBreakpoint<?> breakpoint) {
110         if (!(breakpoint instanceof XLineBreakpoint)) {
111           final XDebugSessionImpl session = getCurrentSession();
112           if (session != null && breakpoint.equals(session.getActiveNonLineBreakpoint())) {
113             final XBreakpointBase breakpointBase = (XBreakpointBase)breakpoint;
114             breakpointBase.clearIcon();
115             myExecutionPointHighlighter.updateGutterIcon(breakpointBase.createGutterIconRenderer());
116           }
117         }
118       }
119     });
120
121     messageBusConnection.subscribe(RunContentManager.TOPIC, new RunContentWithExecutorListener() {
122       @Override
123       public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
124         if (descriptor != null && executor.equals(DefaultDebugExecutor.getDebugExecutorInstance())) {
125           XDebugSessionImpl session = mySessions.get(descriptor.getProcessHandler());
126           if (session != null) {
127             session.activateSession();
128           }
129           else {
130             setCurrentSession(null);
131           }
132         }
133       }
134
135       @Override
136       public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
137         if (descriptor != null && executor.equals(DefaultDebugExecutor.getDebugExecutorInstance())) {
138           mySessions.remove(descriptor.getProcessHandler());
139         }
140       }
141     });
142   }
143
144   private void updateExecutionPoint(@NotNull VirtualFile file, boolean navigate) {
145     if (file.equals(myExecutionPointHighlighter.getCurrentFile())) {
146       myExecutionPointHighlighter.update(navigate);
147     }
148   }
149
150   @Override
151   @NotNull
152   public XBreakpointManagerImpl getBreakpointManager() {
153     return myBreakpointManager;
154   }
155
156   public XDebuggerWatchesManager getWatchesManager() {
157     return myWatchesManager;
158   }
159
160   public Project getProject() {
161     return myProject;
162   }
163
164   @NotNull
165   @Override
166   public String getComponentName() {
167     return COMPONENT_NAME;
168   }
169
170   @Override
171   @NotNull
172   public XDebugSession startSession(@NotNull ExecutionEnvironment environment, @NotNull XDebugProcessStarter processStarter) throws ExecutionException {
173     return startSession(environment.getContentToReuse(), processStarter, new XDebugSessionImpl(environment, this));
174   }
175
176   @Override
177   @NotNull
178   public XDebugSession startSessionAndShowTab(@NotNull String sessionName, @Nullable RunContentDescriptor contentToReuse,
179                                               @NotNull XDebugProcessStarter starter) throws ExecutionException {
180     return startSessionAndShowTab(sessionName, contentToReuse, false, starter);
181   }
182
183   @NotNull
184   @Override
185   public XDebugSession startSessionAndShowTab(@NotNull String sessionName, @Nullable RunContentDescriptor contentToReuse,
186                                               boolean showToolWindowOnSuspendOnly,
187                                               @NotNull XDebugProcessStarter starter) throws ExecutionException {
188     return startSessionAndShowTab(sessionName, null, contentToReuse, showToolWindowOnSuspendOnly, starter);
189   }
190
191   @NotNull
192   @Override
193   public XDebugSession startSessionAndShowTab(@NotNull String sessionName,
194                                               Icon icon,
195                                               @Nullable RunContentDescriptor contentToReuse,
196                                               boolean showToolWindowOnSuspendOnly,
197                                               @NotNull XDebugProcessStarter starter) throws ExecutionException {
198     XDebugSessionImpl session = startSession(contentToReuse, starter, new XDebugSessionImpl(null, this, sessionName,
199                                                                                             icon, showToolWindowOnSuspendOnly));
200     if (!showToolWindowOnSuspendOnly) {
201       session.showSessionTab();
202     }
203     ProcessHandler handler = session.getDebugProcess().getProcessHandler();
204     handler.startNotify();
205     return session;
206   }
207
208   private XDebugSessionImpl startSession(@Nullable RunContentDescriptor contentToReuse,
209                                          @NotNull XDebugProcessStarter processStarter,
210                                          @NotNull XDebugSessionImpl session) throws ExecutionException {
211     XDebugProcess process = processStarter.start(session);
212     myProject.getMessageBus().syncPublisher(TOPIC).processStarted(process);
213
214     XDebugSessionData oldSessionData = null;
215     if (contentToReuse != null) {
216       JComponent component = contentToReuse.getComponent();
217       if (component != null) {
218         oldSessionData = XDebugSessionData.DATA_KEY.getData(DataManager.getInstance().getDataContext(component));
219       }
220     }
221     if (oldSessionData == null) {
222       oldSessionData = new XDebugSessionData(session.getWatchExpressions());
223     }
224
225     // Perform custom configuration of session data for XDebugProcessConfiguratorStarter classes
226     if (processStarter instanceof XDebugProcessConfiguratorStarter) {
227       session.activateSession();
228       ((XDebugProcessConfiguratorStarter)processStarter).configure(oldSessionData);
229     }
230
231     session.init(process, oldSessionData, contentToReuse);
232
233     mySessions.put(session.getDebugProcess().getProcessHandler(), session);
234
235     return session;
236   }
237
238   public void removeSession(@NotNull final XDebugSessionImpl session) {
239     XDebugSessionTab sessionTab = session.getSessionTab();
240     mySessions.remove(session.getDebugProcess().getProcessHandler());
241     if (sessionTab != null) {
242       RunContentDescriptor descriptor = sessionTab.getRunContentDescriptor();
243       if (descriptor != null) {
244         // in test-mode RunContentWithExecutorListener.contentRemoved events are not sent (see RunContentManagerImpl.showRunContent)
245         // so we make sure the mySessions and mySessionData are cleared correctly when session is disposed
246         Disposer.register(descriptor, new Disposable() {
247           @Override
248           public void dispose() {
249             mySessions.remove(session.getDebugProcess().getProcessHandler());
250           }
251         });
252       }
253
254       if (!myProject.isDisposed() && !ApplicationManager.getApplication().isUnitTestMode() && XDebuggerSettingsManager.getInstanceImpl().getGeneralSettings().isHideDebuggerOnProcessTermination()) {
255         ExecutionManager.getInstance(myProject).getContentManager().hideRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), descriptor);
256       }
257     }
258     if (myActiveSession.compareAndSet(session, null)) {
259       onActiveSessionChanged();
260     }
261   }
262
263   void updateExecutionPoint(@Nullable XSourcePosition position, boolean useSelection, @Nullable GutterIconRenderer gutterIconRenderer) {
264     if (position != null) {
265       myExecutionPointHighlighter.show(position, useSelection, gutterIconRenderer);
266     }
267     else {
268       myExecutionPointHighlighter.hide();
269     }
270   }
271
272   private void onActiveSessionChanged() {
273     myBreakpointManager.getLineBreakpointManager().queueAllBreakpointsUpdate();
274   }
275
276   @Override
277   @NotNull
278   public XDebugSession[] getDebugSessions() {
279     final Collection<XDebugSessionImpl> sessions = mySessions.values();
280     return sessions.toArray(new XDebugSessionImpl[sessions.size()]);
281   }
282
283   @Override
284   @Nullable
285   public XDebugSession getDebugSession(@NotNull ExecutionConsole executionConsole) {
286     for (final XDebugSessionImpl debuggerSession : mySessions.values()) {
287       XDebugSessionTab sessionTab = debuggerSession.getSessionTab();
288       if (sessionTab != null) {
289         RunContentDescriptor contentDescriptor = sessionTab.getRunContentDescriptor();
290         if (contentDescriptor != null && executionConsole == contentDescriptor.getExecutionConsole()) {
291           return debuggerSession;
292         }
293       }
294     }
295     return null;
296   }
297
298   @NotNull
299   @Override
300   public <T extends XDebugProcess> List<? extends T> getDebugProcesses(Class<T> processClass) {
301     List<T> list = null;
302     for (XDebugSessionImpl session : mySessions.values()) {
303       final XDebugProcess process = session.getDebugProcess();
304       if (processClass.isInstance(process)) {
305         if (list == null) {
306           list = new SmartList<T>();
307         }
308         list.add(processClass.cast(process));
309       }
310     }
311     return list == null ? Collections.<T>emptyList() : list;
312   }
313
314   @Override
315   @Nullable
316   public XDebugSessionImpl getCurrentSession() {
317     return myActiveSession.get();
318   }
319
320   void setCurrentSession(@Nullable XDebugSessionImpl session) {
321     boolean sessionChanged = myActiveSession.getAndSet(session) != session;
322     if (sessionChanged) {
323       if (session != null) {
324         XDebugSessionTab tab = session.getSessionTab();
325         if (tab != null) {
326           tab.select();
327         }
328       }
329       else {
330         myExecutionPointHighlighter.hide();
331       }
332       onActiveSessionChanged();
333     }
334   }
335
336   @Override
337   public XDebuggerState getState() {
338     return new XDebuggerState(myBreakpointManager.getState(), myWatchesManager.getState());
339   }
340
341   @Override
342   public void loadState(final XDebuggerState state) {
343     myBreakpointManager.loadState(state.myBreakpointManagerState);
344     myWatchesManager.loadState(state.myWatchesManagerState);
345   }
346
347   public void showExecutionPosition() {
348     myExecutionPointHighlighter.navigateTo();
349   }
350
351   @SuppressWarnings("UnusedDeclaration")
352   public static class XDebuggerState {
353     private XBreakpointManagerImpl.BreakpointManagerState myBreakpointManagerState;
354     private XDebuggerWatchesManager.WatchesManagerState myWatchesManagerState;
355
356     public XDebuggerState() {
357     }
358
359     public XDebuggerState(final XBreakpointManagerImpl.BreakpointManagerState breakpointManagerState, XDebuggerWatchesManager.WatchesManagerState watchesManagerState) {
360       myBreakpointManagerState = breakpointManagerState;
361       myWatchesManagerState = watchesManagerState;
362     }
363
364     @Property(surroundWithTag = false)
365     public XBreakpointManagerImpl.BreakpointManagerState getBreakpointManagerState() {
366       return myBreakpointManagerState;
367     }
368
369     public void setBreakpointManagerState(final XBreakpointManagerImpl.BreakpointManagerState breakpointManagerState) {
370       myBreakpointManagerState = breakpointManagerState;
371     }
372
373     @Property(surroundWithTag = false)
374     public XDebuggerWatchesManager.WatchesManagerState getWatchesManagerState() {
375       return myWatchesManagerState;
376     }
377
378     public void setWatchesManagerState(XDebuggerWatchesManager.WatchesManagerState watchesManagerState) {
379       myWatchesManagerState = watchesManagerState;
380     }
381   }
382 }