Esempio n. 1
0
 private void removeAllContents(Project project, Content notRemove) {
   if (project.isDisposed()) {
     return;
   }
   final MessageView messageView = MessageView.SERVICE.getInstance(project);
   Content[] contents = messageView.getContentManager().getContents();
   for (Content content : contents) {
     if (content.isPinned()) {
       continue;
     }
     if (content == notRemove) {
       continue;
     }
     boolean toRemove = CONTENT_ID_KEY.get(content) == myContentId;
     if (!toRemove) {
       final Object contentSessionId = SESSION_ID_KEY.get(content);
       toRemove =
           contentSessionId != null
               && contentSessionId != mySessionId; // the content was added by previous compilation
     }
     if (toRemove) {
       messageView.getContentManager().removeContent(content, true);
     }
   }
 }
/** @author max */
public class FileContextUtil {
  public static final Key<SmartPsiElementPointer> INJECTED_IN_ELEMENT = Key.create("injectedIn");
  public static final Key<PsiFile> CONTAINING_FILE_KEY = Key.create("CONTAINING_FILE_KEY");

  private FileContextUtil() {}

  @Nullable
  public static PsiElement getFileContext(PsiFile file) {
    SmartPsiElementPointer pointer = file.getUserData(INJECTED_IN_ELEMENT);
    return pointer == null ? null : pointer.getElement();
  }

  @Nullable
  public static PsiFile getContextFile(@NotNull PsiElement element) {
    if (!element.isValid()) return null;
    PsiFile file = element.getContainingFile();
    if (file == null) return null;
    PsiElement context = file.getContext();
    if (context == null) {
      return file;
    } else {
      return getContextFile(context);
    }
  }
}
 @Override
 public void chunkBuildFinished(CompileContext context, ModuleChunk chunk) {
   final InstrumentationClassFinder finder = CLASS_FINDER.get(context);
   if (finder != null) {
     CLASS_FINDER.set(context, null);
     finder.releaseResources();
   }
 }
 @NotNull
 public static StatisticsInfo getBaseStatisticsInfo(
     LookupElement item, @Nullable CompletionLocation location) {
   StatisticsInfo info = BASE_STATISTICS_INFO.get(item);
   if (info == null) {
     if (location == null) {
       return StatisticsInfo.EMPTY;
     }
     BASE_STATISTICS_INFO.set(item, info = calcBaseInfo(item, location));
   }
   return info;
 }
  private static boolean checkChunkRebuildNeeded(
      CompileContext context, GroovycOutputParser parser) {
    if (JavaBuilderUtil.isForcedRecompilationAllJavaModules(context) || !parser.shouldRetry()) {
      return false;
    }

    if (CHUNK_REBUILD_ORDERED.get(context) != null) {
      CHUNK_REBUILD_ORDERED.set(context, null);
      return false;
    }

    CHUNK_REBUILD_ORDERED.set(context, Boolean.TRUE);
    LOG.info("Order chunk rebuild");
    return true;
  }
  @Override
  public void compileAndRun(
      @NotNull final Runnable startRunnable,
      @NotNull final ExecutionEnvironment environment,
      @Nullable final RunProfileState state,
      @Nullable final Runnable onCancelRunnable) {
    long id = environment.getExecutionId();
    if (id == 0) {
      id = environment.assignNewExecutionId();
    }

    RunProfile profile = environment.getRunProfile();
    if (!(profile instanceof RunConfiguration)) {
      startRunnable.run();
      return;
    }

    final RunConfiguration runConfiguration = (RunConfiguration) profile;
    final List<BeforeRunTask> beforeRunTasks =
        RunManagerEx.getInstanceEx(myProject).getBeforeRunTasks(runConfiguration);
    if (beforeRunTasks.isEmpty()) {
      startRunnable.run();
    } else {
      DataContext context = environment.getDataContext();
      final DataContext projectContext =
          context != null ? context : SimpleDataContext.getProjectContext(myProject);
      final long finalId = id;
      final Long executionSessionId = new Long(id);
      ApplicationManager.getApplication()
          .executeOnPooledThread(
              () -> {
                for (BeforeRunTask task : beforeRunTasks) {
                  if (myProject.isDisposed()) {
                    return;
                  }
                  @SuppressWarnings("unchecked")
                  BeforeRunTaskProvider<BeforeRunTask> provider =
                      BeforeRunTaskProvider.getProvider(myProject, task.getProviderId());
                  if (provider == null) {
                    LOG.warn(
                        "Cannot find BeforeRunTaskProvider for id='" + task.getProviderId() + "'");
                    continue;
                  }
                  ExecutionEnvironment taskEnvironment =
                      new ExecutionEnvironmentBuilder(environment).contentToReuse(null).build();
                  taskEnvironment.setExecutionId(finalId);
                  EXECUTION_SESSION_ID_KEY.set(taskEnvironment, executionSessionId);
                  if (!provider.executeTask(
                      projectContext, runConfiguration, taskEnvironment, task)) {
                    if (onCancelRunnable != null) {
                      SwingUtilities.invokeLater(onCancelRunnable);
                    }
                    return;
                  }
                }

                doRun(environment, startRunnable);
              });
    }
  }
 @Override
 public void buildStarted(CompileContext context) {
   final JpsProject project = context.getProjectDescriptor().getProject();
   final JpsJavaCompilerConfiguration config =
       JpsJavaExtensionService.getInstance().getCompilerConfiguration(project);
   final String compilerId = config == null ? JavaCompilers.JAVAC_ID : config.getJavaCompilerId();
   if (LOG.isDebugEnabled()) {
     LOG.debug("Java compiler ID: " + compilerId);
   }
   final boolean isJavac =
       JavaCompilers.JAVAC_ID.equalsIgnoreCase(compilerId)
           || JavaCompilers.JAVAC_API_ID.equalsIgnoreCase(compilerId);
   final boolean isEclipse =
       JavaCompilers.ECLIPSE_ID.equalsIgnoreCase(compilerId)
           || JavaCompilers.ECLIPSE_EMBEDDED_ID.equalsIgnoreCase(compilerId);
   IS_ENABLED.set(context, isJavac || isEclipse);
   String messageText = null;
   if (isJavac) {
     messageText =
         "Using javac " + System.getProperty("java.version") + " to compile java sources";
   } else if (isEclipse) {
     messageText = "Using eclipse compiler to compile java sources";
   }
   COMPILER_VERSION_INFO.set(context, new AtomicReference<String>(messageText));
 }
Esempio n. 8
0
 @NotNull
 @Override
 public Navigatable createNavigatable(@NotNull Project project) {
   if (ALTERNATIVE_SOURCE_KEY.get(myFile) != null) {
     return new OpenFileDescriptor(project, getFile(), getLine(), 0);
   }
   return XSourcePositionImpl.doCreateOpenFileDescriptor(project, this);
 }
public abstract class RefJavaManager implements RefManagerExtension<RefJavaManager> {
  @NonNls public static final String CLASS = "class";
  @NonNls public static final String METHOD = "method";
  @NonNls public static final String IMPLICIT_CONSTRUCTOR = "implicit.constructor";
  @NonNls public static final String FIELD = "field";
  @NonNls public static final String PARAMETER = "parameter";
  @NonNls public static final String JAVA_MODULE = "java.module";
  // used in OfflineProjectDescriptor
  @NonNls public static final String PACKAGE = "package";
  public static final Key<RefJavaManager> MANAGER = Key.create("RefJavaManager");

  public abstract RefImplicitConstructor getImplicitConstructor(String classFQName);

  /**
   * Creates (if necessary) and returns the reference graph node for the package with the specified
   * name.
   *
   * @param packageName the name of the package for which the reference graph node is requested.
   * @return the node for the package.
   */
  public abstract RefPackage getPackage(String packageName);

  /**
   * Creates (if necessary) and returns the reference graph node for the specified PSI parameter.
   *
   * @param param the parameter for which the reference graph node is requested.
   * @param index the index of the parameter in its parameter list.
   * @return the node for the element, or null if the element is not valid or does not have a
   *     corresponding reference graph node type (is not a field, method, class or file).
   */
  public abstract RefParameter getParameterReference(PsiParameter param, int index);

  public abstract RefPackage getDefaultPackage();

  public abstract PsiMethod getAppMainPattern();

  public abstract PsiMethod getAppPremainPattern();

  public abstract PsiMethod getAppAgentmainPattern();

  public abstract PsiClass getApplet();

  public abstract PsiClass getServlet();

  public abstract EntryPointsManager getEntryPointsManager();

  @NotNull
  @Override
  public Language getLanguage() {
    return JavaLanguage.INSTANCE;
  }

  @NotNull
  @Override
  public Key<RefJavaManager> getID() {
    return MANAGER;
  }
}
Esempio n. 10
0
 @NotNull
 @Override
 public VirtualFile getFile() {
   VirtualFile file = ALTERNATIVE_SOURCE_KEY.get(myFile);
   if (file != null) {
     return file;
   }
   return myFile;
 }
  private static int getItemToSelect(
      LookupImpl lookup,
      List<LookupElement> items,
      boolean onExplicitAction,
      @Nullable LookupElement mostRelevant) {
    if (items.isEmpty() || lookup.getFocusDegree() == LookupImpl.FocusDegree.UNFOCUSED) {
      return 0;
    }

    if (lookup.isSelectionTouched() || !onExplicitAction) {
      final LookupElement lastSelection = lookup.getCurrentItem();
      int old = ContainerUtil.indexOfIdentity(items, lastSelection);
      if (old >= 0) {
        return old;
      }

      Object selectedValue = ((LookupImpl) lookup).getList().getSelectedValue();
      if (selectedValue instanceof EmptyLookupItem
          && ((EmptyLookupItem) selectedValue).isLoading()) {
        int index = ((LookupImpl) lookup).getList().getSelectedIndex();
        if (index >= 0 && index < items.size()) {
          return index;
        }
      }

      for (int i = 0; i < items.size(); i++) {
        String invariant = PRESENTATION_INVARIANT.get(items.get(i));
        if (invariant != null && invariant.equals(PRESENTATION_INVARIANT.get(lastSelection))) {
          return i;
        }
      }
    }

    String selectedText = lookup.getEditor().getSelectionModel().getSelectedText();
    for (int i = 0; i < items.size(); i++) {
      LookupElement item = items.get(i);
      if (isPrefixItem(lookup, item, true) && !isLiveTemplate(item)
          || item.getLookupString().equals(selectedText)) {
        return i;
      }
    }

    return Math.max(0, ContainerUtil.indexOfIdentity(items, mostRelevant));
  }
 private static int getCompilerSdkVersion(CompileContext context) {
   final Integer cached = JAVA_COMPILER_VERSION_KEY.get(context);
   if (cached != null) {
     return cached;
   }
   int javaVersion = convertToNumber(SystemProperties.getJavaVersion());
   if (!USE_EMBEDDED_JAVAC) {
     // in case of external javac, run compiler from the newest jdk that is used in the project
     for (JpsSdk<?> sdk : context.getProjectDescriptor().getProjectJavaSdks()) {
       final String version = sdk.getVersionString();
       final int ver = convertToNumber(version);
       if (ver > javaVersion) {
         javaVersion = ver;
       }
     }
   }
   JAVA_COMPILER_VERSION_KEY.set(context, javaVersion);
   return javaVersion;
 }
  public ExitCode build(
      final CompileContext context,
      final ModuleChunk chunk,
      DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
      OutputConsumer outputConsumer)
      throws ProjectBuildException {
    if (!IS_ENABLED.get(context, Boolean.TRUE)) {
      return ExitCode.NOTHING_DONE;
    }
    try {
      final Map<File, ModuleBuildTarget> filesToCompile =
          new THashMap<File, ModuleBuildTarget>(FileUtil.FILE_HASHING_STRATEGY);

      dirtyFilesHolder.processDirtyFiles(
          new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
            public boolean apply(
                ModuleBuildTarget target, File file, JavaSourceRootDescriptor descriptor)
                throws IOException {
              if (JAVA_SOURCES_FILTER.accept(file)) {
                filesToCompile.put(file, target);
              }
              return true;
            }
          });

      if (context.isMake()) {
        final ProjectBuilderLogger logger = context.getLoggingManager().getProjectBuilderLogger();
        if (logger.isEnabled()) {
          if (filesToCompile.size() > 0) {
            logger.logCompiledFiles(filesToCompile.keySet(), BUILDER_NAME, "Compiling files:");
          }
        }
      }

      return compile(context, chunk, dirtyFilesHolder, filesToCompile.keySet(), outputConsumer);
    } catch (ProjectBuildException e) {
      throw e;
    } catch (Exception e) {
      String message = e.getMessage();
      if (message == null) {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        final PrintStream stream = new PrintStream(out);
        try {
          e.printStackTrace(stream);
        } finally {
          stream.close();
        }
        message = "Internal error: \n" + out.toString();
      }
      context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, message));
      throw new ProjectBuildException(message, e);
    }
  }
