@Override
  public Map<LookupElement, StringBuilder> getRelevanceStrings() {
    final LinkedHashMap<LookupElement, StringBuilder> map =
        new LinkedHashMap<LookupElement, StringBuilder>();
    for (LookupElement item : myItems) {
      map.put(item, new StringBuilder());
    }
    final MultiMap<CompletionSorterImpl, LookupElement> inputBySorter =
        groupItemsBySorter(new ArrayList<LookupElement>(map.keySet()));

    if (inputBySorter.size() > 1) {
      for (LookupElement element : map.keySet()) {
        map.get(element).append(obtainSorter(element)).append(": ");
      }
    }

    for (CompletionSorterImpl sorter : inputBySorter.keySet()) {
      final LinkedHashMap<LookupElement, StringBuilder> subMap =
          new LinkedHashMap<LookupElement, StringBuilder>();
      for (LookupElement element : inputBySorter.get(sorter)) {
        subMap.put(element, map.get(element));
      }
      Classifier<LookupElement> classifier = myClassifiers.get(sorter);
      if (classifier != null) {
        classifier.describeItems(subMap, createContext(false));
      }
    }

    return map;
  }
  void submitPasses(
      @NotNull Map<FileEditor, HighlightingPass[]> passesMap,
      @NotNull DaemonProgressIndicator updateProgress) {
    if (isDisposed()) return;

    // null keys are ok
    MultiMap<Document, FileEditor> documentToEditors = MultiMap.createSet();
    MultiMap<FileEditor, TextEditorHighlightingPass> documentBoundPasses = MultiMap.createSmart();
    MultiMap<FileEditor, EditorBoundHighlightingPass> editorBoundPasses = MultiMap.createSmart();
    List<Pair<FileEditor, TextEditorHighlightingPass>> passesWithNoDocuments = new ArrayList<>();
    Set<VirtualFile> vFiles = new HashSet<>();

    for (Map.Entry<FileEditor, HighlightingPass[]> entry : passesMap.entrySet()) {
      FileEditor fileEditor = entry.getKey();
      HighlightingPass[] passes = entry.getValue();
      Document document;
      if (fileEditor instanceof TextEditor) {
        Editor editor = ((TextEditor) fileEditor).getEditor();
        LOG.assertTrue(!(editor instanceof EditorWindow));
        document = editor.getDocument();
      } else {
        VirtualFile virtualFile =
            ((FileEditorManagerEx) FileEditorManager.getInstance(myProject)).getFile(fileEditor);
        document =
            virtualFile == null ? null : FileDocumentManager.getInstance().getDocument(virtualFile);
      }
      if (document != null) {
        vFiles.add(FileDocumentManager.getInstance().getFile(document));
      }

      int prevId = 0;
      for (final HighlightingPass pass : passes) {
        if (pass instanceof EditorBoundHighlightingPass) {
          EditorBoundHighlightingPass editorPass = (EditorBoundHighlightingPass) pass;
          editorPass.setId(
              nextPassId.incrementAndGet()); // have to make ids unique for this document
          editorBoundPasses.putValue(fileEditor, editorPass);
        } else {
          TextEditorHighlightingPass textEditorHighlightingPass =
              convertToTextHighlightingPass(pass, document, nextPassId, prevId);
          document = textEditorHighlightingPass.getDocument();
          documentBoundPasses.putValue(fileEditor, textEditorHighlightingPass);
          if (document == null) {
            passesWithNoDocuments.add(Pair.create(fileEditor, textEditorHighlightingPass));
          } else {
            documentToEditors.putValue(document, fileEditor);
          }
          prevId = textEditorHighlightingPass.getId();
        }
      }
    }

    List<ScheduledPass> freePasses = new ArrayList<>(documentToEditors.size() * 5);
    List<ScheduledPass> dependentPasses = new ArrayList<>(documentToEditors.size() * 10);
    // (fileEditor, passId) -> created pass
    Map<Pair<FileEditor, Integer>, ScheduledPass> toBeSubmitted = new THashMap<>(passesMap.size());

    final AtomicInteger threadsToStartCountdown = new AtomicInteger(0);
    for (Map.Entry<Document, Collection<FileEditor>> entry : documentToEditors.entrySet()) {
      Collection<FileEditor> fileEditors = entry.getValue();
      Document document = entry.getKey();
      FileEditor preferredFileEditor = getPreferredFileEditor(document, fileEditors);
      List<TextEditorHighlightingPass> passes =
          (List<TextEditorHighlightingPass>) documentBoundPasses.get(preferredFileEditor);
      if (passes.isEmpty()) {
        continue;
      }
      sortById(passes);
      for (TextEditorHighlightingPass currentPass : passes) {
        createScheduledPass(
            preferredFileEditor,
            currentPass,
            toBeSubmitted,
            passes,
            freePasses,
            dependentPasses,
            updateProgress,
            threadsToStartCountdown);
      }
    }

    for (Map.Entry<FileEditor, Collection<EditorBoundHighlightingPass>> entry :
        editorBoundPasses.entrySet()) {
      FileEditor fileEditor = entry.getKey();
      Collection<EditorBoundHighlightingPass> createdEditorBoundPasses = entry.getValue();
      List<TextEditorHighlightingPass> createdDocumentBoundPasses =
          (List<TextEditorHighlightingPass>) documentBoundPasses.get(fileEditor);
      List<TextEditorHighlightingPass> allCreatedPasses =
          new ArrayList<>(createdDocumentBoundPasses);
      allCreatedPasses.addAll(createdEditorBoundPasses);

      for (EditorBoundHighlightingPass pass : createdEditorBoundPasses) {
        createScheduledPass(
            fileEditor,
            pass,
            toBeSubmitted,
            allCreatedPasses,
            freePasses,
            dependentPasses,
            updateProgress,
            threadsToStartCountdown);
      }
    }

    for (Pair<FileEditor, TextEditorHighlightingPass> pair : passesWithNoDocuments) {
      FileEditor fileEditor = pair.first;
      TextEditorHighlightingPass pass = pair.second;
      createScheduledPass(
          fileEditor,
          pass,
          toBeSubmitted,
          ContainerUtil.emptyList(),
          freePasses,
          dependentPasses,
          updateProgress,
          threadsToStartCountdown);
    }

    if (CHECK_CONSISTENCY && !ApplicationInfoImpl.isInPerformanceTest()) {
      assertConsistency(freePasses, toBeSubmitted, threadsToStartCountdown);
    }

    log(
        updateProgress,
        null,
        vFiles + " ----- starting " + threadsToStartCountdown.get(),
        freePasses);

    for (ScheduledPass dependentPass : dependentPasses) {
      mySubmittedPasses.put(dependentPass, Job.NULL_JOB);
    }
    for (ScheduledPass freePass : freePasses) {
      submit(freePass);
    }
  }
  @Override
  protected void processIntention(@NotNull PsiElement element, Project project, Editor editor)
      throws IncorrectOperationException {
    final GrField field;
    if (element.getParent() instanceof GrField) {
      field = (GrField) element.getParent();
    } else {
      final PsiReference ref = element.getReference();
      LOG.assertTrue(ref != null);
      PsiElement resolved = ref.resolve();
      if (resolved instanceof GrAccessorMethod) {
        resolved = ((GrAccessorMethod) resolved).getProperty();
      }
      LOG.assertTrue(resolved instanceof GrField);
      field = (GrField) resolved;
    }

    final HashSet<PsiReference> usages = new HashSet<PsiReference>();
    usages.addAll(ReferencesSearch.search(field).findAll());
    final GrAccessorMethod[] getters = field.getGetters();
    for (GrAccessorMethod getter : getters) {
      usages.addAll(MethodReferencesSearch.search(getter).findAll());
    }
    final GrAccessorMethod setter = field.getSetter();
    if (setter != null) {
      usages.addAll(MethodReferencesSearch.search(setter).findAll());
    }

    final String fieldName = field.getName();
    LOG.assertTrue(fieldName != null);
    final Collection<PsiElement> fieldUsages = new HashSet<PsiElement>();
    MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
    for (PsiReference usage : usages) {
      final PsiElement psiElement = usage.getElement();
      if (PsiUtil.isMethodUsage(psiElement)) continue;
      if (!GroovyFileType.GROOVY_LANGUAGE.equals(psiElement.getLanguage())) {
        conflicts.putValue(
            psiElement,
            GroovyIntentionsBundle.message("closure.is.accessed.outside.of.groovy", fieldName));
      } else {
        if (psiElement instanceof GrReferenceExpression) {
          fieldUsages.add(psiElement);
          if (PsiUtil.isAccessedForWriting((GrExpression) psiElement)) {
            conflicts.putValue(
                psiElement,
                GroovyIntentionsBundle.message("write.access.to.closure.variable", fieldName));
          }
        } else if (psiElement instanceof GrArgumentLabel) {
          conflicts.putValue(
              psiElement,
              GroovyIntentionsBundle.message("field.is.used.in.argument.label", fieldName));
        }
      }
    }
    final PsiClass containingClass = field.getContainingClass();
    final GrExpression initializer = field.getInitializerGroovy();
    LOG.assertTrue(initializer != null);
    final PsiType type = initializer.getType();
    LOG.assertTrue(type instanceof GrClosureType);
    final GrSignature signature = ((GrClosureType) type).getSignature();
    final List<MethodSignature> signatures =
        GrClosureSignatureUtil.generateAllMethodSignaturesBySignature(fieldName, signature);
    for (MethodSignature s : signatures) {
      final PsiMethod method = MethodSignatureUtil.findMethodBySignature(containingClass, s, true);
      if (method != null) {
        conflicts.putValue(
            method,
            GroovyIntentionsBundle.message(
                "method.with.signature.already.exists",
                GroovyPresentationUtil.getSignaturePresentation(s)));
      }
    }
    if (conflicts.size() > 0) {
      final ConflictsDialog conflictsDialog =
          new ConflictsDialog(
              project,
              conflicts,
              new Runnable() {
                @Override
                public void run() {
                  execute(field, fieldUsages);
                }
              });
      conflictsDialog.show();
      if (conflictsDialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) return;
    }
    execute(field, fieldUsages);
  }