blob: 8e51cefab29a8aba801b81f982d5bb9b282b9267 [file] [log] [blame]
/*
* Copyright 2000-2009 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.lang.Language;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Processor;
import com.intellij.xdebugger.*;
import com.intellij.xdebugger.breakpoints.*;
import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
import com.intellij.xdebugger.evaluation.EvaluationMode;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.frame.XExecutionStack;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.frame.XSuspendContext;
import com.intellij.xdebugger.frame.XValueContainer;
import com.intellij.xdebugger.impl.breakpoints.XBreakpointUtil;
import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl;
import com.intellij.xdebugger.impl.breakpoints.ui.grouping.XBreakpointFileGroupingRule;
import com.intellij.xdebugger.impl.evaluate.quick.common.ValueLookupManager;
import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager;
import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase;
import com.intellij.xdebugger.settings.XDebuggerSettings;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author nik
*/
public class XDebuggerUtilImpl extends XDebuggerUtil {
private XLineBreakpointType<?>[] myLineBreakpointTypes;
private Map<Class<? extends XBreakpointType>, XBreakpointType<?,?>> myBreakpointTypeByClass;
@Override
public XLineBreakpointType<?>[] getLineBreakpointTypes() {
if (myLineBreakpointTypes == null) {
XBreakpointType[] types = XBreakpointUtil.getBreakpointTypes();
List<XLineBreakpointType<?>> lineBreakpointTypes = new ArrayList<XLineBreakpointType<?>>();
for (XBreakpointType type : types) {
if (type instanceof XLineBreakpointType<?>) {
lineBreakpointTypes.add((XLineBreakpointType<?>)type);
}
}
myLineBreakpointTypes = lineBreakpointTypes.toArray(new XLineBreakpointType<?>[lineBreakpointTypes.size()]);
}
return myLineBreakpointTypes;
}
@Override
public void toggleLineBreakpoint(@NotNull final Project project, @NotNull final VirtualFile file, final int line, boolean temporary) {
XLineBreakpointType<?> typeWinner = null;
for (XLineBreakpointType<?> type : getLineBreakpointTypes()) {
if (type.canPutAt(file, line, project) && (typeWinner == null || type.getPriority() > typeWinner.getPriority())) {
typeWinner = type;
}
}
if (typeWinner != null) {
toggleLineBreakpoint(project, typeWinner, file, line, temporary);
}
}
@Override
public boolean canPutBreakpointAt(@NotNull Project project, @NotNull VirtualFile file, int line) {
for (XLineBreakpointType<?> type : getLineBreakpointTypes()) {
if (type.canPutAt(file, line, project)) {
return true;
}
}
return false;
}
@Override
public <P extends XBreakpointProperties> void toggleLineBreakpoint(@NotNull final Project project,
@NotNull final XLineBreakpointType<P> type,
@NotNull final VirtualFile file,
final int line,
final boolean temporary) {
toggleAndReturnLineBreakpoint(project, type, file, line, temporary);
}
public static <P extends XBreakpointProperties> XLineBreakpoint toggleAndReturnLineBreakpoint(@NotNull final Project project,
@NotNull final XLineBreakpointType<P> type,
@NotNull final VirtualFile file,
final int line,
final boolean temporary) {
return new WriteAction<XLineBreakpoint>() {
@Override
protected void run(@NotNull final Result<XLineBreakpoint> result) {
XBreakpointManager breakpointManager = XDebuggerManager.getInstance(project).getBreakpointManager();
XLineBreakpoint<P> breakpoint = breakpointManager.findBreakpointAtLine(type, file, line);
if (breakpoint != null) {
breakpointManager.removeBreakpoint(breakpoint);
}
else {
P properties = type.createBreakpointProperties(file, line);
result.setResult(breakpointManager.addLineBreakpoint(type, file.getUrl(), line, properties, temporary));
}
}
}.execute().getResultObject();
}
@Override
public void removeBreakpoint(final Project project, final XBreakpoint<?> breakpoint) {
new WriteAction() {
@Override
protected void run(@NotNull final Result result) {
XDebuggerManager.getInstance(project).getBreakpointManager().removeBreakpoint(breakpoint);
}
}.execute();
}
@Override
public <B extends XBreakpoint<?>> XBreakpointType<B, ?> findBreakpointType(@NotNull Class<? extends XBreakpointType<B, ?>> typeClass) {
if (myBreakpointTypeByClass == null) {
myBreakpointTypeByClass = new THashMap<Class<? extends XBreakpointType>, XBreakpointType<?,?>>();
for (XBreakpointType<?, ?> breakpointType : XBreakpointUtil.getBreakpointTypes()) {
myBreakpointTypeByClass.put(breakpointType.getClass(), breakpointType);
}
}
XBreakpointType<?, ?> type = myBreakpointTypeByClass.get(typeClass);
//noinspection unchecked
return (XBreakpointType<B, ?>)type;
}
@Override
public <T extends XDebuggerSettings<?>> T getDebuggerSettings(Class<T> aClass) {
return XDebuggerSettingsManager.getInstanceImpl().getSettings(aClass);
}
@Override
public XValueContainer getValueContainer(DataContext dataContext) {
return XDebuggerTreeActionBase.getSelectedValue(dataContext);
}
@Override
@Nullable
public XSourcePosition createPosition(final VirtualFile file, final int line) {
return XSourcePositionImpl.create(file, line);
}
@Override
@Nullable
public XSourcePosition createPositionByOffset(final VirtualFile file, final int offset) {
return XSourcePositionImpl.createByOffset(file, offset);
}
@Override
public <B extends XLineBreakpoint<?>> XBreakpointGroupingRule<B, ?> getGroupingByFileRule() {
return new XBreakpointFileGroupingRule<B>();
}
@Nullable
public static XSourcePosition getCaretPosition(@NotNull Project project, DataContext context) {
Editor editor = getEditor(project, context);
if (editor == null) return null;
final Document document = editor.getDocument();
final int line = editor.getCaretModel().getLogicalPosition().line;
VirtualFile file = FileDocumentManager.getInstance().getFile(document);
return XSourcePositionImpl.create(file, line);
}
@NotNull
public static Collection<XSourcePosition> getAllCaretsPositions(@NotNull Project project, DataContext context) {
Editor editor = getEditor(project, context);
if (editor == null) {
return Collections.emptyList();
}
final Document document = editor.getDocument();
VirtualFile file = FileDocumentManager.getInstance().getFile(document);
Collection<XSourcePosition> res = new ArrayList<XSourcePosition>();
List<Caret> carets = editor.getCaretModel().getAllCarets();
for (Caret caret : carets) {
int line = caret.getLogicalPosition().line;
XSourcePositionImpl position = XSourcePositionImpl.create(file, line);
if (position != null) {
res.add(position);
}
}
return res;
}
@Nullable
private static Editor getEditor(@NotNull Project project, DataContext context) {
Editor editor = CommonDataKeys.EDITOR.getData(context);
if(editor == null) {
return FileEditorManager.getInstance(project).getSelectedTextEditor();
}
return editor;
}
@Override
public <B extends XBreakpoint<?>> Comparator<B> getDefaultBreakpointComparator(final XBreakpointType<B, ?> type) {
return new Comparator<B>() {
@Override
public int compare(final B o1, final B o2) {
return type.getDisplayText(o1).compareTo(type.getDisplayText(o2));
}
};
}
@Override
public <P extends XBreakpointProperties> Comparator<XLineBreakpoint<P>> getDefaultLineBreakpointComparator() {
return new Comparator<XLineBreakpoint<P>>() {
@Override
public int compare(final XLineBreakpoint<P> o1, final XLineBreakpoint<P> o2) {
int fileCompare = o1.getFileUrl().compareTo(o2.getFileUrl());
if (fileCompare != 0) return fileCompare;
return o1.getLine() - o2.getLine();
}
};
}
@Nullable
public static XDebuggerEvaluator getEvaluator(final XSuspendContext suspendContext) {
XExecutionStack executionStack = suspendContext.getActiveExecutionStack();
if (executionStack != null) {
XStackFrame stackFrame = executionStack.getTopFrame();
if (stackFrame != null) {
return stackFrame.getEvaluator();
}
}
return null;
}
@Override
public void iterateLine(@NotNull Project project, @NotNull Document document, int line, @NotNull Processor<PsiElement> processor) {
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document);
if (file == null) {
return;
}
int lineStart;
int lineEnd;
try {
lineStart = document.getLineStartOffset(line);
lineEnd = document.getLineEndOffset(line);
}
catch (IndexOutOfBoundsException ignored) {
return;
}
PsiElement element;
int offset = lineStart;
while (offset < lineEnd) {
element = file.findElementAt(offset);
if (element != null) {
if (!processor.process(element)) {
return;
}
else {
offset = element.getTextRange().getEndOffset();
}
}
else {
offset++;
}
}
}
@Override
public <B extends XLineBreakpoint<?>> List<XBreakpointGroupingRule<B, ?>> getGroupingByFileRuleAsList() {
return Collections.<XBreakpointGroupingRule<B, ?>>singletonList(this.<B>getGroupingByFileRule());
}
@Override
@Nullable
public PsiElement findContextElement(@NotNull VirtualFile virtualFile, int offset, @NotNull Project project, boolean checkXml) {
if (!virtualFile.isValid()) {
return null;
}
Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
PsiFile file = document == null ? null : PsiManager.getInstance(project).findFile(virtualFile);
if (file == null) {
return null;
}
if (offset < 0) {
offset = 0;
}
if (offset > document.getTextLength()) {
offset = document.getTextLength();
}
int startOffset = offset;
int lineEndOffset = document.getLineEndOffset(document.getLineNumber(offset));
PsiElement result = null;
do {
PsiElement element = file.findElementAt(offset);
if (!(element instanceof PsiWhiteSpace) && !(element instanceof PsiComment)) {
result = element;
break;
}
offset = element.getTextRange().getEndOffset() + 1;
}
while (offset < lineEndOffset);
if (result == null) {
result = file.findElementAt(startOffset);
}
if (checkXml && result != null && StdFileTypes.XML.getLanguage().equals(result.getLanguage())) {
PsiLanguageInjectionHost parent = PsiTreeUtil.getParentOfType(result, PsiLanguageInjectionHost.class);
if (parent != null) {
result = InjectedLanguageUtil.findElementInInjected(parent, offset);
}
}
return result;
}
@Override
public void disableValueLookup(@NotNull Editor editor) {
ValueLookupManager.DISABLE_VALUE_LOOKUP.set(editor, Boolean.TRUE);
}
@Nullable
public static Editor createEditor(@NotNull OpenFileDescriptor descriptor) {
return descriptor.canNavigate() ? FileEditorManager.getInstance(descriptor.getProject()).openTextEditor(descriptor, false) : null;
}
public static void rebuildAllSessionsViews(@Nullable Project project) {
if (project == null) return;
for (XDebugSession session : XDebuggerManager.getInstance(project).getDebugSessions()) {
if (session.isSuspended()) {
session.rebuildViews();
}
}
}
@NotNull
@Override
public XExpression createExpression(@NotNull String text, Language language, String custom, EvaluationMode mode) {
return new XExpressionImpl(text, language, custom, mode);
}
public static boolean isEmptyExpression(@Nullable XExpression expression) {
return expression == null || StringUtil.isEmptyOrSpaces(expression.getExpression());
}
}