Esempio n. 14
0
 public static void deleteTypeAnnotations(@NotNull PsiTypeElement typeElement) {
   PsiElement left =
       PsiTreeUtil.skipSiblingsBackward(
           typeElement, PsiComment.class, PsiWhiteSpace.class, PsiTypeParameterList.class);
   if (left instanceof PsiModifierList) {
     for (PsiAnnotation annotation : ((PsiModifierList) left).getAnnotations()) {
       if (TYPE_ANNO_MARK.get(annotation) == Boolean.TRUE) {
         annotation.delete();
       }
     }
   }
 }
 public void setBreakpointDefaults(
     Key<? extends Breakpoint> category, BreakpointDefaults defaults) {
   Class typeCls = null;
   if (LineBreakpoint.CATEGORY.toString().equals(category.toString())) {
     typeCls = JavaLineBreakpointType.class;
   } else if (MethodBreakpoint.CATEGORY.toString().equals(category.toString())) {
     typeCls = JavaMethodBreakpointType.class;
   } else if (FieldBreakpoint.CATEGORY.toString().equals(category.toString())) {
     typeCls = JavaFieldBreakpointType.class;
   } else if (ExceptionBreakpoint.CATEGORY.toString().equals(category.toString())) {
     typeCls = JavaExceptionBreakpointType.class;
   }
   if (typeCls != null) {
     XBreakpointType<XBreakpoint<?>, ?> type =
         XDebuggerUtil.getInstance().findBreakpointType(typeCls);
     ((XBreakpointManagerImpl) getXBreakpointManager())
         .getBreakpointDefaults(type)
         .setSuspendPolicy(Breakpoint.transformSuspendPolicy(defaults.getSuspendPolicy()));
   }
   // myBreakpointDefaults.put(category, defaults);
 }
 private static <T extends Breakpoint> Element getCategoryGroupElement(
     @NotNull final Map<Key<? extends Breakpoint>, Element> categoryToElementMap,
     @NotNull final Key<T> category,
     @NotNull final Element parentNode) {
   Element group = categoryToElementMap.get(category);
   if (group == null) {
     group = new Element(category.toString());
     categoryToElementMap.put(category, group);
     parentNode.addContent(group);
   }
   return group;
 }
Esempio n. 17
0
  // error tree view initialization must be invoked from event dispatch thread
  private void openMessageView() {
    if (isHeadlessMode()) {
      return;
    }
    if (myIndicator.isCanceled()) {
      return;
    }

    final JComponent component;
    synchronized (myMessageViewLock) {
      if (myErrorTreeView != null) {
        return;
      }
      myErrorTreeView = new CompilerErrorTreeView(myProject, myRestartWork);

      myErrorTreeView.setProcessController(
          new NewErrorTreeViewPanel.ProcessController() {
            @Override
            public void stopProcess() {
              cancel();
            }

            @Override
            public boolean isProcessStopped() {
              return !myIndicator.isRunning();
            }
          });
      component = myErrorTreeView.getComponent();
    }

    final MessageView messageView = MessageView.SERVICE.getInstance(myProject);
    final Content content =
        ContentFactory.SERVICE.getInstance().createContent(component, myContentName, true);
    CONTENT_ID_KEY.set(content, myContentId);
    SESSION_ID_KEY.set(content, mySessionId);
    messageView.getContentManager().addContent(content);
    myCloseListener.setContent(content, messageView.getContentManager());
    removeAllContents(myProject, content);
    messageView.getContentManager().setSelectedContent(content);
  }
  @Override
  public final ExitCode build(
      CompileContext context,
      ModuleChunk chunk,
      DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
      OutputConsumer outputConsumer)
      throws ProjectBuildException, IOException {
    if (outputConsumer.getCompiledClasses().isEmpty() || !isEnabled(context, chunk)) {
      return ExitCode.NOTHING_DONE;
    }

    final String progress = getProgressMessage();
    final boolean shouldShowProgress = !StringUtil.isEmptyOrSpaces(progress);
    if (shouldShowProgress) {
      context.processMessage(new ProgressMessage(progress + " [" + chunk.getName() + "]"));
    }

    ExitCode exitCode = ExitCode.NOTHING_DONE;
    try {
      InstrumentationClassFinder finder = CLASS_FINDER.get(context); // try using shared finder
      if (finder == null) {
        final Collection<File> platformCp =
            ProjectPaths.getPlatformCompilationClasspath(chunk, false);
        final Collection<File> classpath = new ArrayList<File>();
        classpath.addAll(ProjectPaths.getCompilationClasspath(chunk, false));
        classpath.addAll(ProjectPaths.getSourceRootsWithDependents(chunk).keySet());

        finder = createInstrumentationClassFinder(platformCp, classpath, outputConsumer);
        CLASS_FINDER.set(context, finder);
      }

      exitCode = performBuild(context, chunk, finder, outputConsumer);
    } finally {
      if (shouldShowProgress) {
        context.processMessage(new ProgressMessage("")); // cleanup progress
      }
    }
    return exitCode;
  }
  /**
   * @return breakpoints of one of the category: LINE_BREAKPOINTS, EXCEPTION_BREAKPOINTS,
   *     FIELD_BREAKPOINTS, METHOD_BREAKPOINTS
   */
  public <T extends Breakpoint> Breakpoint[] getBreakpoints(@NotNull final Key<T> category) {
    ApplicationManager.getApplication().assertIsDispatchThread();
    removeInvalidBreakpoints();

    final ArrayList<Breakpoint> breakpoints = new ArrayList<Breakpoint>();
    for (Breakpoint breakpoint : getBreakpoints()) {
      if (category.equals(breakpoint.getCategory())) {
        breakpoints.add(breakpoint);
      }
    }

    return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
  }
Esempio n. 20
0
 public void showCompilerContent() {
   synchronized (myMessageViewLock) {
     if (myErrorTreeView != null) {
       final MessageView messageView = MessageView.SERVICE.getInstance(myProject);
       Content[] contents = messageView.getContentManager().getContents();
       for (Content content : contents) {
         if (CONTENT_ID_KEY.get(content) == myContentId) {
           messageView.getContentManager().setSelectedContent(content);
           return;
         }
       }
     }
   }
 }
 /** @param category breakpoint category, null if the category does not matter */
 @Nullable
 public <T extends BreakpointWithHighlighter> T findBreakpoint(
     final Document document, final int offset, @Nullable final Key<T> category) {
   for (final Breakpoint breakpoint : getBreakpoints()) {
     if (breakpoint instanceof BreakpointWithHighlighter
         && ((BreakpointWithHighlighter) breakpoint).isAt(document, offset)) {
       if (category == null || category.equals(breakpoint.getCategory())) {
         //noinspection CastConflictsWithInstanceof,unchecked
         return (T) breakpoint;
       }
     }
   }
   return null;
 }
/**
 * @author Dmitry Avdeev
 * @author peter
 */
public class PerIndexDocumentVersionMap {
  private static final int INVALID_STAMP =
      -1; // 0 isn't acceptable as Document has 0 stamp when loaded from unchanged file
  private volatile int mapVersion;

  private static class IdVersionInfo {
    private final ID<?, ?> id;
    private int mapVersion;
    private long docVersion;

    private IdVersionInfo(@NotNull ID<?, ?> id, long docVersion, int mapVersion) {
      this.docVersion = docVersion;
      this.mapVersion = mapVersion;
      this.id = id;
    }
  }

  private static final Key<List<IdVersionInfo>> KEY = Key.create("UnsavedDocIdVersionInfo");

  public long getAndSet(@NotNull Document document, @NotNull ID<?, ?> indexId, long value) {
    List<IdVersionInfo> list = document.getUserData(KEY);
    if (list == null) {
      list = ((UserDataHolderEx) document).putUserDataIfAbsent(KEY, new ArrayList<IdVersionInfo>());
    }

    synchronized (list) {
      for (IdVersionInfo info : list) {
        if (info.id == indexId) {
          long old = info.docVersion;
          if (info.mapVersion != mapVersion) {
            old = INVALID_STAMP;
            info.mapVersion = mapVersion;
          }
          info.docVersion = value;
          return old;
        }
      }
      list.add(new IdVersionInfo(indexId, value, mapVersion));
      return INVALID_STAMP;
    }
  }

  public void clear() {
    mapVersion++;
  }
}
Esempio n. 23
0
public interface IGosuMethod
    extends IGosuMembersDeclaration,
        PsiMethod,
        IGosuNamedElement,
        IGosuMember,
        IGosuParametersOwner,
        IGosuTypeParameterListOwner {
  Key<Boolean> BUILDER_METHOD = Key.create("BUILDER_METHOD");

  IGosuMethod[] EMPTY_ARRAY = new IGosuMethod[0];

  ArrayFactory<IGosuMethod> ARRAY_FACTORY =
      new ArrayFactory<IGosuMethod>() {
        @NotNull
        public IGosuMethod[] create(int count) {
          return count == 0 ? EMPTY_ARRAY : new IGosuMethod[count];
        }
      };

  IGosuTypeElement getReturnTypeElementGosu();

  /** @return the static return type, which will appear in the compiled Gosu class */
  @Nullable
  PsiType getReturnType();

  @Nullable
  IGosuTypeElement setReturnType(PsiType newReturnType);

  @NotNull
  String getName();

  @NotNull
  IGosuParameterList getParameterList();

  @NotNull
  IGosuModifierList getModifierList();

  String[] getNamedParametersArray();

  boolean isForProperty();

  boolean isForPropertySetter();

  boolean isForPropertyGetter();
}
public abstract class HTMLJavaHTMLComposer implements HTMLComposerExtension<HTMLJavaHTMLComposer> {
  public static final Key<HTMLJavaHTMLComposer> COMPOSER = Key.create("HTMLJavaComposer");

  public abstract void appendClassOrInterface(
      StringBuffer buf, RefClass refClass, boolean capitalizeFirstLetter);

  public static String getClassOrInterface(RefClass refClass, boolean capitalizeFirstLetter) {
    if (refClass.isInterface()) {
      return capitalizeFirstLetter
          ? InspectionsBundle.message("inspection.export.results.capitalized.interface")
          : InspectionsBundle.message("inspection.export.results.interface");
    } else if (refClass.isAbstract()) {
      return capitalizeFirstLetter
          ? InspectionsBundle.message("inspection.export.results.capitalized.abstract.class")
          : InspectionsBundle.message("inspection.export.results.abstract.class");
    } else {
      return capitalizeFirstLetter
          ? InspectionsBundle.message("inspection.export.results.capitalized.class")
          : InspectionsBundle.message("inspection.export.results.class");
    }
  }

  public abstract void appendClassExtendsImplements(StringBuffer buf, RefClass refClass);

  public abstract void appendDerivedClasses(StringBuffer buf, RefClass refClass);

  public abstract void appendLibraryMethods(StringBuffer buf, RefClass refClass);

  public abstract void appendSuperMethods(StringBuffer buf, RefMethod refMethod);

  public abstract void appendDerivedMethods(StringBuffer buf, RefMethod refMethod);

  public abstract void appendTypeReferences(StringBuffer buf, RefClass refClass);

