// returns (injected psi, leaf element at the offset, language of the leaf element)
  // since findElementAt() is expensive, we trying to reuse its result
  @NotNull
  private static Trinity<PsiElement, PsiElement, Language> tryOffset(
      @NotNull PsiFile hostFile, final int offset, @NotNull PsiDocumentManager documentManager) {
    FileViewProvider provider = hostFile.getViewProvider();
    Language leafLanguage = null;
    PsiElement leafElement = null;
    for (Language language : provider.getLanguages()) {
      PsiElement element = provider.findElementAt(offset, language);
      if (element != null) {
        if (leafLanguage == null) {
          leafLanguage = language;
          leafElement = element;
        }
        PsiElement injected = findInside(element, hostFile, offset, documentManager);
        if (injected != null) return Trinity.create(injected, element, language);
      }
      // maybe we are at the border between two psi elements, then try to find injection at the end
      // of the left element
      if (offset != 0 && (element == null || element.getTextRange().getStartOffset() == offset)) {
        PsiElement leftElement = provider.findElementAt(offset - 1, language);
        if (leftElement != null && leftElement.getTextRange().getEndOffset() == offset) {
          PsiElement injected = findInside(leftElement, hostFile, offset, documentManager);
          if (injected != null) return Trinity.create(injected, element, language);
        }
      }
    }

    return Trinity.create(null, leafElement, leafLanguage);
  }
 public static ValueHint createValueHint(
     Project project, Editor editor, Point point, ValueHintType type) {
   Trinity<PsiElement, TextRange, Value> trinity =
       getSelectedExpression(project, editor, point, type);
   final ValueHint hint =
       new ValueHint(project, editor, point, type, trinity.getFirst(), trinity.getSecond());
   hint.myValueToShow = trinity.getThird();
   return hint;
 }
  private void highlightInjectedSyntax(final PsiFile injectedPsi, HighlightInfoHolder holder) {
    List<Trinity<IElementType, PsiLanguageInjectionHost, TextRange>> tokens =
        InjectedLanguageUtil.getHighlightTokens(injectedPsi);
    if (tokens == null) return;

    final Language injectedLanguage = injectedPsi.getLanguage();
    Project project = injectedPsi.getProject();
    SyntaxHighlighter syntaxHighlighter =
        SyntaxHighlighterFactory.getSyntaxHighlighter(
            injectedLanguage, project, injectedPsi.getVirtualFile());
    final TextAttributes defaultAttrs = myGlobalScheme.getAttributes(HighlighterColors.TEXT);

    for (Trinity<IElementType, PsiLanguageInjectionHost, TextRange> token : tokens) {
      ProgressManager.checkCanceled();
      IElementType tokenType = token.getFirst();
      PsiLanguageInjectionHost injectionHost = token.getSecond();
      TextRange textRange = token.getThird();
      TextAttributesKey[] keys = syntaxHighlighter.getTokenHighlights(tokenType);
      if (textRange.getLength() == 0) continue;

      TextRange annRange = textRange.shiftRight(injectionHost.getTextRange().getStartOffset());
      // force attribute colors to override host' ones
      TextAttributes attributes = null;
      for (TextAttributesKey key : keys) {
        TextAttributes attrs2 = myGlobalScheme.getAttributes(key);
        if (attrs2 != null) {
          attributes = attributes == null ? attrs2 : TextAttributes.merge(attributes, attrs2);
        }
      }
      TextAttributes forcedAttributes;
      if (attributes == null || attributes.isEmpty() || attributes.equals(defaultAttrs)) {
        forcedAttributes = TextAttributes.ERASE_MARKER;
      } else {
        Color back =
            attributes.getBackgroundColor() == null
                ? myGlobalScheme.getDefaultBackground()
                : attributes.getBackgroundColor();
        Color fore =
            attributes.getForegroundColor() == null
                ? myGlobalScheme.getDefaultForeground()
                : attributes.getForegroundColor();
        forcedAttributes =
            new TextAttributes(
                fore,
                back,
                attributes.getEffectColor(),
                attributes.getEffectType(),
                attributes.getFontType());
      }

      HighlightInfo info =
          HighlightInfo.createHighlightInfo(
              HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT, annRange, null, null, forcedAttributes);
      holder.add(info);
    }
  }
  public void initMarkers(Place shreds) {
    SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject);
    int curOffset = -1;
    for (PsiLanguageInjectionHost.Shred shred : shreds) {
      final RangeMarker rangeMarker =
          myNewDocument.createRangeMarker(
              shred.getRange().getStartOffset() + shred.getPrefix().length(),
              shred.getRange().getEndOffset() - shred.getSuffix().length());
      final TextRange rangeInsideHost = shred.getRangeInsideHost();
      PsiLanguageInjectionHost host = shred.getHost();
      RangeMarker origMarker =
          myOrigDocument.createRangeMarker(
              rangeInsideHost.shiftRight(host.getTextRange().getStartOffset()));
      SmartPsiElementPointer<PsiLanguageInjectionHost> elementPointer =
          smartPointerManager.createSmartPsiElementPointer(host);
      Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> markers =
          Trinity.<RangeMarker, RangeMarker, SmartPsiElementPointer>create(
              origMarker, rangeMarker, elementPointer);
      myMarkers.add(markers);

      origMarker.setGreedyToRight(true);
      rangeMarker.setGreedyToRight(true);
      if (origMarker.getStartOffset() > curOffset) {
        origMarker.setGreedyToLeft(true);
        rangeMarker.setGreedyToLeft(true);
      }
      curOffset = origMarker.getEndOffset();
    }
    initGuardedBlocks(shreds);
  }
 @NotNull
 private GroovyResolveResult[] doPolyResolveWithCaching(
     final boolean incompleteCode, final boolean genericsMatter) {
   final InferenceContext context = TypeInferenceHelper.getCurrentContext();
   final Trinity<?, ?, ?> key =
       Trinity.create(this, context, Pair.create(incompleteCode, genericsMatter));
   final GroovyResolveResult[] value =
       RecursionManager.doPreventingRecursion(
           key,
           true,
           new Computable<GroovyResolveResult[]>() {
             @Override
             public GroovyResolveResult[] compute() {
               return doPolyResolve(incompleteCode, genericsMatter);
             }
           });
   return value == null ? GroovyResolveResult.EMPTY_ARRAY : value;
 }
 @NotNull
 private Pair<Boolean, GroovyResolveResult[]> resolveByShape(
     final boolean allVariants, @Nullable final GrExpression upToArgument) {
   LOG.assertTrue(allVariants || upToArgument == null);
   final Trinity key =
       Trinity.create(
           TypeInferenceHelper.getCurrentContext(), this, Pair.create(allVariants, upToArgument));
   final Pair<Boolean, GroovyResolveResult[]> result =
       RecursionManager.doPreventingRecursion(
           key,
           true,
           new Computable<Pair<Boolean, GroovyResolveResult[]>>() {
             @Override
             public Pair<Boolean, GroovyResolveResult[]> compute() {
               return doResolveByShape(allVariants, upToArgument);
             }
           });
   return result == null ? Pair.create(false, GroovyResolveResult.EMPTY_ARRAY) : result;
 }
  @Nullable
  private <TRef extends PsiReference, TResult> TResult resolve(
      @NotNull final TRef ref,
      @NotNull final AbstractResolver<TRef, TResult> resolver,
      boolean needToPreventRecursion,
      final boolean incompleteCode,
      boolean isPoly,
      boolean isPhysical) {
    ProgressIndicatorProvider.checkCanceled();
    if (isPhysical) {
      ApplicationManager.getApplication().assertReadAccessAllowed();
    }

    int index = getIndex(isPhysical, incompleteCode, isPoly);
    ConcurrentMap<TRef, TResult> map = getMap(index);
    TResult result = map.get(ref);
    if (result != null) {
      return result;
    }

    RecursionGuard.StackStamp stamp = myGuard.markStack();
    result =
        needToPreventRecursion
            ? myGuard.doPreventingRecursion(
                Trinity.create(ref, incompleteCode, isPoly),
                true,
                new Computable<TResult>() {
                  @Override
                  public TResult compute() {
                    return resolver.resolve(ref, incompleteCode);
                  }
                })
            : resolver.resolve(ref, incompleteCode);
    PsiElement element =
        result instanceof ResolveResult ? ((ResolveResult) result).getElement() : null;
    LOG.assertTrue(element == null || element.isValid(), result);

    if (stamp.mayCacheNow()) {
      cache(ref, map, result);
    }
    return result;
  }
  private static Trinity<PsiElement, TextRange, Value> getSelectedExpression(
      final Project project, final Editor editor, final Point point, final ValueHintType type) {
    final Ref<PsiElement> selectedExpression = Ref.create(null);
    final Ref<TextRange> currentRange = Ref.create(null);
    final Ref<Value> preCalculatedValue = Ref.create(null);

    PsiDocumentManager.getInstance(project)
        .commitAndRunReadAction(
            new Runnable() {
              public void run() {
                // Point -> offset
                final int offset = calculateOffset(editor, point);

                PsiFile psiFile =
                    PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());

                if (psiFile == null || !psiFile.isValid()) return;

                int selectionStart = editor.getSelectionModel().getSelectionStart();
                int selectionEnd = editor.getSelectionModel().getSelectionEnd();

                if ((type == ValueHintType.MOUSE_CLICK_HINT
                        || type == ValueHintType.MOUSE_ALT_OVER_HINT)
                    && (selectionStart <= offset && offset <= selectionEnd)) {
                  PsiElement ctx =
                      (selectionStart > 0)
                          ? psiFile.findElementAt(selectionStart - 1)
                          : psiFile.findElementAt(selectionStart);
                  try {
                    String text = editor.getSelectionModel().getSelectedText();
                    if (text != null && ctx != null) {
                      selectedExpression.set(
                          JVMElementFactories.getFactory(ctx.getLanguage(), project)
                              .createExpressionFromText(text, ctx));
                      currentRange.set(
                          new TextRange(
                              editor.getSelectionModel().getSelectionStart(),
                              editor.getSelectionModel().getSelectionEnd()));
                    }
                  } catch (IncorrectOperationException ignored) {
                  }
                }

                if (currentRange.get() == null) {
                  PsiElement elementAtCursor = psiFile.findElementAt(offset);
                  if (elementAtCursor == null) {
                    return;
                  }
                  Pair<PsiElement, TextRange> pair =
                      findExpression(
                          elementAtCursor,
                          type == ValueHintType.MOUSE_CLICK_HINT
                              || type == ValueHintType.MOUSE_ALT_OVER_HINT);
                  if (pair == null) {
                    if (type == ValueHintType.MOUSE_OVER_HINT) {
                      final DebuggerSession debuggerSession =
                          DebuggerManagerEx.getInstanceEx(project)
                              .getContext()
                              .getDebuggerSession();
                      if (debuggerSession != null && debuggerSession.isPaused()) {
                        final Pair<Method, Value> lastExecuted =
                            debuggerSession.getProcess().getLastExecutedMethod();
                        if (lastExecuted != null) {
                          final Method method = lastExecuted.getFirst();
                          if (method != null) {
                            final Pair<PsiElement, TextRange> expressionPair =
                                findExpression(elementAtCursor, true);
                            if (expressionPair != null
                                && expressionPair.getFirst() instanceof PsiMethodCallExpression) {
                              final PsiMethodCallExpression methodCallExpression =
                                  (PsiMethodCallExpression) expressionPair.getFirst();
                              final PsiMethod psiMethod = methodCallExpression.resolveMethod();
                              if (psiMethod != null) {
                                final JVMName jvmSignature = JVMNameUtil.getJVMSignature(psiMethod);
                                try {
                                  if (method.name().equals(psiMethod.getName())
                                      && method
                                          .signature()
                                          .equals(
                                              jvmSignature.getName(debuggerSession.getProcess()))) {
                                    pair = expressionPair;
                                    preCalculatedValue.set(lastExecuted.getSecond());
                                  }
                                } catch (EvaluateException ignored) {
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                  if (pair == null) {
                    return;
                  }
                  selectedExpression.set(pair.getFirst());
                  currentRange.set(pair.getSecond());
                }
              }
            });
    return Trinity.create(selectedExpression.get(), currentRange.get(), preCalculatedValue.get());
  }