/** Search known properties for the best 'match' to show as hover data. */
 private PropertyInfo findBestHoverMatch(String propName) {
   debug(">> findBestHoverMatch(" + propName + ")");
   debug("index size: " + getIndex().size());
   // TODO: optimize, should be able to use index's treemap to find this without iterating all
   // entries.
   PropertyInfo best = null;
   int bestCommonPrefixLen = 0; // We try to pick property with longest common prefix
   int bestExtraLen = Integer.MAX_VALUE;
   for (PropertyInfo candidate : getIndex()) {
     int commonPrefixLen = StringUtil.commonPrefixLength(propName, candidate.getId());
     int extraLen = candidate.getId().length() - commonPrefixLen;
     if (commonPrefixLen == propName.length() && extraLen == 0) {
       // exact match found, can stop searching for better matches
       return candidate;
     }
     // candidate is better if...
     if (commonPrefixLen > bestCommonPrefixLen // it has a longer common prefix
         || commonPrefixLen == bestCommonPrefixLen
             && extraLen < bestExtraLen // or same common prefix but fewer extra chars
     ) {
       bestCommonPrefixLen = commonPrefixLen;
       bestExtraLen = extraLen;
       best = candidate;
     }
   }
   debug("<< findBestHoverMatch(" + propName + "): " + best);
   return best;
 }
    private void highlightPattern(String pattern, String data, StyledString result) {
      Styler highlightStyle = isDeemphasized() ? GREY_UNDERLINE : UNDERLINE;
      Styler plainStyle = isDeemphasized() ? GREY : NULL_STYLER;
      if (StringUtil.hasText(pattern)) {
        int dataPos = 0;
        int dataLen = data.length();
        int patternPos = 0;
        int patternLen = pattern.length();

        while (dataPos < dataLen && patternPos < patternLen) {
          int pChar = pattern.charAt(patternPos++);
          int highlightPos = data.indexOf(pChar, dataPos);
          if (dataPos < highlightPos) {
            result.append(data.substring(dataPos, highlightPos), plainStyle);
          }
          result.append(data.charAt(highlightPos), highlightStyle);
          dataPos = highlightPos + 1;
        }
        if (dataPos < dataLen) {
          result.append(data.substring(dataPos), plainStyle);
        }
      } else { // no pattern to highlight
        result.append(data, plainStyle);
      }
    }
 /** Create completions proposals for a field editor where property names can be entered. */
 public IContentProposal[] getPropertyFieldProposals(String contents, int position) {
   String prefix = contents.substring(0, position);
   if (StringUtil.hasText(prefix)) {
     List<Match<PropertyInfo>> matches = findMatches(prefix);
     if (matches != null && !matches.isEmpty()) {
       IContentProposal[] proposals = new IContentProposal[matches.size()];
       Collections.sort(
           matches,
           new Comparator<Match<PropertyInfo>>() {
             @Override
             public int compare(Match<PropertyInfo> o1, Match<PropertyInfo> o2) {
               int scoreCompare = Double.compare(o2.score, o1.score);
               if (scoreCompare != 0) {
                 return scoreCompare;
               } else {
                 return o1.data.getId().compareTo(o2.data.getId());
               }
             }
           });
       int i = 0;
       for (Match<PropertyInfo> m : matches) {
         proposals[i++] = new ContentProposal(m.data.getId(), m.data.getDescription());
       }
       return proposals;
     }
   }
   return NO_CONTENT_PROPOSALS;
 }