  public Key<HTMLJavaHTMLComposer> getID() {
    return COMPOSER;
  }

  public Language getLanguage() {
    return StdLanguages.JAVA;
  }
}
  private static final class CompileStatistics {
    private static final Key<CompileStatistics> KEY = Key.create("_Compile_Statistics_");
    private int myClassesCount;
    private int myFilesCount;

    public int getClassesCount() {
      return myClassesCount;
    }

    public int incClassesCount() {
      return ++myClassesCount;
    }

    public int getFilesCount() {
      return myFilesCount;
    }

    public int incFilesCount() {
      return ++myFilesCount;
    }
  }
 public void process(CompileContext context, OutputFileObject out) {
   Map<String, String> stubToSrc = STUB_TO_SRC.get(context);
   if (stubToSrc == null) {
     return;
   }
   File src = out.getSourceFile();
   if (src == null) {
     return;
   }
   String groovy = stubToSrc.get(FileUtil.toSystemIndependentName(src.getPath()));
   if (groovy == null) {
     return;
   }
   try {
     final File groovyFile = new File(groovy);
     if (!FSOperations.isMarkedDirty(context, CompilationRound.CURRENT, groovyFile)) {
       FSOperations.markDirty(context, CompilationRound.NEXT, groovyFile);
       FILES_MARKED_DIRTY_FOR_NEXT_ROUND.set(context, Boolean.TRUE);
     }
   } catch (IOException e) {
     LOG.error(e);
   }
 }
Esempio n. 27
0
public class IndentsPass extends TextEditorHighlightingPass implements DumbAware {
  private static final ConcurrentMap<IElementType, String> COMMENT_PREFIXES =
      ContainerUtil.newConcurrentMap();
  private static final String NO_COMMENT_INFO_MARKER =
      "hopefully, noone uses this string as a comment prefix";

  private static final Key<List<RangeHighlighter>> INDENT_HIGHLIGHTERS_IN_EDITOR_KEY =
      Key.create("INDENT_HIGHLIGHTERS_IN_EDITOR_KEY");
  private static final Key<Long> LAST_TIME_INDENTS_BUILT = Key.create("LAST_TIME_INDENTS_BUILT");

  private final EditorEx myEditor;
  private final PsiFile myFile;
  public static final Comparator<TextRange> RANGE_COMPARATOR =
      new Comparator<TextRange>() {
        @Override
        public int compare(TextRange o1, TextRange o2) {
          if (o1.getStartOffset() == o2.getStartOffset()) {
            return o1.getEndOffset() - o2.getEndOffset();
          }

          return o1.getStartOffset() - o2.getStartOffset();
        }
      };

