2 * Copyright 2000-2015 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Class DebuggerUtilsEx
21 package com.intellij.debugger.impl;
23 import com.intellij.debugger.DebuggerBundle;
24 import com.intellij.debugger.SourcePosition;
25 import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
26 import com.intellij.debugger.engine.DebuggerUtils;
27 import com.intellij.debugger.engine.SuspendContextImpl;
28 import com.intellij.debugger.engine.evaluation.*;
29 import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder;
30 import com.intellij.debugger.engine.requests.RequestManagerImpl;
31 import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
32 import com.intellij.debugger.requests.Requestor;
33 import com.intellij.debugger.ui.CompletionEditor;
34 import com.intellij.debugger.ui.breakpoints.Breakpoint;
35 import com.intellij.debugger.ui.tree.DebuggerTreeNode;
36 import com.intellij.execution.filters.ExceptionFilters;
37 import com.intellij.execution.filters.TextConsoleBuilder;
38 import com.intellij.execution.filters.TextConsoleBuilderFactory;
39 import com.intellij.execution.ui.ConsoleView;
40 import com.intellij.execution.ui.RunnerLayoutUi;
41 import com.intellij.execution.ui.layout.impl.RunnerContentUi;
42 import com.intellij.openapi.Disposable;
43 import com.intellij.openapi.actionSystem.DataContext;
44 import com.intellij.openapi.actionSystem.DefaultActionGroup;
45 import com.intellij.openapi.application.ApplicationManager;
46 import com.intellij.openapi.diagnostic.Logger;
47 import com.intellij.openapi.editor.Document;
48 import com.intellij.openapi.editor.Editor;
49 import com.intellij.openapi.editor.markup.RangeHighlighter;
50 import com.intellij.openapi.editor.markup.TextAttributes;
51 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
52 import com.intellij.openapi.fileTypes.FileType;
53 import com.intellij.openapi.fileTypes.StdFileTypes;
54 import com.intellij.openapi.project.Project;
55 import com.intellij.openapi.util.*;
56 import com.intellij.openapi.util.text.StringUtil;
57 import com.intellij.openapi.vfs.VirtualFile;
58 import com.intellij.pom.Navigatable;
59 import com.intellij.psi.*;
60 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
61 import com.intellij.psi.util.PsiTreeUtil;
62 import com.intellij.ui.classFilter.ClassFilter;
63 import com.intellij.ui.content.Content;
64 import com.intellij.unscramble.ThreadDumpPanel;
65 import com.intellij.unscramble.ThreadState;
66 import com.intellij.util.SmartList;
67 import com.intellij.util.containers.ContainerUtil;
68 import com.intellij.xdebugger.XSourcePosition;
69 import com.intellij.xdebugger.frame.XValueNode;
70 import com.intellij.xdebugger.impl.XSourcePositionImpl;
71 import com.intellij.xdebugger.impl.ui.ExecutionPointHighlighter;
73 import com.sun.jdi.event.Event;
74 import com.sun.jdi.event.EventSet;
75 import org.jdom.Attribute;
76 import org.jdom.Element;
77 import org.jetbrains.annotations.NonNls;
78 import org.jetbrains.annotations.NotNull;
79 import org.jetbrains.annotations.Nullable;
82 import java.util.regex.PatternSyntaxException;
84 public abstract class DebuggerUtilsEx extends DebuggerUtils {
85 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerUtilsEx");
89 * @return all CodeFragmentFactoryProviders that provide code fragment factories suitable in the context given
91 public static List<CodeFragmentFactory> getCodeFragmentFactories(@Nullable PsiElement context) {
92 final DefaultCodeFragmentFactory defaultFactory = DefaultCodeFragmentFactory.getInstance();
93 final CodeFragmentFactory[] providers = ApplicationManager.getApplication().getExtensions(CodeFragmentFactory.EXTENSION_POINT_NAME);
94 final List<CodeFragmentFactory> suitableFactories = new ArrayList<CodeFragmentFactory>(providers.length);
95 if (providers.length > 0) {
96 for (CodeFragmentFactory factory : providers) {
97 if (factory != defaultFactory && factory.isContextAccepted(context)) {
98 suitableFactories.add(factory);
102 suitableFactories.add(defaultFactory); // let default factory be the last one
103 return suitableFactories;
107 public static PsiMethod findPsiMethod(PsiFile file, int offset) {
108 PsiElement element = null;
111 element = file.findElementAt(offset);
112 if(element != null) {
118 for (; element != null; element = element.getParent()) {
119 if (element instanceof PsiClass || element instanceof PsiLambdaExpression) {
122 if (element instanceof PsiMethod) {
123 return (PsiMethod)element;
130 public static boolean isAssignableFrom(final String baseQualifiedName, ReferenceType checkedType) {
131 if (CommonClassNames.JAVA_LANG_OBJECT.equals(baseQualifiedName)) {
134 return getSuperClass(baseQualifiedName, checkedType) != null;
137 public static ReferenceType getSuperClass(final String baseQualifiedName, ReferenceType checkedType) {
138 if (baseQualifiedName.equals(checkedType.name())) {
142 if (checkedType instanceof ClassType) {
143 ClassType classType = (ClassType)checkedType;
144 ClassType superClassType = classType.superclass();
145 if (superClassType != null) {
146 ReferenceType superClass = getSuperClass(baseQualifiedName, superClassType);
147 if (superClass != null) {
151 List<InterfaceType> interfaces = classType.allInterfaces();
152 for (InterfaceType iface : interfaces) {
153 ReferenceType superClass = getSuperClass(baseQualifiedName, iface);
154 if (superClass != null) {
160 if (checkedType instanceof InterfaceType) {
161 List<InterfaceType> list = ((InterfaceType)checkedType).superinterfaces();
162 for (InterfaceType superInterface : list) {
163 ReferenceType superClass = getSuperClass(baseQualifiedName, superInterface);
164 if (superClass != null) {
172 public static boolean valuesEqual(Value val1, Value val2) {
179 if (val1 instanceof StringReference && val2 instanceof StringReference) {
180 return ((StringReference)val1).value().equals(((StringReference)val2).value());
182 return val1.equals(val2);
185 public static String getValueOrErrorAsString(final EvaluationContext evaluationContext, Value value) {
187 return getValueAsString(evaluationContext, value);
189 catch (EvaluateException e) {
190 return e.getMessage();
194 public static boolean isCharOrInteger(Value value) {
195 return value instanceof CharValue || isInteger(value);
198 private static Set<String> myCharOrIntegers;
200 @SuppressWarnings({"HardCodedStringLiteral"})
201 public static boolean isCharOrIntegerArray(Value value) {
202 if (value == null) return false;
203 if (myCharOrIntegers == null) {
204 myCharOrIntegers = new HashSet<String>();
205 myCharOrIntegers.add("C");
206 myCharOrIntegers.add("B");
207 myCharOrIntegers.add("S");
208 myCharOrIntegers.add("I");
209 myCharOrIntegers.add("J");
212 String signature = value.type().signature();
214 for (i = 0; signature.charAt(i) == '['; i++) ;
215 if (i == 0) return false;
216 signature = signature.substring(i, signature.length());
217 return myCharOrIntegers.contains(signature);
220 public static ClassFilter create(Element element) throws InvalidDataException {
221 ClassFilter filter = new ClassFilter();
222 DefaultJDOMExternalizer.readExternal(filter, element);
226 private static boolean isFiltered(ClassFilter classFilter, String qName) {
227 if (!classFilter.isEnabled()) {
231 if (classFilter.matches(qName)) {
235 catch (PatternSyntaxException e) {
241 public static boolean isFiltered(String qName, ClassFilter[] classFilters) {
242 return isFiltered(qName, Arrays.asList(classFilters));
245 public static boolean isFiltered(String qName, List<ClassFilter> classFilters) {
246 if(qName.indexOf('[') != -1) {
247 return false; //is array
250 for (ClassFilter filter : classFilters) {
251 if (isFiltered(filter, qName)) {
258 public static int getEnabledNumber(ClassFilter[] classFilters) {
260 for (ClassFilter filter : classFilters) {
261 if (filter.isEnabled()) {
268 public static ClassFilter[] readFilters(List<Element> children) throws InvalidDataException {
269 if (ContainerUtil.isEmpty(children)) {
270 return ClassFilter.EMPTY_ARRAY;
273 ClassFilter[] filters = new ClassFilter[children.size()];
274 for (int i = 0, size = children.size(); i < size; i++) {
275 filters[i] = create(children.get(i));
280 public static void writeFilters(Element parentNode, @NonNls String tagName, ClassFilter[] filters) throws WriteExternalException {
281 for (ClassFilter filter : filters) {
282 Element element = new Element(tagName);
283 parentNode.addContent(element);
284 DefaultJDOMExternalizer.writeExternal(filter, element);
288 public static boolean filterEquals(ClassFilter[] filters1, ClassFilter[] filters2) {
289 if (filters1.length != filters2.length) {
292 final Set<ClassFilter> f1 = new HashSet<ClassFilter>(Math.max((int) (filters1.length/.75f) + 1, 16));
293 final Set<ClassFilter> f2 = new HashSet<ClassFilter>(Math.max((int) (filters2.length/.75f) + 1, 16));
294 Collections.addAll(f1, filters1);
295 Collections.addAll(f2, filters2);
296 return f2.equals(f1);
299 private static boolean elementListsEqual(List<Element> l1, List<Element> l2) {
300 if(l1 == null) return l2 == null;
301 if(l2 == null) return false;
303 if(l1.size() != l2.size()) return false;
305 Iterator<Element> i1 = l1.iterator();
307 for (Element aL2 : l2) {
308 Element elem1 = i1.next();
310 if (!elementsEqual(elem1, aL2)) return false;
315 private static boolean attributeListsEqual(List<Attribute> l1, List<Attribute> l2) {
316 if(l1 == null) return l2 == null;
317 if(l2 == null) return false;
319 if(l1.size() != l2.size()) return false;
321 Iterator<Attribute> i1 = l1.iterator();
323 for (Attribute aL2 : l2) {
324 Attribute attr1 = i1.next();
326 if (!Comparing.equal(attr1.getName(), aL2.getName()) || !Comparing.equal(attr1.getValue(), aL2.getValue())) {
333 public static boolean elementsEqual(Element e1, Element e2) {
337 if (!Comparing.equal(e1.getName(), e2.getName())) {
340 if (!elementListsEqual (e1.getChildren(), e2.getChildren())) {
343 if (!attributeListsEqual(e1.getAttributes(), e2.getAttributes())) {
349 @SuppressWarnings({"HardCodedStringLiteral"})
350 public static boolean externalizableEqual(JDOMExternalizable e1, JDOMExternalizable e2) {
351 Element root1 = new Element("root");
352 Element root2 = new Element("root");
354 e1.writeExternal(root1);
356 catch (WriteExternalException e) {
360 e2.writeExternal(root2);
362 catch (WriteExternalException e) {
366 return elementsEqual(root1, root2);
370 public static List<Pair<Breakpoint, Event>> getEventDescriptors(SuspendContextImpl suspendContext) {
371 DebuggerManagerThreadImpl.assertIsManagerThread();
372 if(suspendContext == null) {
373 return Collections.emptyList();
375 final EventSet events = suspendContext.getEventSet();
377 return Collections.emptyList();
379 final List<Pair<Breakpoint, Event>> eventDescriptors = new SmartList<Pair<Breakpoint, Event>>();
381 final RequestManagerImpl requestManager = suspendContext.getDebugProcess().getRequestsManager();
382 for (final Event event : events) {
383 final Requestor requestor = requestManager.findRequestor(event.request());
384 if (requestor instanceof Breakpoint) {
385 eventDescriptors.add(Pair.create((Breakpoint)requestor, event));
388 return eventDescriptors;
391 public static TextWithImports getEditorText(final Editor editor) {
392 if (editor == null) {
395 final Project project = editor.getProject();
396 if (project == null) return null;
398 String defaultExpression = editor.getSelectionModel().getSelectedText();
399 if (defaultExpression == null) {
400 int offset = editor.getCaretModel().getOffset();
401 PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
402 if (psiFile != null) {
403 PsiElement elementAtCursor = psiFile.findElementAt(offset);
404 if (elementAtCursor != null) {
405 final EditorTextProvider textProvider = EditorTextProvider.EP.forLanguage(elementAtCursor.getLanguage());
406 if (textProvider != null) {
407 final TextWithImports editorText = textProvider.getEditorText(elementAtCursor);
408 if (editorText != null) return editorText;
414 return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, defaultExpression);
419 private static int myThreadDumpsCount = 0;
420 private static int myCurrentThreadDumpId = 1;
422 private static final String THREAD_DUMP_CONTENT_PREFIX = "Dump";
424 public static void addThreadDump(Project project, List<ThreadState> threads, final RunnerLayoutUi ui, DebuggerSession session) {
425 final TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project);
426 consoleBuilder.filters(ExceptionFilters.getFilters(session.getSearchScope()));
427 final ConsoleView consoleView = consoleBuilder.getConsole();
428 final DefaultActionGroup toolbarActions = new DefaultActionGroup();
429 consoleView.allowHeavyFilters();
430 final ThreadDumpPanel panel = new ThreadDumpPanel(project, consoleView, toolbarActions, threads);
432 final String id = THREAD_DUMP_CONTENT_PREFIX + " #" + myCurrentThreadDumpId;
433 final Content content = ui.createContent(id, panel, id, null, null);
434 content.putUserData(RunnerContentUi.LIGHTWEIGHT_CONTENT_MARKER, Boolean.TRUE);
435 content.setCloseable(true);
436 content.setDescription("Thread Dump");
437 ui.addContent(content);
438 ui.selectAndFocus(content, true, true);
439 myThreadDumpsCount++;
440 myCurrentThreadDumpId++;
441 Disposer.register(content, new Disposable() {
443 public void dispose() {
444 myThreadDumpsCount--;
445 if (myThreadDumpsCount == 0) {
446 myCurrentThreadDumpId = 1;
450 Disposer.register(content, consoleView);
451 ui.selectAndFocus(content, true, false);
452 if (threads.size() > 0) {
453 panel.selectStackFrame(0);
457 public static void keep(Value value, EvaluationContext context) {
458 if (value instanceof ObjectReference) {
459 ((SuspendContextImpl)context.getSuspendContext()).keep((ObjectReference)value);
463 public abstract DebuggerTreeNode getSelectedNode (DataContext context);
465 public abstract EvaluatorBuilder getEvaluatorBuilder();
467 public abstract CompletionEditor createEditor(Project project, PsiElement context, @NonNls String recentsId);
470 public static CodeFragmentFactory findAppropriateCodeFragmentFactory(final TextWithImports text, final PsiElement context) {
471 CodeFragmentFactory factory = ApplicationManager.getApplication().runReadAction(new Computable<CodeFragmentFactory>() {
473 public CodeFragmentFactory compute() {
474 final FileType fileType = text.getFileType();
475 final List<CodeFragmentFactory> factories = getCodeFragmentFactories(context);
476 if (fileType == null) {
477 return factories.get(0);
479 for (CodeFragmentFactory factory : factories) {
480 if (factory.getFileType().equals(fileType)) {
484 return DefaultCodeFragmentFactory.getInstance();
487 return new CodeFragmentFactoryContextWrapper(factory);
490 private static class SigReader {
494 SigReader(String s) {
499 return buffer.charAt(pos++);
503 return buffer.charAt(pos);
507 return buffer.length() <= pos;
510 @NonNls String getSignature() {
511 if (eof()) return "";
534 pos = buffer.indexOf(';', start) + 1;
535 LOG.assertTrue(pos > 0);
536 return buffer.substring(start, pos - 1).replace('/', '.');
538 return getSignature() + "[]";
540 StringBuilder result = new StringBuilder("(");
541 String separator = "";
542 while (peek() != ')') {
543 result.append(separator);
544 result.append(getSignature());
549 return getSignature() + " " + getClassName() + "." + getMethodName() + " " + result;
551 // LOG.assertTrue(false, "unknown signature " + buffer);
556 String getMethodName() {
560 String getClassName() {
565 public static String methodName(final Method m) {
566 return methodName(signatureToName(m.declaringType().signature()), m.name(), m.signature());
569 public static String methodName(final String className, final String methodName, final String signature) {
571 return new SigReader(signature) {
573 String getMethodName() {
578 String getClassName() {
583 catch (Exception ignored) {
584 if (LOG.isDebugEnabled()) {
585 LOG.debug("Internal error : unknown signature" + signature);
587 return className + "." + methodName;
591 public static String signatureToName(String s) {
592 return new SigReader(s).getSignature();
595 public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, double value) {
596 if (PsiType.DOUBLE.getPresentableText().equals(expectedType)) {
597 return vm.mirrorOf(value);
599 if (PsiType.FLOAT.getPresentableText().equals(expectedType)) {
600 return vm.mirrorOf((float)value);
602 return createValue(vm, expectedType, (long)value);
605 public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, long value) {
606 if (PsiType.LONG.getPresentableText().equals(expectedType)) {
607 return vm.mirrorOf(value);
609 if (PsiType.INT.getPresentableText().equals(expectedType)) {
610 return vm.mirrorOf((int)value);
612 if (PsiType.SHORT.getPresentableText().equals(expectedType)) {
613 return vm.mirrorOf((short)value);
615 if (PsiType.BYTE.getPresentableText().equals(expectedType)) {
616 return vm.mirrorOf((byte)value);
618 if (PsiType.CHAR.getPresentableText().equals(expectedType)) {
619 return vm.mirrorOf((char)value);
621 if (PsiType.DOUBLE.getPresentableText().equals(expectedType)) {
622 return vm.mirrorOf((double)value);
624 if (PsiType.FLOAT.getPresentableText().equals(expectedType)) {
625 return vm.mirrorOf((float)value);
630 public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, boolean value) {
631 if (PsiType.BOOLEAN.getPresentableText().equals(expectedType)) {
632 return vm.mirrorOf(value);
637 public static Value createValue(VirtualMachineProxyImpl vm, String expectedType, char value) {
638 if (PsiType.CHAR.getPresentableText().equals(expectedType)) {
639 return vm.mirrorOf(value);
641 if (PsiType.LONG.getPresentableText().equals(expectedType)) {
642 return vm.mirrorOf((long)value);
644 if (PsiType.INT.getPresentableText().equals(expectedType)) {
645 return vm.mirrorOf((int)value);
647 if (PsiType.SHORT.getPresentableText().equals(expectedType)) {
648 return vm.mirrorOf((short)value);
650 if (PsiType.BYTE.getPresentableText().equals(expectedType)) {
651 return vm.mirrorOf((byte)value);
656 public static String truncateString(final String str) {
657 // leave a small gap over XValueNode.MAX_VALUE_LENGTH to detect oversize
658 if (str.length() > XValueNode.MAX_VALUE_LENGTH + 5) {
659 return str.substring(0, XValueNode.MAX_VALUE_LENGTH + 5);
664 public static String getThreadStatusText(int statusId) {
666 case ThreadReference.THREAD_STATUS_MONITOR:
667 return DebuggerBundle.message("status.thread.monitor");
668 case ThreadReference.THREAD_STATUS_NOT_STARTED:
669 return DebuggerBundle.message("status.thread.not.started");
670 case ThreadReference.THREAD_STATUS_RUNNING:
671 return DebuggerBundle.message("status.thread.running");
672 case ThreadReference.THREAD_STATUS_SLEEPING:
673 return DebuggerBundle.message("status.thread.sleeping");
674 case ThreadReference.THREAD_STATUS_UNKNOWN:
675 return DebuggerBundle.message("status.thread.unknown");
676 case ThreadReference.THREAD_STATUS_WAIT:
677 return DebuggerBundle.message("status.thread.wait");
678 case ThreadReference.THREAD_STATUS_ZOMBIE:
679 return DebuggerBundle.message("status.thread.zombie");
681 return DebuggerBundle.message("status.thread.undefined");
685 public static String prepareValueText(String text, Project project) {
686 text = StringUtil.unquoteString(text);
687 text = StringUtil.unescapeStringCharacters(text);
688 int tabSize = CodeStyleSettingsManager.getSettings(project).getTabSize(StdFileTypes.JAVA);
692 return text.replace("\t", StringUtil.repeat(" ", tabSize));
696 public static XSourcePosition toXSourcePosition(@NotNull SourcePosition position) {
697 VirtualFile file = position.getFile().getVirtualFile();
699 file = position.getFile().getOriginalFile().getVirtualFile();
704 return new JavaXSourcePosition(position, file);
707 private static final Key<VirtualFile> ALTERNATIVE_SOURCE_KEY = new Key<VirtualFile>("DEBUGGER_ALTERNATIVE_SOURCE");
709 public static void setAlternativeSource(VirtualFile source, VirtualFile dest) {
710 ALTERNATIVE_SOURCE_KEY.set(source, dest);
711 ALTERNATIVE_SOURCE_KEY.set(dest, null);
714 private static class JavaXSourcePosition implements XSourcePosition, ExecutionPointHighlighter.HighlighterProvider {
715 private final SourcePosition mySourcePosition;
716 @NotNull private final VirtualFile myFile;
718 public JavaXSourcePosition(@NotNull SourcePosition sourcePosition, @NotNull VirtualFile file) {
719 mySourcePosition = sourcePosition;
724 public int getLine() {
725 return mySourcePosition.getLine();
729 public int getOffset() {
730 return mySourcePosition.getOffset();
735 public VirtualFile getFile() {
736 VirtualFile file = ALTERNATIVE_SOURCE_KEY.get(myFile);
745 public Navigatable createNavigatable(@NotNull Project project) {
746 if (ALTERNATIVE_SOURCE_KEY.get(myFile) != null) {
747 return new OpenFileDescriptor(project, getFile(), getLine(), 0);
749 return XSourcePositionImpl.doCreateOpenFileDescriptor(project, this);
754 public RangeHighlighter createHighlighter(Document document, Project project, TextAttributes attributes) {
755 if (mySourcePosition instanceof ExecutionPointHighlighter.HighlighterProvider) {
756 return ((ExecutionPointHighlighter.HighlighterProvider)mySourcePosition).createHighlighter(document, project, attributes);
763 * Decompiler aware version
766 public static PsiElement findElementAt(@Nullable PsiFile file, int offset) {
767 if (file instanceof PsiCompiledFile) {
768 file = ((PsiCompiledFile)file).getDecompiledPsiFile();
770 if (file == null) return null;
771 return file.findElementAt(offset);
774 public static String getLocationMethodQName(@NotNull Location location) {
775 StringBuilder res = new StringBuilder();
776 ReferenceType type = location.declaringType();
778 res.append(type.name()).append('.');
780 res.append(location.method().name());
781 return res.toString();
784 private static PsiElement getNextElement(PsiElement element) {
785 PsiElement sibling = element.getNextSibling();
786 if (sibling != null) return sibling;
787 element = element.getParent();
788 if (element != null) return getNextElement(element);
792 public static List<PsiLambdaExpression> collectLambdas(SourcePosition position, final boolean onlyOnTheLine) {
793 ApplicationManager.getApplication().assertReadAccessAllowed();
794 PsiFile file = position.getFile();
795 int line = position.getLine();
796 Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
797 if (document == null || line >= document.getLineCount()) {
798 return Collections.emptyList();
800 PsiElement element = position.getElementAt();
801 final TextRange lineRange = new TextRange(document.getLineStartOffset(line), document.getLineEndOffset(line));
803 PsiElement parent = element.getParent();
804 if (parent == null || (parent.getTextOffset() < lineRange.getStartOffset())) {
811 final List<PsiLambdaExpression> lambdas = new ArrayList<PsiLambdaExpression>(3);
812 final PsiElementVisitor lambdaCollector = new JavaRecursiveElementVisitor() {
814 public void visitLambdaExpression(PsiLambdaExpression expression) {
815 super.visitLambdaExpression(expression);
816 if (!onlyOnTheLine || lineRange.intersects(expression.getTextRange())) {
817 lambdas.add(expression);
821 element.accept(lambdaCollector);
822 // add initial lambda if we're inside already
823 NavigatablePsiElement method = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiLambdaExpression.class);
824 if (method instanceof PsiLambdaExpression) {
825 lambdas.add((PsiLambdaExpression)method);
827 for (PsiElement sibling = getNextElement(element); sibling != null; sibling = getNextElement(sibling)) {
828 if (!lineRange.intersects(sibling.getTextRange())) {
831 sibling.accept(lambdaCollector);
837 public static PsiElement getFirstElementOnTheLine(PsiLambdaExpression lambda, Document document, int line) {
838 ApplicationManager.getApplication().assertReadAccessAllowed();
839 TextRange lineRange = new TextRange(document.getLineStartOffset(line), document.getLineEndOffset(line));
840 if (!lineRange.intersects(lambda.getTextRange())) return null;
841 PsiElement body = lambda.getBody();
842 if (body instanceof PsiCodeBlock) {
843 for (PsiStatement statement : ((PsiCodeBlock)body).getStatements()) {
844 if (lineRange.intersects(statement.getTextRange())) {
852 public static boolean inTheSameMethod(@NotNull SourcePosition pos1, @NotNull SourcePosition pos2) {
853 ApplicationManager.getApplication().assertReadAccessAllowed();
854 PsiElement elem1 = pos1.getElementAt();
855 PsiElement elem2 = pos2.getElementAt();
856 if (elem1 == null) return elem2 == null;
858 NavigatablePsiElement expectedMethod = PsiTreeUtil.getParentOfType(elem1, PsiMethod.class, PsiLambdaExpression.class);
859 NavigatablePsiElement currentMethod = PsiTreeUtil.getParentOfType(elem2, PsiMethod.class, PsiLambdaExpression.class);
860 return Comparing.equal(expectedMethod, currentMethod);