public int matchingDegree(@NotNull String name) {
    FList<TextRange> iterable = matchingFragments(name);
    if (iterable == null) return Integer.MIN_VALUE;
    if (iterable.isEmpty()) return 0;

    final TextRange first = iterable.getHead();
    boolean startMatch = first.getStartOffset() == 0;

    int matchingCase = 0;
    int p = -1;

    int integral =
        0; // -sum of matching-char-count * hump-index over all matched humps; favors longer
           // fragments matching earlier words
    int humpIndex = 1;
    int nextHumpStart = 0;
    for (TextRange range : iterable) {
      for (int i = range.getStartOffset(); i < range.getEndOffset(); i++) {
        boolean isHumpStart = false;
        while (nextHumpStart <= i) {
          if (nextHumpStart == i) {
            isHumpStart = true;
          }
          nextHumpStart = NameUtil.nextWord(name, nextHumpStart);
          if (first != range) {
            humpIndex++;
          }
        }
        integral -= humpIndex;

        char c = name.charAt(i);
        p = StringUtil.indexOf(myPattern, c, p + 1, myPattern.length, false);
        if (p < 0) {
          break;
        }

        if (c == myPattern[p]) {
          if (isUpperCase[p])
            matchingCase +=
                50; // strongly prefer user's uppercase matching uppercase: they made an effort to
                    // press Shift
          else if (i == 0 && startMatch)
            matchingCase += 15; // the very first letter case distinguishes classes in Java etc
          else if (isHumpStart)
            matchingCase +=
                1; // if a lowercase matches lowercase hump start, that also means something
        } else if (isHumpStart) {
          // disfavor hump starts where pattern letter case doesn't match name case
          matchingCase -= 20;
        }
      }
    }

    int startIndex = first.getStartOffset();
    boolean afterSeparator = StringUtil.indexOfAny(name, HARD_SEPARATORS, 0, startIndex) >= 0;
    boolean wordStart =
        startIndex == 0 || isWordStart(name, startIndex) && !isWordStart(name, startIndex - 1);
    boolean finalMatch = iterable.get(iterable.size() - 1).getEndOffset() == name.length();

    return (wordStart ? 1000 : 0)
        + integral * 10
        + matchingCase * (startMatch ? 10 : 1)
        + // in start matches, case is more important; in middle matches - fragment length
          // (integral)
        (afterSeparator ? 0 : 2)
        + (finalMatch ? 1 : 0);
  }