  private static final CustomHighlighterRenderer RENDERER =
      new CustomHighlighterRenderer() {
        @Override
        @SuppressWarnings({"AssignmentToForLoopParameter"})
        public void paint(
            @NotNull Editor editor, @NotNull RangeHighlighter highlighter, @NotNull Graphics g) {
          int startOffset = highlighter.getStartOffset();
          final Document doc = highlighter.getDocument();
          if (startOffset >= doc.getTextLength()) return;

          final int endOffset = highlighter.getEndOffset();
          final int endLine = doc.getLineNumber(endOffset);

          int off;
          int startLine = doc.getLineNumber(startOffset);
          IndentGuideDescriptor descriptor =
              editor.getIndentsModel().getDescriptor(startLine, endLine);

          final CharSequence chars = doc.getCharsSequence();
          do {
            int start = doc.getLineStartOffset(startLine);
            int end = doc.getLineEndOffset(startLine);
            off = CharArrayUtil.shiftForward(chars, start, end, " \t");
            startLine--;
          } while (startLine > 1 && off < doc.getTextLength() && chars.charAt(off) == '\n');

          final VisualPosition startPosition = editor.offsetToVisualPosition(off);
          int indentColumn = startPosition.column;

          // It's considered that indent guide can cross not only white space but comments, javadocs
          // etc. Hence, there is a possible
          // case that the first indent guide line is, say, single-line comment where comment
          // symbols ('//') are located at the first
          // visual column. We need to calculate correct indent guide column then.
          int lineShift = 1;
          if (indentColumn <= 0 && descriptor != null) {
            indentColumn = descriptor.indentLevel;
            lineShift = 0;
          }
          if (indentColumn <= 0) return;

          final FoldingModel foldingModel = editor.getFoldingModel();
          if (foldingModel.isOffsetCollapsed(off)) return;

          final FoldRegion headerRegion =
              foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(doc.getLineNumber(off)));
          final FoldRegion tailRegion =
              foldingModel.getCollapsedRegionAtOffset(
                  doc.getLineStartOffset(doc.getLineNumber(endOffset)));

          if (tailRegion != null && tailRegion == headerRegion) return;

          final boolean selected;
          final IndentGuideDescriptor guide = editor.getIndentsModel().getCaretIndentGuide();
          if (guide != null) {
            final CaretModel caretModel = editor.getCaretModel();
            final int caretOffset = caretModel.getOffset();
            selected =
                caretOffset >= off
                    && caretOffset < endOffset
                    && caretModel.getLogicalPosition().column == indentColumn;
          } else {
            selected = false;
          }

          Point start =
              editor.visualPositionToXY(
                  new VisualPosition(startPosition.line + lineShift, indentColumn));
          final VisualPosition endPosition = editor.offsetToVisualPosition(endOffset);
          Point end =
              editor.visualPositionToXY(new VisualPosition(endPosition.line, endPosition.column));
          int maxY = end.y;
          if (endPosition.line == editor.offsetToVisualPosition(doc.getTextLength()).line) {
            maxY += editor.getLineHeight();
          }

          Rectangle clip = g.getClipBounds();
          if (clip != null) {
            if (clip.y >= maxY || clip.y + clip.height <= start.y) {
              return;
            }
            maxY = Math.min(maxY, clip.y + clip.height);
          }

          final EditorColorsScheme scheme = editor.getColorsScheme();
          g.setColor(
              selected
                  ? scheme.getColor(EditorColors.SELECTED_INDENT_GUIDE_COLOR)
                  : scheme.getColor(EditorColors.INDENT_GUIDE_COLOR));

          // There is a possible case that indent line intersects soft wrap-introduced text.
          // Example:
          //     this is a long line <soft-wrap>
          // that| is soft-wrapped
          //     |
          //     | <- vertical indent
          //
          // Also it's possible that no additional intersections are added because of soft wrap:
          //     this is a long line <soft-wrap>
          //     |   that is soft-wrapped
          //     |
          //     | <- vertical indent
          // We want to use the following approach then:
          //     1. Show only active indent if it crosses soft wrap-introduced text;
          //     2. Show indent as is if it doesn't intersect with soft wrap-introduced text;
          if (selected) {
            g.drawLine(start.x + 2, start.y, start.x + 2, maxY);
          } else {
            int y = start.y;
            int newY = start.y;
            SoftWrapModel softWrapModel = editor.getSoftWrapModel();
            int lineHeight = editor.getLineHeight();
            for (int i = Math.max(0, startLine + lineShift); i < endLine && newY < maxY; i++) {
              List<? extends SoftWrap> softWraps = softWrapModel.getSoftWrapsForLine(i);
              int logicalLineHeight = softWraps.size() * lineHeight;
              if (i > startLine + lineShift) {
                logicalLineHeight +=
                    lineHeight; // We assume that initial 'y' value points just below the target
                // line.
              }
              if (!softWraps.isEmpty() && softWraps.get(0).getIndentInColumns() < indentColumn) {
                if (y < newY
                    || i
                        > startLine
                            + lineShift) { // There is a possible case that soft wrap is located on
                  // indent start line.
                  g.drawLine(start.x + 2, y, start.x + 2, newY + lineHeight);
                }
                newY += logicalLineHeight;
                y = newY;
              } else {
                newY += logicalLineHeight;
              }

              FoldRegion foldRegion =
                  foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(i));
              if (foldRegion != null && foldRegion.getEndOffset() < doc.getTextLength()) {
                i = doc.getLineNumber(foldRegion.getEndOffset());
              }
            }

            if (y < maxY) {
              g.drawLine(start.x + 2, y, start.x + 2, maxY);
            }
          }
        }
      };
  private volatile List<TextRange> myRanges;
  private volatile List<IndentGuideDescriptor> myDescriptors;

  public IndentsPass(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
    super(project, editor.getDocument(), false);
    myEditor = (EditorEx) editor;
    myFile = file;
  }

  @Override
  public void doCollectInformation(@NotNull ProgressIndicator progress) {
    assert myDocument != null;
    final Long stamp = myEditor.getUserData(LAST_TIME_INDENTS_BUILT);
    if (stamp != null && stamp.longValue() == nowStamp()) return;

    myDescriptors = buildDescriptors();

    ArrayList<TextRange> ranges = new ArrayList<TextRange>();
    for (IndentGuideDescriptor descriptor : myDescriptors) {
      ProgressManager.checkCanceled();
      int endOffset =
          descriptor.endLine < myDocument.getLineCount()
              ? myDocument.getLineStartOffset(descriptor.endLine)
              : myDocument.getTextLength();
      ranges.add(new TextRange(myDocument.getLineStartOffset(descriptor.startLine), endOffset));
    }

    Collections.sort(ranges, RANGE_COMPARATOR);
    myRanges = ranges;
  }

  private long nowStamp() {
    if (!myEditor.getSettings().isIndentGuidesShown()) return -1;
    assert myDocument != null;
    return myDocument.getModificationStamp();
  }

  @Override
  public void doApplyInformationToEditor() {
    final Long stamp = myEditor.getUserData(LAST_TIME_INDENTS_BUILT);
    if (stamp != null && stamp.longValue() == nowStamp()) return;

    List<RangeHighlighter> oldHighlighters =
        myEditor.getUserData(INDENT_HIGHLIGHTERS_IN_EDITOR_KEY);
    final List<RangeHighlighter> newHighlighters = new ArrayList<RangeHighlighter>();
    final MarkupModel mm = myEditor.getMarkupModel();

    int curRange = 0;

    if (oldHighlighters != null) {
      int curHighlight = 0;
      while (curRange < myRanges.size() && curHighlight < oldHighlighters.size()) {
        TextRange range = myRanges.get(curRange);
        RangeHighlighter highlighter = oldHighlighters.get(curHighlight);

        int cmp = compare(range, highlighter);
        if (cmp < 0) {
          newHighlighters.add(createHighlighter(mm, range));
          curRange++;
        } else if (cmp > 0) {
          highlighter.dispose();
          curHighlight++;
        } else {
          newHighlighters.add(highlighter);
          curHighlight++;
          curRange++;
        }
      }

      for (; curHighlight < oldHighlighters.size(); curHighlight++) {
        RangeHighlighter highlighter = oldHighlighters.get(curHighlight);
        highlighter.dispose();
      }
    }

    final int startRangeIndex = curRange;
    assert myDocument != null;
    DocumentUtil.executeInBulk(
        myDocument,
        myRanges.size() > 10000,
        new Runnable() {
          @Override
          public void run() {
            for (int i = startRangeIndex; i < myRanges.size(); i++) {
              newHighlighters.add(createHighlighter(mm, myRanges.get(i)));
            }
          }
        });

    myEditor.putUserData(INDENT_HIGHLIGHTERS_IN_EDITOR_KEY, newHighlighters);
    myEditor.putUserData(LAST_TIME_INDENTS_BUILT, nowStamp());
    myEditor.getIndentsModel().assumeIndents(myDescriptors);
  }

  private List<IndentGuideDescriptor> buildDescriptors() {
    if (!myEditor.getSettings().isIndentGuidesShown()) return Collections.emptyList();

    IndentsCalculator calculator = new IndentsCalculator();
    calculator.calculate();
    int[] lineIndents = calculator.lineIndents;
    TIntIntHashMap effectiveCommentColumns = calculator.indentAfterUncomment;

    List<IndentGuideDescriptor> descriptors = new ArrayList<IndentGuideDescriptor>();

    IntStack lines = new IntStack();
    IntStack indents = new IntStack();

    lines.push(0);
    indents.push(0);
    assert myDocument != null;
    final CharSequence chars = myDocument.getCharsSequence();
    for (int line = 1; line < lineIndents.length; line++) {
      ProgressManager.checkCanceled();
      int curIndent = lineIndents[line];

      while (!indents.empty() && curIndent <= indents.peek()) {
        ProgressManager.checkCanceled();
        final int level = indents.pop();
        int startLine = lines.pop();
        if (level > 0) {
          boolean addDescriptor =
              effectiveCommentColumns.contains(startLine); // Indent started at comment
          if (!addDescriptor) {
            for (int i = startLine; i < line; i++) {
              if (level != lineIndents[i] && level != effectiveCommentColumns.get(i)) {
                addDescriptor = true;
                break;
              }
            }
          }
          if (addDescriptor) {
            descriptors.add(createDescriptor(level, startLine, line, chars));
          }
        }
      }

      int prevLine = line - 1;
      int prevIndent = lineIndents[prevLine];

      if (curIndent - prevIndent > 1) {
        lines.push(prevLine);
        indents.push(prevIndent);
      }
    }

    while (!indents.empty()) {
      ProgressManager.checkCanceled();
      final int level = indents.pop();
      int startLine = lines.pop();
      if (level > 0) {
        descriptors.add(createDescriptor(level, startLine, myDocument.getLineCount(), chars));
      }
    }
    return descriptors;
  }

  private IndentGuideDescriptor createDescriptor(
      int level, int startLine, int endLine, CharSequence chars) {
    while (startLine > 0 && isBlankLine(startLine, chars)) startLine--;
    return new IndentGuideDescriptor(level, startLine, endLine);
  }

  private boolean isBlankLine(int line, CharSequence chars) {
    Document document = myDocument;
    if (document == null) {
      return true;
    }
    int startOffset = document.getLineStartOffset(line);
    int endOffset = document.getLineEndOffset(line);
    return CharArrayUtil.shiftForward(chars, startOffset, endOffset, " \t")
        >= myDocument.getLineEndOffset(line);
  }

  /**
   * We want to treat comments specially in a way to skip comment prefix on line indent calculation.
   *
   * <p>Example:
   *
   * <pre>
   *   if (true) {
   *     int i1;
   * //    int i2;
   *     int i3;
   *   }
   * </pre>
   *
   * We want to use 'int i2;' start offset as the third line indent (though it has non-white space
   * comment prefix (//) at the first column.
   *
   * <p>This method tries to parse comment prefix for the language implied by the given comment
   * type. It uses {@link #NO_COMMENT_INFO_MARKER} as an indicator that that information is
   * unavailable
   *
   * @param commentType target comment type
   * @return prefix of the comment denoted by the given type if any; {@link #NO_COMMENT_INFO_MARKER}
   *     otherwise
   */
  @NotNull
  private static String getCommentPrefix(@NotNull IElementType commentType) {
    Commenter c = LanguageCommenters.INSTANCE.forLanguage(commentType.getLanguage());
    if (!(c instanceof CodeDocumentationAwareCommenter)) {
      COMMENT_PREFIXES.put(commentType, NO_COMMENT_INFO_MARKER);
      return NO_COMMENT_INFO_MARKER;
    }
    CodeDocumentationAwareCommenter commenter = (CodeDocumentationAwareCommenter) c;

    IElementType lineCommentType = commenter.getLineCommentTokenType();
    String lineCommentPrefix = commenter.getLineCommentPrefix();
    if (lineCommentType != null) {
      COMMENT_PREFIXES.put(
          lineCommentType, lineCommentPrefix == null ? NO_COMMENT_INFO_MARKER : lineCommentPrefix);
    }

    IElementType blockCommentType = commenter.getBlockCommentTokenType();
    String blockCommentPrefix = commenter.getBlockCommentPrefix();
    if (blockCommentType != null) {
      COMMENT_PREFIXES.put(
          blockCommentType,
          blockCommentPrefix == null ? NO_COMMENT_INFO_MARKER : blockCommentPrefix);
    }

    IElementType docCommentType = commenter.getDocumentationCommentTokenType();
    String docCommentPrefix = commenter.getDocumentationCommentPrefix();
    if (docCommentType != null) {
      COMMENT_PREFIXES.put(
          docCommentType, docCommentPrefix == null ? NO_COMMENT_INFO_MARKER : docCommentPrefix);
    }

    COMMENT_PREFIXES.putIfAbsent(commentType, NO_COMMENT_INFO_MARKER);
    return COMMENT_PREFIXES.get(commentType);
  }

  @NotNull
  private static RangeHighlighter createHighlighter(MarkupModel mm, TextRange range) {
    final RangeHighlighter highlighter =
        mm.addRangeHighlighter(
            range.getStartOffset(),
            range.getEndOffset(),
            0,
            null,
            HighlighterTargetArea.EXACT_RANGE);
    highlighter.setCustomRenderer(RENDERER);
    return highlighter;
  }

  private static int compare(@NotNull TextRange r, @NotNull RangeHighlighter h) {
    int answer = r.getStartOffset() - h.getStartOffset();
    return answer != 0 ? answer : r.getEndOffset() - h.getEndOffset();
  }

  private class IndentsCalculator {

    @NotNull public final Map<Language, TokenSet> myComments = ContainerUtilRt.newHashMap();

    /**
     * We need to treat specially commented lines. Consider a situation like below:
     *
     * <pre>
     *   void test() {
     *     if (true) {
     *       int i;
     *  //     int j;
     *     }
     *   }
     * </pre>
     *
     * We don't want to show indent guide after 'int i;' line because un-commented line below ('int
     * j;') would have the same indent level. That's why we remember 'indents after un-comment' at
     * this collection.
     */
    @NotNull
    public final TIntIntHashMap /* line -> indent column after un-comment */ indentAfterUncomment =
        new TIntIntHashMap();

    @NotNull public final int[] lineIndents;
    @NotNull public final CharSequence myChars;

    IndentsCalculator() {
      assert myDocument != null;
      lineIndents = new int[myDocument.getLineCount()];
      myChars = myDocument.getCharsSequence();
    }

    /** Calculates line indents for the {@link #myDocument target document}. */
    void calculate() {
      final FileType fileType = myFile.getFileType();
      int prevLineIndent = -1;

      for (int line = 0; line < lineIndents.length; line++) {
        ProgressManager.checkCanceled();
        int lineStart = myDocument.getLineStartOffset(line);
        int lineEnd = myDocument.getLineEndOffset(line);
        final int nonWhitespaceOffset =
            CharArrayUtil.shiftForward(myChars, lineStart, lineEnd, " \t");
        if (nonWhitespaceOffset == lineEnd) {
          lineIndents[line] = -1; // Blank line marker
        } else {
          final int column =
              ((EditorImpl) myEditor).calcColumnNumber(nonWhitespaceOffset, line, true, myChars);
          if (prevLineIndent > 0 && prevLineIndent > column) {
            lineIndents[line] = calcIndent(line, nonWhitespaceOffset, lineEnd, column);
          } else {
            lineIndents[line] = column;
          }
          prevLineIndent = lineIndents[line];
        }
      }

      int topIndent = 0;
      for (int line = 0; line < lineIndents.length; line++) {
        ProgressManager.checkCanceled();
        if (lineIndents[line] >= 0) {
          topIndent = lineIndents[line];
        } else {
          int startLine = line;
          while (line < lineIndents.length && lineIndents[line] < 0) {
            //noinspection AssignmentToForLoopParameter
            line++;
          }

          int bottomIndent = line < lineIndents.length ? lineIndents[line] : topIndent;

          int indent = Math.min(topIndent, bottomIndent);
          if (bottomIndent < topIndent) {
            int lineStart = myDocument.getLineStartOffset(line);
            int lineEnd = myDocument.getLineEndOffset(line);
            int nonWhitespaceOffset =
                CharArrayUtil.shiftForward(myChars, lineStart, lineEnd, " \t");
            HighlighterIterator iterator =
                myEditor.getHighlighter().createIterator(nonWhitespaceOffset);
            if (BraceMatchingUtil.isRBraceToken(iterator, myChars, fileType)) {
              indent = topIndent;
            }
          }

          for (int blankLine = startLine; blankLine < line; blankLine++) {
            assert lineIndents[blankLine] == -1;
            lineIndents[blankLine] = Math.min(topIndent, indent);
          }

          //noinspection AssignmentToForLoopParameter
          line--; // will be incremented back at the end of the loop;
        }
      }
    }

    /**
     * Tries to calculate given line's indent column assuming that there might be a comment at the
     * given indent offset (see {@link #getCommentPrefix(IElementType)}).
     *
     * @param line target line
     * @param indentOffset start indent offset to use for the given line
     * @param lineEndOffset given line's end offset
     * @param fallbackColumn column to return if it's not possible to apply comment-specific indent
     *     calculation rules
     * @return given line's indent column to use
     */
    private int calcIndent(int line, int indentOffset, int lineEndOffset, int fallbackColumn) {
      final HighlighterIterator it = myEditor.getHighlighter().createIterator(indentOffset);
      IElementType tokenType = it.getTokenType();
      Language language = tokenType.getLanguage();
      TokenSet comments = myComments.get(language);
      if (comments == null) {
        ParserDefinition definition = LanguageParserDefinitions.INSTANCE.forLanguage(language);
        if (definition != null) {
          comments = definition.getCommentTokens();
        }
        if (comments == null) {
          return fallbackColumn;
        } else {
          myComments.put(language, comments);
        }
      }
      if (comments.contains(tokenType) && indentOffset == it.getStart()) {
        String prefix = COMMENT_PREFIXES.get(tokenType);
        if (prefix == null) {
          prefix = getCommentPrefix(tokenType);
        }
        if (!NO_COMMENT_INFO_MARKER.equals(prefix)) {
          final int indentInsideCommentOffset =
              CharArrayUtil.shiftForward(
                  myChars, indentOffset + prefix.length(), lineEndOffset, " \t");
          if (indentInsideCommentOffset < lineEndOffset) {
            int indent = myEditor.calcColumnNumber(indentInsideCommentOffset, line);
            indentAfterUncomment.put(line, indent - prefix.length());
            return indent;
          }
        }
      }
      return fallbackColumn;
    }
  }
}
/** @author Eugene.Kudelevsky */
public class AndroidDependenciesCache {
  private static final Key<SoftReference<AndroidDependenciesCache>> KEY =
      Key.create("ANDROID_DEPENDENCIES_CACHE");
  private final Module myModule;

  private final Ref<List<WeakReference<AndroidModuleExtension>>> myAllDependencies = Ref.create();
  private final Ref<List<WeakReference<AndroidModuleExtension>>> myAllLibraryDependencies =
      Ref.create();

  private AndroidDependenciesCache(@NotNull Module module) {
    myModule = module;

    module
        .getProject()
        .getMessageBus()
        .connect(module)
        .subscribe(
            ProjectTopics.PROJECT_ROOTS,
            new ModuleRootAdapter() {
              @Override
              public void rootsChanged(ModuleRootEvent event) {
                dropCache();
              }
            });
  }

  private synchronized void dropCache() {
    myAllDependencies.set(null);
    myAllLibraryDependencies.set(null);
  }

  @NotNull
  public static AndroidDependenciesCache getInstance(@NotNull Module module) {
    AndroidDependenciesCache cache = SoftReference.dereference(module.getUserData(KEY));

    if (cache == null) {
      cache = new AndroidDependenciesCache(module);
      module.putUserData(KEY, new SoftReference<AndroidDependenciesCache>(cache));
    }
    return cache;
  }

  @NotNull
  public synchronized List<AndroidModuleExtension> getAllAndroidDependencies(
      boolean androidLibrariesOnly) {
    return getAllAndroidDependencies(
        myModule, androidLibrariesOnly, getListRef(androidLibrariesOnly));
  }

