@Nullable
  private LookupFile getClosestParent(final String typed) {
    if (typed == null) return null;
    LookupFile lastFound = myFinder.find(typed);
    if (lastFound == null) return null;
    if (lastFound.exists()) {
      if (typed.charAt(typed.length() - 1) != File.separatorChar) return lastFound.getParent();
      return lastFound;
    }

    final String[] splits = myFinder.normalize(typed).split(myFileSpitRegExp);
    StringBuilder fullPath = new StringBuilder();
    for (int i = 0; i < splits.length; i++) {
      String each = splits[i];
      fullPath.append(each);
      if (i < splits.length - 1) {
        fullPath.append(myFinder.getSeparator());
      }
      final LookupFile file = myFinder.find(fullPath.toString());
      if (file == null || !file.exists()) return lastFound;
      lastFound = file;
    }

    return lastFound;
  }
 private void setTextToFile(final LookupFile file) {
   String text = file.getAbsolutePath();
   if (file.isDirectory() && !text.endsWith(myFinder.getSeparator())) {
     text += myFinder.getSeparator();
   }
   myPathTextField.setText(text);
 }
  /**
   * Replace the path component under the caret with the file selected from the completion list.
   *
   * @param file the selected file.
   * @param caretPos
   * @param start the start offset of the path component under the caret.
   * @param end the end offset of the path component under the caret.
   * @throws BadLocationException
   */
  private void replacePathComponent(LookupFile file, int caretPos, int start, int end)
      throws BadLocationException {
    final Document doc = myPathTextField.getDocument();

    myPathTextField.setSelectionStart(0);
    myPathTextField.setSelectionEnd(0);

    final String name = file.getName();
    boolean toRemoveExistingName;
    String prefix = "";

    if (caretPos >= start) {
      prefix = doc.getText(start, caretPos - start);
      if (prefix.length() == 0) {
        prefix = doc.getText(start, end - start);
      }
      if (SystemInfo.isFileSystemCaseSensitive) {
        toRemoveExistingName = name.startsWith(prefix) && prefix.length() > 0;
      } else {
        toRemoveExistingName =
            name.toUpperCase().startsWith(prefix.toUpperCase()) && prefix.length() > 0;
      }
    } else {
      toRemoveExistingName = true;
    }

    int newPos;
    if (toRemoveExistingName) {
      doc.remove(start, end - start);
      doc.insertString(start, name, doc.getDefaultRootElement().getAttributes());
      newPos = start + name.length();
    } else {
      doc.insertString(caretPos, name, doc.getDefaultRootElement().getAttributes());
      newPos = caretPos + name.length();
    }

    if (file.isDirectory()) {
      if (!myFinder.getSeparator().equals(doc.getText(newPos, 1))) {
        doc.insertString(
            newPos, myFinder.getSeparator(), doc.getDefaultRootElement().getAttributes());
        newPos++;
      }
    }

    if (newPos < doc.getLength()) {
      if (myFinder.getSeparator().equals(doc.getText(newPos, 1))) {
        newPos++;
      }
    }
    myPathTextField.setCaretPosition(newPos);
  }
  private void processChosenFromCompletion(boolean nameOnly) {
    final LookupFile file = getSelectedFileFromCompletionPopup();
    if (file == null) return;

    if (nameOnly) {
      try {
        final Document doc = myPathTextField.getDocument();
        int caretPos = myPathTextField.getCaretPosition();
        if (myFinder.getSeparator().equals(doc.getText(caretPos, 1))) {
          for (; caretPos < doc.getLength(); caretPos++) {
            final String eachChar = doc.getText(caretPos, 1);
            if (!myFinder.getSeparator().equals(eachChar)) break;
          }
        }

        int start = caretPos > 0 ? caretPos - 1 : caretPos;
        while (start >= 0) {
          final String each = doc.getText(start, 1);
          if (myFinder.getSeparator().equals(each)) {
            start++;
            break;
          }
          start--;
        }

        int end = start < caretPos ? caretPos : start;
        while (end <= doc.getLength()) {
          final String each = doc.getText(end, 1);
          if (myFinder.getSeparator().equals(each)) {
            break;
          }
          end++;
        }

        if (end > doc.getLength()) {
          end = doc.getLength();
        }

        if (start > end || start < 0 || end > doc.getLength()) {
          setTextToFile(file);
        } else {
          replacePathComponent(file, caretPos, start, end);
        }
      } catch (BadLocationException e) {
        LOG.error(e);
      }
    } else {
      setTextToFile(file);
    }
  }
  public FileTextFieldImpl(
      final JTextField field,
      Finder finder,
      LookupFilter filter,
      Map<String, String> macroMap,
      final Disposable parent) {
    myPathTextField = field;
    myMacroMap = new TreeMap<String, String>();
    myMacroMap.putAll(macroMap);

    final InputMap listMap = (InputMap) UIManager.getDefaults().get("List.focusInputMap");
    final KeyStroke[] listKeys = listMap.keys();
    myDisabledTextActions = new HashSet<Action>();
    for (KeyStroke eachListStroke : listKeys) {
      final String listActionID = (String) listMap.get(eachListStroke);
      if ("selectNextRow".equals(listActionID) || "selectPreviousRow".equals(listActionID)) {
        final Object textActionID = field.getInputMap().get(eachListStroke);
        if (textActionID != null) {
          final Action textAction = field.getActionMap().get(textActionID);
          if (textAction != null) {
            myDisabledTextActions.add(textAction);
          }
        }
      }
    }

    final FileTextFieldImpl assigned = (FileTextFieldImpl) myPathTextField.getClientProperty(KEY);
    if (assigned != null) {
      assigned.myFinder = finder;
      assigned.myFilter = filter;
      return;
    }

    myPathTextField.putClientProperty(KEY, this);
    final boolean headless = ApplicationManager.getApplication().isUnitTestMode();

    myUiUpdater = new MergingUpdateQueue("FileTextField.UiUpdater", 200, false, myPathTextField);
    if (!headless) {
      new UiNotifyConnector(myPathTextField, myUiUpdater);
    }

    myFinder = finder;
    myFilter = filter;

    myFileSpitRegExp = myFinder.getSeparator().replaceAll("\\\\", "\\\\\\\\");

    myPathTextField
        .getDocument()
        .addDocumentListener(
            new DocumentListener() {
              public void insertUpdate(final DocumentEvent e) {
                processTextChanged();
              }

              public void removeUpdate(final DocumentEvent e) {
                processTextChanged();
              }

              public void changedUpdate(final DocumentEvent e) {
                processTextChanged();
              }
            });

    myPathTextField.addKeyListener(
        new KeyAdapter() {
          public void keyPressed(final KeyEvent e) {
            processListSelection(e);
          }
        });

    myPathTextField.addFocusListener(
        new FocusAdapter() {
          public void focusLost(final FocusEvent e) {
            closePopup();
          }
        });

    myCancelAction = new CancelAction();

    new LazyUiDisposable<FileTextFieldImpl>(parent, field, this) {
      protected void initialize(
          @NotNull Disposable parent, @NotNull FileTextFieldImpl child, @Nullable Project project) {
        Disposer.register(child, myUiUpdater);
      }
    };
  }
  public void processCompletion(final CompletionResult result) {
    result.myToComplete = new ArrayList<LookupFile>();
    result.mySiblings = new ArrayList<LookupFile>();
    result.myKidsAfterSeparator = new ArrayList<LookupFile>();
    String typed = result.myCompletionBase;

    if (typed == null || typed.length() == 0) return;

    addMacroPaths(result, typed);

    final String typedText = myFinder.normalize(typed);

    result.current = getClosestParent(typed);
    result.myClosestParent = result.current;

    if (result.current != null) {
      result.currentParentMatch =
          SystemInfo.isFileSystemCaseSensitive
              ? typedText.equals(result.current.getAbsolutePath())
              : typedText.equalsIgnoreCase(result.current.getAbsolutePath());

      result.closedPath =
          typed.endsWith(myFinder.getSeparator())
              && typedText.length() > myFinder.getSeparator().length();
      final String currentParentText = result.current.getAbsolutePath();

      if (!typedText.toUpperCase().startsWith(currentParentText.toUpperCase())) return;

      String prefix = typedText.substring(currentParentText.length());
      if (prefix.startsWith(myFinder.getSeparator())) {
        prefix = prefix.substring(myFinder.getSeparator().length());
      } else if (typed.endsWith(myFinder.getSeparator())) {
        prefix = "";
      }

      result.effectivePrefix = prefix.toUpperCase();

      result.currentGrandparent = result.current.getParent();
      if (result.currentGrandparent != null && result.currentParentMatch && !result.closedPath) {
        final String currentGrandparentText = result.currentGrandparent.getAbsolutePath();
        if (StringUtil.startsWithConcatenationOf(
            typedText, currentGrandparentText, myFinder.getSeparator())) {
          result.grandparentPrefix =
              currentParentText
                  .substring(currentGrandparentText.length() + myFinder.getSeparator().length())
                  .toUpperCase();
        }
      }
    } else {
      result.effectivePrefix = typedText.toUpperCase();
    }

    ApplicationManager.getApplication()
        .runReadAction(
            new Runnable() {
              public void run() {
                if (result.current != null) {
                  result.myToComplete.addAll(
                      result.current.getChildren(
                          new LookupFilter() {
                            public boolean isAccepted(final LookupFile file) {
                              return myFilter.isAccepted(file)
                                  && file.getName()
                                      .toUpperCase()
                                      .startsWith(result.effectivePrefix);
                            }
                          }));

                  if (result.currentParentMatch && !result.closedPath) {
                    result.myKidsAfterSeparator.addAll(result.myToComplete);
                  }

                  if (result.grandparentPrefix != null) {
                    final List<LookupFile> siblings =
                        result.currentGrandparent.getChildren(
                            new LookupFilter() {
                              public boolean isAccepted(final LookupFile file) {
                                return !file.equals(result.current)
                                    && myFilter.isAccepted(file)
                                    && file.getName()
                                        .toUpperCase()
                                        .startsWith(result.grandparentPrefix);
                              }
                            });
                    result.myToComplete.addAll(0, siblings);
                    result.mySiblings.addAll(siblings);
                  }
                }

                int currentDiff = Integer.MIN_VALUE;
                LookupFile toPreselect = result.myPreselected;

                if (toPreselect == null || !result.myToComplete.contains(toPreselect)) {
                  boolean toPreselectFixed = false;
                  if (result.effectivePrefix.length() > 0) {
                    for (LookupFile each : result.myToComplete) {
                      String eachName = each.getName().toUpperCase();
                      if (!eachName.startsWith(result.effectivePrefix)) continue;
                      int diff = result.effectivePrefix.compareTo(eachName);
                      currentDiff = Math.max(diff, currentDiff);
                      if (currentDiff == diff) {
                        toPreselect = each;
                        toPreselectFixed = true;
                        break;
                      }
                    }

                    if (!toPreselectFixed) {
                      toPreselect = null;
                    }
                  } else {
                    toPreselect = null;
                  }

                  if (toPreselect == null) {
                    if (result.myToComplete.size() == 1) {
                      toPreselect = result.myToComplete.get(0);
                    } else if (result.effectivePrefix.length() == 0) {
                      if (result.mySiblings.size() > 0) {
                        toPreselect = result.mySiblings.get(0);
                      }
                    }

                    if (toPreselect == null
                        && !result.myToComplete.contains(toPreselect)
                        && result.myToComplete.size() > 0) {
                      toPreselect = result.myToComplete.get(0);
                    }
                  }
                }

                if (result.currentParentMatch && result.mySiblings.size() > 0) {
                  toPreselect = null;
                }

                result.myPreselected = toPreselect;
              }
            });
  }