pymatcher.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import vim, re
  2. import heapq
  3. _escape = dict((c , "\\" + c) for c in ['^','$','.','{','}','(',')','[',']','\\','/','+'])
  4. def CtrlPPyMatch():
  5. items = vim.eval('a:items')
  6. astr = vim.eval('a:str')
  7. lowAstr = astr.lower()
  8. limit = int(vim.eval('a:limit'))
  9. mmode = vim.eval('a:mmode')
  10. aregex = int(vim.eval('a:regex'))
  11. spath = vim.eval('a:ispath')
  12. crfile = vim.eval('a:crfile')
  13. if not vim.eval('exists("g:ctrlp_match_current_file")') and ispath and crfile:
  14. items.remove(crfile)
  15. rez = vim.eval('s:rez')
  16. regex = ''
  17. if aregex == 1:
  18. regex = astr
  19. else:
  20. # Escape all of the characters as necessary
  21. escaped = [_escape.get(c, c) for c in lowAstr]
  22. # If the string is longer that one character, append a mismatch
  23. # expression to each character (except the last).
  24. if len(lowAstr) > 1:
  25. mismatch = ["[^" + c + "]*" for c in escaped[:-1]]
  26. regex = ''.join([c for pair in zip(escaped[:-1], mismatch) for c in pair])
  27. # Append the last character in the string to the regex
  28. regex += escaped[-1]
  29. # because this IGNORECASE flag is extremely expensive we are converting everything to lower case
  30. # see https://github.com/FelikZ/ctrlp-py-matcher/issues/29
  31. regex = regex.lower()
  32. res = []
  33. prog = re.compile(regex)
  34. def filename_score(line):
  35. # get filename via reverse find to improve performance
  36. slashPos = line.rfind('/')
  37. if slashPos != -1:
  38. line = line[slashPos + 1:]
  39. lineLower = line.lower()
  40. result = prog.search(lineLower)
  41. if result:
  42. score = result.end() - result.start() + 1
  43. score = score + ( len(lineLower) + 1 ) / 100.0
  44. score = score + ( len(line) + 1 ) / 1000.0
  45. return 1000.0 / score
  46. return 0
  47. def path_score(line):
  48. lineLower = line.lower()
  49. result = prog.search(lineLower)
  50. if result:
  51. score = result.end() - result.start() + 1
  52. score = score + ( len(lineLower) + 1 ) / 100.0
  53. return 1000.0 / score
  54. return 0
  55. if mmode == 'filename-only':
  56. res = [(filename_score(line), line) for line in items]
  57. elif mmode == 'first-non-tab':
  58. res = [(path_score(line.split('\t')[0]), line) for line in items]
  59. elif mmode == 'until-last-tab':
  60. res = [(path_score(line.rsplit('\t')[0]), line) for line in items]
  61. else:
  62. res = [(path_score(line), line) for line in items]
  63. rez.extend([line for score, line in heapq.nlargest(limit, res) if score != 0])
  64. # Use double quoted vim strings and escape \
  65. vimrez = ['"' + line.replace('\\', '\\\\').replace('"', '\\"') + '"' for line in rez]
  66. vim.command("let s:regex = '%s'" % regex)
  67. vim.command('let s:rez = [%s]' % ','.join(vimrez))