  @NotNull
  private Ref<List<WeakReference<AndroidModuleExtension>>> getListRef(
      boolean androidLibrariesOnly) {
    return androidLibrariesOnly ? myAllLibraryDependencies : myAllDependencies;
  }

  @NotNull
  private static List<AndroidModuleExtension> getAllAndroidDependencies(
      @NotNull Module module,
      boolean androidLibrariesOnly,
      Ref<List<WeakReference<AndroidModuleExtension>>> listRef) {
    List<WeakReference<AndroidModuleExtension>> refs = listRef.get();

    if (refs == null) {
      final List<AndroidModuleExtension> facets = new ArrayList<AndroidModuleExtension>();
      collectAllAndroidDependencies(
          module, androidLibrariesOnly, facets, new HashSet<AndroidModuleExtension>());

      refs =
          ContainerUtil.map(
              ContainerUtil.reverse(facets),
              new Function<AndroidModuleExtension, WeakReference<AndroidModuleExtension>>() {
                @Override
                public WeakReference<AndroidModuleExtension> fun(AndroidModuleExtension facet) {
                  return new WeakReference<AndroidModuleExtension>(facet);
                }
              });
      listRef.set(refs);
    }
    return dereference(refs);
  }

  @NotNull
  private static List<AndroidModuleExtension> dereference(
      @NotNull List<WeakReference<AndroidModuleExtension>> refs) {
    return ContainerUtil.mapNotNull(
        refs,
        new Function<WeakReference<AndroidModuleExtension>, AndroidModuleExtension>() {
          @Override
          public AndroidModuleExtension fun(WeakReference<AndroidModuleExtension> ref) {
            return ref.get();
          }
        });
  }

  private static void collectAllAndroidDependencies(
      Module module,
      boolean androidLibrariesOnly,
      List<AndroidModuleExtension> result,
      Set<AndroidModuleExtension> visited) {
    final OrderEntry[] entries = ModuleRootManager.getInstance(module).getOrderEntries();
    // loop in the inverse order to resolve dependencies on the libraries, so that if a library
    // is required by two higher level libraries it can be inserted in the correct place

    for (int i = entries.length - 1; i >= 0; i--) {
      final OrderEntry orderEntry = entries[i];
      if (orderEntry instanceof ModuleOrderEntry) {
        final ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry) orderEntry;

        if (moduleOrderEntry.getScope() == DependencyScope.COMPILE) {
          final Module depModule = moduleOrderEntry.getModule();

          if (depModule != null) {
            final AndroidModuleExtension depFacet =
                ModuleUtilCore.getExtension(depModule, AndroidModuleExtension.class);

            if (depFacet != null
                && (!androidLibrariesOnly || depFacet.isLibraryProject())
                && visited.add(depFacet)) {
              final List<WeakReference<AndroidModuleExtension>> cachedDepDeps =
                  getInstance(depModule).getListRef(androidLibrariesOnly).get();

              if (cachedDepDeps != null) {
                final List<AndroidModuleExtension> depDeps = dereference(cachedDepDeps);

                for (AndroidModuleExtension depDepFacet : depDeps) {
                  if (visited.add(depDepFacet)) {
                    result.add(depDepFacet);
                  }
                }
              } else {
                collectAllAndroidDependencies(depModule, androidLibrariesOnly, result, visited);
              }
              result.add(depFacet);
            }
          }
        }
      }
    }
  }
}
Esempio n. 29
0
/** @author dsl */
public class ElementToWorkOn {
  public static final Key<PsiElement> PARENT = Key.create("PARENT");
  private final PsiExpression myExpression;
  private final PsiLocalVariable myLocalVariable;
  public static final Key<String> PREFIX = Key.create("prefix");
  public static final Key<String> SUFFIX = Key.create("suffix");
  public static final Key<RangeMarker> TEXT_RANGE = Key.create("range");
  public static final Key<Boolean> OUT_OF_CODE_BLOCK = Key.create("out_of_code_block");

  private ElementToWorkOn(PsiLocalVariable localVariable, PsiExpression expr) {
    myLocalVariable = localVariable;
    myExpression = expr;
  }

  public PsiExpression getExpression() {
    return myExpression;
  }

  public PsiLocalVariable getLocalVariable() {
    return myLocalVariable;
  }

  public boolean isInvokedOnDeclaration() {
    return myExpression == null;
  }

  public static void processElementToWorkOn(
      final Editor editor,
      final PsiFile file,
      final String refactoringName,
      final String helpId,
      final Project project,
      final Pass<ElementToWorkOn> processor) {
    PsiLocalVariable localVar = null;
    PsiExpression expr = null;

    if (!editor.getSelectionModel().hasSelection()) {
      PsiElement element =
          TargetElementUtilBase.findTargetElement(
              editor,
              TargetElementUtilBase.ELEMENT_NAME_ACCEPTED
                  | TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED
                  | TargetElementUtilBase.LOOKUP_ITEM_ACCEPTED);
      if (element instanceof PsiLocalVariable) {
        localVar = (PsiLocalVariable) element;
        final PsiElement elementAt = file.findElementAt(editor.getCaretModel().getOffset());
        if (elementAt instanceof PsiIdentifier
            && elementAt.getParent() instanceof PsiReferenceExpression) {
          expr = (PsiExpression) elementAt.getParent();
        }
      } else {
        final PsiLocalVariable variable =
            PsiTreeUtil.getParentOfType(
                file.findElementAt(editor.getCaretModel().getOffset()), PsiLocalVariable.class);

        final int offset = editor.getCaretModel().getOffset();
        final PsiElement[] statementsInRange =
            IntroduceVariableBase.findStatementsAtOffset(editor, file, offset);

        if (statementsInRange.length == 1
            && (PsiUtil.hasErrorElementChild(statementsInRange[0])
                || !PsiUtil.isStatement(statementsInRange[0]))) {
          editor.getSelectionModel().selectLineAtCaret();
          final ElementToWorkOn elementToWorkOn =
              getElementToWorkOn(editor, file, refactoringName, helpId, project, localVar, expr);
          if (elementToWorkOn == null
              || elementToWorkOn.getLocalVariable() == null
                  && elementToWorkOn.getExpression() == null) {
            editor.getSelectionModel().removeSelection();
          }
        }

        if (!editor.getSelectionModel().hasSelection()) {
          final List<PsiExpression> expressions =
              IntroduceVariableBase.collectExpressions(file, editor, offset, statementsInRange);
          if (expressions.isEmpty()) {
            editor.getSelectionModel().selectLineAtCaret();
          } else if (expressions.size() == 1) {
            expr = expressions.get(0);
          } else {
            IntroduceTargetChooser.showChooser(
                editor,
                expressions,
                new Pass<PsiExpression>() {
                  @Override
                  public void pass(final PsiExpression selectedValue) {
                    PsiLocalVariable var =
                        null; // replace var if selected expression == var initializer
                    if (variable != null && variable.getInitializer() == selectedValue) {
                      var = variable;
                    }
                    processor.pass(
                        getElementToWorkOn(
                            editor, file, refactoringName, helpId, project, var, selectedValue));
                  }
                },
                new PsiExpressionTrimRenderer.RenderFunction());
            return;
          }
        }
      }
    }

    processor.pass(
        getElementToWorkOn(editor, file, refactoringName, helpId, project, localVar, expr));
  }

  private static ElementToWorkOn getElementToWorkOn(
      final Editor editor,
      final PsiFile file,
      final String refactoringName,
      final String helpId,
      final Project project,
      PsiLocalVariable localVar,
      PsiExpression expr) {
    int startOffset = 0;
    int endOffset = 0;
    if (localVar == null && expr == null) {
      startOffset = editor.getSelectionModel().getSelectionStart();
      endOffset = editor.getSelectionModel().getSelectionEnd();
      expr = CodeInsightUtil.findExpressionInRange(file, startOffset, endOffset);
      if (expr == null) {
        PsiIdentifier ident =
            CodeInsightUtil.findElementInRange(file, startOffset, endOffset, PsiIdentifier.class);
        if (ident != null) {
          localVar = PsiTreeUtil.getParentOfType(ident, PsiLocalVariable.class);
        }
      }
    }

    if (expr == null && localVar == null) {
      PsiElement[] statements = CodeInsightUtil.findStatementsInRange(file, startOffset, endOffset);
      if (statements.length == 1 && statements[0] instanceof PsiExpressionStatement) {
        expr = ((PsiExpressionStatement) statements[0]).getExpression();
      } else if (statements.length == 1 && statements[0] instanceof PsiDeclarationStatement) {
        PsiDeclarationStatement decl = (PsiDeclarationStatement) statements[0];
        PsiElement[] declaredElements = decl.getDeclaredElements();
        if (declaredElements.length == 1 && declaredElements[0] instanceof PsiLocalVariable) {
          localVar = (PsiLocalVariable) declaredElements[0];
        }
      }
    }
    if (localVar == null && expr == null) {
      expr = IntroduceVariableBase.getSelectedExpression(project, file, startOffset, endOffset);
    }

    if (localVar == null && expr == null) {
      String message =
          RefactoringBundle.getCannotRefactorMessage(
              RefactoringBundle.message("error.wrong.caret.position.local.or.expression.name"));
      CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId);
      return null;
    }
    return new ElementToWorkOn(localVar, expr);
  }
}
/** @author gregsh */
public class GeneratedParserUtilBase {

  private static final Logger LOG =
      Logger.getInstance("org.intellij.grammar.parser.GeneratedParserUtilBase");

  private static final int MAX_RECURSION_LEVEL = 1000;
  private static final int MAX_VARIANTS_SIZE = 10000;
  private static final int MAX_VARIANTS_TO_DISPLAY = 50;

  private static final int INITIAL_VARIANTS_SIZE = 1000;
  private static final int VARIANTS_POOL_SIZE = 10000;
  private static final int FRAMES_POOL_SIZE = 500;

  public static final IElementType DUMMY_BLOCK = new DummyBlockElementType();

  public interface Parser {
    boolean parse(PsiBuilder builder, int level);
  }

  public static final Parser TOKEN_ADVANCER =
      new Parser() {
        @Override
        public boolean parse(PsiBuilder builder, int level) {
          if (builder.eof()) return false;
          builder.advanceLexer();
          return true;
        }
      };

  public static final Parser TRUE_CONDITION =
      new Parser() {
        @Override
        public boolean parse(PsiBuilder builder, int level) {
          return true;
        }
      };

  public static boolean eof(PsiBuilder builder, int level) {
    return builder.eof();
  }

  public static int current_position_(PsiBuilder builder) {
    return builder.rawTokenIndex();
  }

  public static boolean recursion_guard_(PsiBuilder builder, int level, String funcName) {
    if (level > MAX_RECURSION_LEVEL) {
      builder.error(
          "Maximum recursion level (" + MAX_RECURSION_LEVEL + ") reached in '" + funcName + "'");
      return false;
    }
    return true;
  }

  public static boolean empty_element_parsed_guard_(PsiBuilder builder, String funcName, int pos) {
    if (pos == current_position_(builder)) {
      builder.error(
          "Empty element parsed in '" + funcName + "' at offset " + builder.getCurrentOffset());
      return false;
    }
    return true;
  }

  public static boolean invalid_left_marker_guard_(
      PsiBuilder builder, PsiBuilder.Marker marker, String funcName) {
    // builder.error("Invalid left marker encountered in " + funcName_ +" at offset " +
    // builder.getCurrentOffset());
    boolean goodMarker =
        marker != null; // && ((LighterASTNode)marker).getTokenType() != TokenType.ERROR_ELEMENT;
    if (!goodMarker) return false;
    ErrorState state = ErrorState.get(builder);

    return !state.frameStack.isEmpty();
  }

  public static TokenSet create_token_set_(IElementType... tokenTypes) {
    return TokenSet.create(tokenTypes);
  }

  public static boolean leftMarkerIs(PsiBuilder builder, IElementType type) {
    LighterASTNode marker = builder.getLatestDoneMarker();
    return marker != null && marker.getTokenType() == type;
  }

  private static boolean consumeTokens(
      PsiBuilder builder, boolean smart, int pin, IElementType... tokens) {
    ErrorState state = ErrorState.get(builder);
    if (state.completionState != null && state.predicateSign) {
      addCompletionVariant(builder, state.completionState, tokens);
    }
    // suppress single token completion
    CompletionState completionState = state.completionState;
    state.completionState = null;
    boolean result = true;
    boolean pinned = false;
    for (int i = 0, tokensLength = tokens.length; i < tokensLength; i++) {
      if (pin > 0 && i == pin) pinned = result;
      if (result || pinned) {
        boolean fast = smart && i == 0;
        if (!(fast ? consumeTokenFast(builder, tokens[i]) : consumeToken(builder, tokens[i]))) {
          result = false;
          if (pin < 0 || pinned) report_error_(builder, state, false);
        }
      }
    }
    state.completionState = completionState;
    return pinned || result;
  }

  public static boolean consumeTokens(PsiBuilder builder, int pin, IElementType... token) {
    return consumeTokens(builder, false, pin, token);
  }

  public static boolean consumeTokensSmart(PsiBuilder builder, int pin, IElementType... token) {
    return consumeTokens(builder, true, pin, token);
  }

  public static boolean parseTokens(PsiBuilder builder, int pin, IElementType... tokens) {
    return parseTokens(builder, false, pin, tokens);
  }

  public static boolean parseTokensSmart(PsiBuilder builder, int pin, IElementType... tokens) {
    return parseTokens(builder, true, pin, tokens);
  }

  public static boolean parseTokens(
      PsiBuilder builder, boolean smart, int pin, IElementType... tokens) {
    PsiBuilder.Marker marker = builder.mark();
    boolean result = consumeTokens(builder, smart, pin, tokens);
    if (!result) {
      marker.rollbackTo();
    } else {
      marker.drop();
    }
    return result;
  }

  public static boolean consumeTokenSmart(PsiBuilder builder, IElementType token) {
    addCompletionVariantSmart(builder, token);
    return consumeTokenFast(builder, token);
  }

  public static boolean consumeTokenSmart(PsiBuilder builder, String token) {
    addCompletionVariantSmart(builder, token);
    return consumeTokenFast(builder, token);
  }

  public static boolean consumeToken(PsiBuilder builder, IElementType token) {
    addVariantSmart(builder, token, true);
    if (nextTokenIsFast(builder, token)) {
      builder.advanceLexer();
      return true;
    }
    return false;
  }

  public static boolean consumeTokenFast(PsiBuilder builder, IElementType token) {
    if (nextTokenIsFast(builder, token)) {
      builder.advanceLexer();
      return true;
    }
    return false;
  }

  public static boolean consumeToken(PsiBuilder builder, String text) {
    return consumeToken(builder, text, ErrorState.get(builder).caseSensitive);
  }

  public static boolean consumeToken(PsiBuilder builder, String text, boolean caseSensitive) {
    addVariantSmart(builder, text, true);
    int count = nextTokenIsFast(builder, text, caseSensitive);
    if (count > 0) {
      while (count-- > 0) builder.advanceLexer();
      return true;
    }
    return false;
  }

  public static boolean consumeTokenFast(PsiBuilder builder, String text) {
    int count = nextTokenIsFast(builder, text, ErrorState.get(builder).caseSensitive);
    if (count > 0) {
      while (count-- > 0) builder.advanceLexer();
      return true;
    }
    return false;
  }

  public static boolean nextTokenIsFast(PsiBuilder builder, IElementType token) {
    return builder.getTokenType() == token;
  }

  public static boolean nextTokenIsFast(PsiBuilder builder, IElementType... tokens) {
    IElementType tokenType = builder.getTokenType();
    for (IElementType token : tokens) {
      if (token == tokenType) return true;
    }
    return false;
  }

  public static boolean nextTokenIs(PsiBuilder builder, String frameName, IElementType... tokens) {
    ErrorState state = ErrorState.get(builder);
    if (state.completionState != null) return true;
    boolean track = !state.suppressErrors && state.predicateCount < 2 && state.predicateSign;
    if (!track) return nextTokenIsFast(builder, tokens);
    IElementType tokenType = builder.getTokenType();
    if (StringUtil.isNotEmpty(frameName)) {
      addVariantInner(state, builder.rawTokenIndex(), frameName);
    } else {
      for (IElementType token : tokens) {
        addVariant(builder, state, token);
      }
    }
    if (tokenType == null) return false;
    for (IElementType token : tokens) {
      if (tokenType == token) return true;
    }
    return false;
  }

  public static boolean nextTokenIs(PsiBuilder builder, IElementType token) {
    if (!addVariantSmart(builder, token, false)) return true;
    return nextTokenIsFast(builder, token);
  }

  public static boolean nextTokenIs(PsiBuilder builder, String tokenText) {
    if (!addVariantSmart(builder, tokenText, false)) return true;
    return nextTokenIsFast(builder, tokenText, ErrorState.get(builder).caseSensitive) > 0;
  }

  public static boolean nextTokenIsFast(PsiBuilder builder, String tokenText) {
    return nextTokenIsFast(builder, tokenText, ErrorState.get(builder).caseSensitive) > 0;
  }

  public static int nextTokenIsFast(PsiBuilder builder, String tokenText, boolean caseSensitive) {
    CharSequence sequence = builder.getOriginalText();
    int offset = builder.getCurrentOffset();
    int endOffset = offset + tokenText.length();
    CharSequence subSequence = sequence.subSequence(offset, Math.min(endOffset, sequence.length()));

    if (!Comparing.equal(subSequence, tokenText, caseSensitive)) return 0;

    int count = 0;
    while (true) {
      int nextOffset = builder.rawTokenTypeStart(++count);
      if (nextOffset > endOffset) {
        return -count;
      } else if (nextOffset == endOffset) {
        break;
      }
    }
    return count;
  }

  private static void addCompletionVariantSmart(PsiBuilder builder, Object token) {
    ErrorState state = ErrorState.get(builder);
    CompletionState completionState = state.completionState;
    if (completionState != null && state.predicateSign) {
      addCompletionVariant(builder, completionState, token);
    }
  }

  private static boolean addVariantSmart(PsiBuilder builder, Object token, boolean force) {
    ErrorState state = ErrorState.get(builder);
    // skip FIRST check in completion mode
    if (state.completionState != null && !force) return false;
    builder.eof();
    if (!state.suppressErrors && state.predicateCount < 2) {
      addVariant(builder, state, token);
    }
    return true;
  }

  public static void addVariant(PsiBuilder builder, String text) {
    addVariant(builder, ErrorState.get(builder), text);
  }

  private static void addVariant(PsiBuilder builder, ErrorState state, Object o) {
    builder.eof(); // skip whitespaces
    addVariantInner(state, builder.rawTokenIndex(), o);

    CompletionState completionState = state.completionState;
    if (completionState != null && state.predicateSign) {
      addCompletionVariant(builder, completionState, o);
    }
  }

  private static void addVariantInner(ErrorState state, int pos, Object o) {
    Variant variant = state.VARIANTS.alloc().init(pos, o);
    if (state.predicateSign) {
      state.variants.add(variant);
      if (state.lastExpectedVariantPos < variant.position) {
        state.lastExpectedVariantPos = variant.position;
      }
    } else {
      state.unexpected.add(variant);
    }
  }

  private static void addCompletionVariant(
      @NotNull PsiBuilder builder, @NotNull CompletionState completionState, Object o) {
    int offset = builder.getCurrentOffset();
    if (!builder.eof() && offset == builder.rawTokenTypeStart(1))
      return; // suppress for zero-length tokens

    boolean add = false;
    int diff = completionState.offset - offset;
    String text = completionState.convertItem(o);
    int length = text == null ? 0 : text.length();
    if (length == 0) return;
    if (diff == 0) {
      add = true;
    } else if (diff > 0 && diff <= length) {
      CharSequence fragment = builder.getOriginalText().subSequence(offset, completionState.offset);
      add = completionState.prefixMatches(fragment.toString(), text);
    } else if (diff < 0) {
      for (int i = -1; ; i--) {
        IElementType type = builder.rawLookup(i);
        int tokenStart = builder.rawTokenTypeStart(i);
        if (isWhitespaceOrComment(builder, type)) {
          diff = completionState.offset - tokenStart;
        } else if (type != null && tokenStart < completionState.offset) {
          CharSequence fragment =
              builder.getOriginalText().subSequence(tokenStart, completionState.offset);
          if (completionState.prefixMatches(fragment.toString(), text)) {
            diff = completionState.offset - tokenStart;
          }
          break;
        } else break;
      }
      add = diff >= 0 && diff < length;
    }
    add =
        add
            && length > 1
            && !(text.charAt(0) == '<' && text.charAt(length - 1) == '>')
            && !(text.charAt(0) == '\'' && text.charAt(length - 1) == '\'' && length < 5);
    if (add) {
      completionState.addItem(builder, text);
    }
  }

  public static boolean isWhitespaceOrComment(
      @NotNull PsiBuilder builder, @Nullable IElementType type) {
    return ((PsiBuilderImpl) ((Builder) builder).getDelegate()).whitespaceOrComment(type);
  }

  // here's the new section API for compact parsers & less IntelliJ platform API exposure
  public static final int _NONE_ = 0x0;
  public static final int _COLLAPSE_ = 0x1;
  public static final int _LEFT_ = 0x2;
  public static final int _LEFT_INNER_ = 0x4;
  public static final int _AND_ = 0x8;
  public static final int _NOT_ = 0x10;

  // simple enter/exit methods pair that doesn't require frame object
  public static PsiBuilder.Marker enter_section_(PsiBuilder builder) {
    return builder.mark();
  }

  public static void exit_section_(
      PsiBuilder builder,
      PsiBuilder.Marker marker,
      @Nullable IElementType elementType,
      boolean result) {
    close_marker_impl_(ErrorState.get(builder).frameStack.peekLast(), marker, elementType, result);
  }

  // complex enter/exit methods pair with frame object
  public static PsiBuilder.Marker enter_section_(
      PsiBuilder builder, int level, int modifiers, @Nullable String frameName) {
    PsiBuilder.Marker marker = builder.mark();
    enter_section_impl_(builder, level, modifiers, frameName);
    return marker;
  }

  private static void enter_section_impl_(
      PsiBuilder builder, int level, int modifiers, @Nullable String frameName) {
    ErrorState state = ErrorState.get(builder);
    Frame frame = state.FRAMES.alloc().init(builder, state, level, modifiers, frameName);
    Frame prevFrame = state.frameStack.peekLast();
    if (prevFrame != null && prevFrame.errorReportedAt > frame.position) {
      // report error for previous unsuccessful frame
      reportError(builder, state, frame, null, true, false);
    }
    if (((frame.modifiers & _LEFT_) | (frame.modifiers & _LEFT_INNER_)) != 0) {
      PsiBuilder.Marker left = (PsiBuilder.Marker) builder.getLatestDoneMarker();
      if (invalid_left_marker_guard_(builder, left, frameName)) {
        frame.leftMarker = left;
      }
    }
    state.frameStack.add(frame);
    if ((modifiers & _AND_) != 0) {
      if (state.predicateCount == 0 && !state.predicateSign) {
        throw new AssertionError("Incorrect false predicate sign");
      }
      state.predicateCount++;
    } else if ((modifiers & _NOT_) != 0) {
      if (state.predicateCount == 0) {
        state.predicateSign = false;
      } else {
        state.predicateSign = !state.predicateSign;
      }
      state.predicateCount++;
    }
  }

  public static void exit_section_(
      PsiBuilder builder,
      int level,
      PsiBuilder.Marker marker,
      @Nullable IElementType elementType,
      boolean result,
      boolean pinned,
      @Nullable Parser eatMore) {
    ErrorState state = ErrorState.get(builder);

    Frame frame = state.frameStack.pollLast();
    if (frame == null || level != frame.level) {
      LOG.error("Unbalanced error section: got " + frame + ", expected level " + level);
      if (frame != null) state.FRAMES.recycle(frame);
      close_marker_impl_(frame, marker, elementType, result);
      return;
    }

    if (((frame.modifiers & _AND_) | (frame.modifiers & _NOT_)) != 0) {
      close_marker_impl_(frame, marker, null, false);
      state.predicateCount--;
      if ((frame.modifiers & _NOT_) != 0) state.predicateSign = !state.predicateSign;
      state.FRAMES.recycle(frame);
      return;
    }
    close_frame_impl_(state, frame, builder, marker, elementType, result, pinned);
    exit_section_impl_(state, frame, builder, elementType, result, pinned, eatMore);
    state.FRAMES.recycle(frame);
  }

  private static void exit_section_impl_(
      ErrorState state,
      Frame frame,
      PsiBuilder builder,
      @Nullable IElementType elementType,
      boolean result,
      boolean pinned,
      @Nullable Parser eatMore) {
    int initialPos = builder.rawTokenIndex();
    boolean willFail = !result && !pinned;
    if (willFail
        && initialPos == frame.position
        && state.lastExpectedVariantPos == frame.position
        && frame.name != null
        && state.variants.size() - frame.variantCount > 1) {
      state.clearVariants(true, frame.variantCount);
      addVariantInner(state, initialPos, frame.name);
    }
    int lastErrorPos = getLastVariantPos(state, initialPos);
    if (!state.suppressErrors && eatMore != null) {
      state.suppressErrors = true;
      final boolean eatMoreFlagOnce = !builder.eof() && eatMore.parse(builder, frame.level + 1);
      boolean eatMoreFlag =
          eatMoreFlagOnce
              || !result && frame.position == initialPos && lastErrorPos > frame.position;

      PsiBuilderImpl.ProductionMarker latestDoneMarker =
          (pinned || result) && (state.altMode || elementType != null) && eatMoreFlagOnce
              ? (PsiBuilderImpl.ProductionMarker) builder.getLatestDoneMarker()
              : null;
      PsiBuilder.Marker extensionMarker = null;
      IElementType extensionTokenType = null;
      // whitespace prefix makes the very first frame offset bigger than marker start offset which
      // is always 0
      if (latestDoneMarker != null
          && frame.position >= latestDoneMarker.getStartIndex()
          && frame.position <= latestDoneMarker.getEndIndex()) {
        extensionMarker = ((PsiBuilder.Marker) latestDoneMarker).precede();
        extensionTokenType = latestDoneMarker.getTokenType();
        ((PsiBuilder.Marker) latestDoneMarker).drop();
      }
      // advance to the last error pos
      // skip tokens until lastErrorPos. parseAsTree might look better here...
      int parenCount = 0;
      while ((eatMoreFlag || parenCount > 0) && builder.rawTokenIndex() < lastErrorPos) {
        if (state.braces != null) {
          if (builder.getTokenType() == state.braces[0].getLeftBraceType()) parenCount++;
          else if (builder.getTokenType() == state.braces[0].getRightBraceType()) parenCount--;
        }
        builder.advanceLexer();
        eatMoreFlag = eatMore.parse(builder, frame.level + 1);
      }
      boolean errorReported =
          frame.errorReportedAt == initialPos || !result && frame.errorReportedAt >= frame.position;
      if (errorReported) {
        if (eatMoreFlag) {
          builder.advanceLexer();
          parseAsTree(state, builder, frame.level + 1, DUMMY_BLOCK, true, TOKEN_ADVANCER, eatMore);
        }
      } else if (eatMoreFlag) {
        errorReported = reportError(builder, state, frame, null, true, true);
        parseAsTree(state, builder, frame.level + 1, DUMMY_BLOCK, true, TOKEN_ADVANCER, eatMore);
      } else if (eatMoreFlagOnce
          || (!result && frame.position != builder.rawTokenIndex())
          || frame.errorReportedAt > initialPos) {
        errorReported = reportError(builder, state, frame, null, true, false);
      }
      if (extensionMarker != null) {
        extensionMarker.done(extensionTokenType);
      }
      state.suppressErrors = false;
      if (errorReported || result) {
        state.clearVariants(true, 0);
        state.clearVariants(false, 0);
        state.lastExpectedVariantPos = -1;
      }
    } else if (!result && pinned && frame.errorReportedAt < 0) {
      // do not report if there are errors beyond current position
      if (lastErrorPos == initialPos) {
        // do not force, inner recoverRoot might have skipped some tokens
        reportError(builder, state, frame, elementType, false, false);
      } else if (lastErrorPos > initialPos) {
        // set error pos here as if it is reported for future reference
        frame.errorReportedAt = lastErrorPos;
      }
    }
    // propagate errorReportedAt up the stack to avoid duplicate reporting
    Frame prevFrame = willFail && eatMore == null ? null : state.frameStack.peekLast();
    if (prevFrame != null && prevFrame.errorReportedAt < frame.errorReportedAt) {
      prevFrame.errorReportedAt = frame.errorReportedAt;
    }
  }

  private static void close_frame_impl_(
      ErrorState state,
      Frame frame,
      PsiBuilder builder,
      PsiBuilder.Marker marker,
      IElementType elementType,
      boolean result,
      boolean pinned) {
    if (elementType != null && marker != null) {
      if ((frame.modifiers & _COLLAPSE_) != 0) {
        PsiBuilderImpl.ProductionMarker last =
            result || pinned
                ? (PsiBuilderImpl.ProductionMarker) builder.getLatestDoneMarker()
                : null;
        if (last != null
            && last.getStartIndex() == frame.position
            && state.typeExtends(last.getTokenType(), elementType)) {
          IElementType resultType = last.getTokenType();
          ((PsiBuilder.Marker) last).drop();
          marker.done(resultType);
          return;
        }
      }
      if (result || pinned) {
        if ((frame.modifiers & _LEFT_INNER_) != 0 && frame.leftMarker != null) {
          marker.done(elementType);
          frame.leftMarker.precede().done(((LighterASTNode) frame.leftMarker).getTokenType());
          frame.leftMarker.drop();
        } else if ((frame.modifiers & _LEFT_) != 0 && frame.leftMarker != null) {
          marker.drop();
          frame.leftMarker.precede().done(elementType);
        } else {
          if (frame.level == 0) builder.eof(); // skip whitespaces
          marker.done(elementType);
        }
      } else {
        close_marker_impl_(frame, marker, null, false);
      }
    } else if (result || pinned) {
      if (marker != null) marker.drop();
      if ((frame.modifiers & _LEFT_INNER_) != 0 && frame.leftMarker != null) {
        frame.leftMarker.precede().done(((LighterASTNode) frame.leftMarker).getTokenType());
        frame.leftMarker.drop();
      }
    } else {
      close_marker_impl_(frame, marker, null, false);
    }
  }

  private static void close_marker_impl_(
      Frame frame, PsiBuilder.Marker marker, IElementType elementType, boolean result) {
    if (marker == null) return;
    if (result) {
      if (elementType != null) {
        marker.done(elementType);
      } else {
        marker.drop();
      }
    } else {
      if (frame != null) {
        int position = ((PsiBuilderImpl.ProductionMarker) marker).getStartIndex();
        if (frame.errorReportedAt > position) {
          frame.errorReportedAt = frame.errorReportedAtPrev;
        }
      }
      marker.rollbackTo();
    }
  }

  public static boolean report_error_(PsiBuilder builder, boolean result) {
    if (!result) report_error_(builder, ErrorState.get(builder), false);
    return result;
  }

  public static void report_error_(PsiBuilder builder, ErrorState state, boolean advance) {
    Frame frame = state.frameStack.isEmpty() ? null : state.frameStack.getLast();
    if (frame == null) {
      LOG.error("unbalanced enter/exit section call: got null");
      return;
    }
    int position = builder.rawTokenIndex();
    if (frame.errorReportedAt < position && getLastVariantPos(state, position + 1) <= position) {
      reportError(builder, state, frame, null, true, advance);
    }
  }

  private static int getLastVariantPos(ErrorState state, int defValue) {
    return state.lastExpectedVariantPos < 0 ? defValue : state.lastExpectedVariantPos;
  }

  private static boolean reportError(
      PsiBuilder builder,
      ErrorState state,
      Frame frame,
      IElementType elementType,
      boolean force,
      boolean advance) {
    String expectedText = state.getExpectedText(builder);
    boolean notEmpty = StringUtil.isNotEmpty(expectedText);
    if (force || notEmpty || advance) {
      String gotText =
          builder.eof()
              ? "unexpected end of file"
              : notEmpty
                  ? "got '" + builder.getTokenText() + "'"
                  : "'" + builder.getTokenText() + "' unexpected";
      String message = expectedText + gotText;
      if (advance) {
        PsiBuilder.Marker mark = builder.mark();
        builder.advanceLexer();
        mark.error(message);
      } else if (!force) {
        PsiBuilder.Marker extensionMarker = null;
        IElementType extensionTokenType = null;
        PsiBuilderImpl.ProductionMarker latestDoneMarker =
            elementType == null
                ? null
                : (PsiBuilderImpl.ProductionMarker) builder.getLatestDoneMarker();
        if (latestDoneMarker != null
            && frame.position >= latestDoneMarker.getStartIndex()
            && frame.position <= latestDoneMarker.getEndIndex()) {
          extensionMarker = ((PsiBuilder.Marker) latestDoneMarker).precede();
          extensionTokenType = latestDoneMarker.getTokenType();
          ((PsiBuilder.Marker) latestDoneMarker).drop();
        }
        builder.error(message);
        if (extensionMarker != null) extensionMarker.done(extensionTokenType);
      } else {
        builder.error(message);
      }
      builder.eof(); // skip whitespaces
      frame.errorReportedAt = builder.rawTokenIndex();
      return true;
    }
    return false;
  }

  public static final Key<CompletionState> COMPLETION_STATE_KEY =
      Key.create("COMPLETION_STATE_KEY");

  public static class CompletionState implements Function<Object, String> {
    public final int offset;
    public final Collection<String> items = ContainerUtil.newTroveSet();

    public CompletionState(int offset_) {
      offset = offset_;
    }

    @Nullable
    public String convertItem(Object o) {
      return o instanceof Object[] ? StringUtil.join((Object[]) o, this, " ") : o.toString();
    }

    @Override
    public String fun(Object o) {
      return o.toString();
    }

    public void addItem(@NotNull PsiBuilder builder, @NotNull String text) {
      items.add(text);
    }

    public boolean prefixMatches(@NotNull String prefix, @NotNull String variant) {
      boolean matches =
          new CamelHumpMatcher(prefix, false).prefixMatches(variant.replace(' ', '_'));
      if (matches && StringUtil.isWhiteSpace(prefix.charAt(prefix.length() - 1))) {
        return StringUtil.startsWithIgnoreCase(variant, prefix);
      }
      return matches;
    }
  }

  public static class Builder extends PsiBuilderAdapter {
    public final ErrorState state;
    public final PsiParser parser;

    public Builder(PsiBuilder builder, ErrorState state_, PsiParser parser_) {
      super(builder);
      state = state_;
      parser = parser_;
    }

    public Lexer getLexer() {
      return ((PsiBuilderImpl) myDelegate).getLexer();
    }
  }

  public static PsiBuilder adapt_builder_(IElementType root, PsiBuilder builder, PsiParser parser) {
    return adapt_builder_(root, builder, parser, null);
  }

  public static PsiBuilder adapt_builder_(
      IElementType root, PsiBuilder builder, PsiParser parser, TokenSet[] extendsSets) {
    ErrorState state = new ErrorState();
    ErrorState.initState(state, builder, root, extendsSets);
    return new Builder(builder, state, parser);
  }

  public static class ErrorState {
    TokenSet[] extendsSets;
    public PairProcessor<IElementType, IElementType> altExtendsChecker;

    int predicateCount;
    boolean predicateSign = true;
    boolean suppressErrors;
    public final LinkedList<Frame> frameStack = new LinkedList<Frame>();
    public CompletionState completionState;

    private boolean caseSensitive;
    public BracePair[] braces;
    public boolean altMode;

    int lastExpectedVariantPos = -1;
    MyList<Variant> variants = new MyList<Variant>(INITIAL_VARIANTS_SIZE);
    MyList<Variant> unexpected = new MyList<Variant>(INITIAL_VARIANTS_SIZE / 10);

    final LimitedPool<Variant> VARIANTS =
        new LimitedPool<Variant>(
            VARIANTS_POOL_SIZE,
            new LimitedPool.ObjectFactory<Variant>() {
              @NotNull
              @Override
              public Variant create() {
                return new Variant();
              }

              @Override
              public void cleanup(@NotNull final Variant o) {}
            });
    final LimitedPool<Frame> FRAMES =
        new LimitedPool<Frame>(
            FRAMES_POOL_SIZE,
            new LimitedPool.ObjectFactory<Frame>() {
              @NotNull
              @Override
              public Frame create() {
                return new Frame();
              }

              @Override
              public void cleanup(@NotNull final Frame o) {}
            });

    public static ErrorState get(PsiBuilder builder) {
      return ((Builder) builder).state;
    }

    public static void initState(
        ErrorState state, PsiBuilder builder, IElementType root, TokenSet[] extendsSets) {
      state.extendsSets = extendsSets;
      PsiFile file = builder.getUserDataUnprotected(FileContextUtil.CONTAINING_FILE_KEY);
      state.completionState = file == null ? null : file.getUserData(COMPLETION_STATE_KEY);
      Language language = file == null ? root.getLanguage() : file.getLanguage();
      state.caseSensitive = language.isCaseSensitive();
      PairedBraceMatcher matcher = LanguageBraceMatching.INSTANCE.forLanguage(language);
      state.braces = matcher == null ? null : matcher.getPairs();
      if (state.braces != null && state.braces.length == 0) state.braces = null;
    }

    public String getExpectedText(PsiBuilder builder) {
      int position = builder.rawTokenIndex();
      StringBuilder sb = new StringBuilder();
      if (addExpected(sb, position, true)) {
        sb.append(" expected, ");
      }
      return sb.toString();
    }

    private boolean addExpected(StringBuilder sb, int position, boolean expected) {
      MyList<Variant> list = expected ? variants : unexpected;
      String[] strings = new String[list.size()];
      long[] hashes = new long[strings.length];
      Arrays.fill(strings, "");
      int count = 0;
      loop:
      for (Variant variant : list) {
        if (position == variant.position) {
          String text = variant.object.toString();
          long hash = StringHash.calc(text);
          for (int i = 0; i < count; i++) {
            if (hashes[i] == hash) continue loop;
          }
          hashes[count] = hash;
          strings[count] = text;
          count++;
        }
      }
      Arrays.sort(strings);
      count = 0;
      for (String s : strings) {
        if (s.length() == 0) continue;
        if (count++ > 0) {
          if (count > MAX_VARIANTS_TO_DISPLAY) {
            sb.append(" and ...");
            break;
          } else {
            sb.append(", ");
          }
        }
        char c = s.charAt(0);
        String displayText = c == '<' || StringUtil.isJavaIdentifierStart(c) ? s : '\'' + s + '\'';
        sb.append(displayText);
      }
      if (count > 1 && count < MAX_VARIANTS_TO_DISPLAY) {
        int idx = sb.lastIndexOf(", ");
        sb.replace(idx, idx + 1, " or");
      }
      return count > 0;
    }

    public int getVariantsSize() {
      return variants.size();
    }

    public void clearVariants(boolean expected, int start) {
      MyList<Variant> list = expected ? variants : unexpected;
      if (start < 0 || start >= list.size()) return;
      for (int i = start, len = list.size(); i < len; i++) {
        VARIANTS.recycle(list.get(i));
      }
      list.setSize(start);
      if (expected) {
        lastExpectedVariantPos = -1;
        for (Variant variant : list) {
          if (lastExpectedVariantPos < variant.position) lastExpectedVariantPos = variant.position;
        }
      }
    }

    boolean typeExtends(IElementType child, IElementType parent) {
      if (child == parent) return true;
      if (extendsSets != null) {
        for (TokenSet set : extendsSets) {
          if (set.contains(child) && set.contains(parent)) return true;
        }
      }
      return altExtendsChecker != null && altExtendsChecker.process(child, parent);
    }
  }

  public static class Frame {
    public int offset;
    public int position;
    public int level;
    public int modifiers;
    public String name;
    public int variantCount;
    public int errorReportedAt;
    public int errorReportedAtPrev;
    public PsiBuilder.Marker leftMarker;

    public Frame() {}

    public Frame init(
        PsiBuilder builder, ErrorState state, int level_, int modifiers_, String name_) {
      offset = builder.getCurrentOffset();
      position = builder.rawTokenIndex();
      level = level_;
      modifiers = modifiers_;
      name = name_;
      variantCount = state.variants.size();
      errorReportedAt = -1;

      Frame prev = state.frameStack.peekLast();
      errorReportedAtPrev = prev == null ? -1 : prev.errorReportedAt;
      leftMarker = null;
      return this;
    }

    @Override
    public String toString() {
      String mod =
          modifiers == _NONE_
              ? "_NONE_, "
              : ((modifiers & _COLLAPSE_) != 0 ? "_CAN_COLLAPSE_, " : "")
                  + ((modifiers & _LEFT_) != 0 ? "_LEFT_, " : "")
                  + ((modifiers & _LEFT_INNER_) != 0 ? "_LEFT_INNER_, " : "")
                  + ((modifiers & _AND_) != 0 ? "_AND_, " : "")
                  + ((modifiers & _NOT_) != 0 ? "_NOT_, " : "");
      return String.format(
          "{%s:%s:%d, %d, %s%s}", offset, position, level, errorReportedAt, mod, name);
    }
  }

  private static class Variant {
    int position;
    Object object;

    public Variant init(int pos, Object o) {
      position = pos;
      object = o;
      return this;
    }

    @Override
    public String toString() {
      return "<" + position + ", " + object + ">";
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      Variant variant = (Variant) o;

      if (position != variant.position) return false;
      if (!this.object.equals(variant.object)) return false;

      return true;
    }

    @Override
    public int hashCode() {
      int result = position;
      result = 31 * result + object.hashCode();
      return result;
    }
  }

  private static final int MAX_CHILDREN_IN_TREE = 10;

  public static boolean parseAsTree(
      ErrorState state,
      final PsiBuilder builder,
      int level,
      final IElementType chunkType,
      boolean checkBraces,
      final Parser parser,
      final Parser eatMoreCondition) {
    final LinkedList<Pair<PsiBuilder.Marker, PsiBuilder.Marker>> parenList =
        new LinkedList<Pair<PsiBuilder.Marker, PsiBuilder.Marker>>();
    final LinkedList<Pair<PsiBuilder.Marker, Integer>> siblingList =
        new LinkedList<Pair<PsiBuilder.Marker, Integer>>();
    PsiBuilder.Marker marker = null;

    final Runnable checkSiblingsRunnable =
        new Runnable() {
          @Override
          public void run() {
            main:
            while (!siblingList.isEmpty()) {
              final Pair<PsiBuilder.Marker, PsiBuilder.Marker> parenPair = parenList.peek();
              final int rating = siblingList.getFirst().second;
              int count = 0;
              for (Pair<PsiBuilder.Marker, Integer> pair : siblingList) {
                if (pair.second != rating || parenPair != null && pair.first == parenPair.second)
                  break main;
                if (++count >= MAX_CHILDREN_IN_TREE) {
                  PsiBuilder.Marker parentMarker = pair.first.precede();
                  parentMarker.setCustomEdgeTokenBinders(
                      WhitespacesBinders.GREEDY_LEFT_BINDER, null);
                  while (count-- > 0) {
                    siblingList.removeFirst();
                  }
                  parentMarker.done(chunkType);
                  siblingList.addFirst(Pair.create(parentMarker, rating + 1));
                  continue main;
                }
              }
              break;
            }
          }
        };
    boolean checkParens = state.braces != null && checkBraces;
    int totalCount = 0;
    int tokenCount = 0;
    if (checkParens) {
      int tokenIdx = -1;
      while (builder.rawLookup(tokenIdx) == TokenType.WHITE_SPACE) tokenIdx--;
      LighterASTNode doneMarker =
          builder.rawLookup(tokenIdx) == state.braces[0].getLeftBraceType()
              ? builder.getLatestDoneMarker()
              : null;
      if (doneMarker != null
          && doneMarker.getStartOffset() == builder.rawTokenTypeStart(tokenIdx)
          && doneMarker.getTokenType() == TokenType.ERROR_ELEMENT) {
        parenList.add(
            Pair.create(((PsiBuilder.Marker) doneMarker).precede(), (PsiBuilder.Marker) null));
      }
    }
    while (true) {
      final IElementType tokenType = builder.getTokenType();
      if (checkParens
          && (tokenType == state.braces[0].getLeftBraceType()
              || tokenType == state.braces[0].getRightBraceType() && !parenList.isEmpty())) {
        if (marker != null) {
          marker.done(chunkType);
          siblingList.addFirst(Pair.create(marker, 1));
          marker = null;
          tokenCount = 0;
        }
        if (tokenType == state.braces[0].getLeftBraceType()) {
          final Pair<PsiBuilder.Marker, Integer> prev = siblingList.peek();
          parenList.addFirst(Pair.create(builder.mark(), prev == null ? null : prev.first));
        }
        checkSiblingsRunnable.run();
        builder.advanceLexer();
        if (tokenType == state.braces[0].getRightBraceType()) {
          final Pair<PsiBuilder.Marker, PsiBuilder.Marker> pair = parenList.removeFirst();
          pair.first.done(chunkType);
          // drop all markers inside parens
          while (!siblingList.isEmpty() && siblingList.getFirst().first != pair.second) {
            siblingList.removeFirst();
          }
          siblingList.addFirst(Pair.create(pair.first, 1));
          checkSiblingsRunnable.run();
        }
      } else {
        if (marker == null) {
          marker = builder.mark();
          marker.setCustomEdgeTokenBinders(WhitespacesBinders.GREEDY_LEFT_BINDER, null);
        }
        final boolean result =
            (!parenList.isEmpty() || eatMoreCondition.parse(builder, level + 1))
                && parser.parse(builder, level + 1);
        if (result) {
          tokenCount++;
          totalCount++;
        }
        if (!result) {
          break;
        }
      }

      if (tokenCount >= MAX_CHILDREN_IN_TREE && marker != null) {
        marker.done(chunkType);
        siblingList.addFirst(Pair.create(marker, 1));
        checkSiblingsRunnable.run();
        marker = null;
        tokenCount = 0;
      }
    }
    if (marker != null) {
      marker.drop();
    }
    for (Pair<PsiBuilder.Marker, PsiBuilder.Marker> pair : parenList) {
      pair.first.drop();
    }
    return totalCount != 0;
  }

  private static class DummyBlockElementType extends IElementType implements ICompositeElementType {
    DummyBlockElementType() {
      super("DUMMY_BLOCK", Language.ANY);
    }

    @NotNull
    @Override
    public ASTNode createCompositeNode() {
      return new DummyBlock();
    }
  }

  public static class DummyBlock extends CompositePsiElement {
    DummyBlock() {
      super(DUMMY_BLOCK);
    }

    @NotNull
    @Override
    public PsiReference[] getReferences() {
      return PsiReference.EMPTY_ARRAY;
    }

    @NotNull
    @Override
    public Language getLanguage() {
      return getParent().getLanguage();
    }
  }

  private static class MyList<E> extends ArrayList<E> {
    MyList(int initialCapacity) {
      super(initialCapacity);
    }

    protected void setSize(int fromIndex) {
      removeRange(fromIndex, size());
    }

    @Override
    public boolean add(E e) {
      int size = size();
      if (size >= MAX_VARIANTS_SIZE) {
        removeRange(MAX_VARIANTS_SIZE / 4, size - MAX_VARIANTS_SIZE / 4);
      }
      return super.add(e);
    }
  }
}