From 1c2fe908e166ecb1288505110005b22f6b1ac960 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 28 Feb 2014 16:48:41 +0200 Subject: [PATCH 01/41] call a process function from the matcher instead of returning the lines --- autoload/ctrlp.vim | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 19ac1463..096dbc76 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -469,7 +469,8 @@ fu! s:MatchIt(items, pat, limit, exc) if a:limit > 0 && len(lines) >= a:limit | brea | en endfo let s:mdata = [s:dyncwd, s:itemtype, s:regexp, s:sublist(a:items, id, -1)] - retu lines + + cal s:ProcessMatches(lines, a:pat) endf fu! s:MatchedItems(items, pat, limit) @@ -486,13 +487,17 @@ fu! s:MatchedItems(items, pat, limit) \ 'crfile': exc, \ 'regex': s:regexp, \ }] : [items, a:pat, a:limit, s:mmode(), s:ispath, exc, s:regexp] - let lines = call(s:matcher['match'], argms, s:matcher) + call(s:matcher['match'], argms, s:matcher) el - let lines = s:MatchIt(items, a:pat, a:limit, exc) + cal s:MatchIt(items, a:pat, a:limit, exc) en - let s:matches = len(lines) +endf + +fu! s:ProcessMatches(lines, pat) + let s:matches = len(a:lines) unl! s:did_exp - retu lines + + cal s:Render(a:lines, a:pat) endf fu! s:SplitPattern(str) @@ -579,9 +584,11 @@ fu! s:Update(str) if str == oldstr && !empty(str) && !exists('s:force') | retu | en let s:martcs = &scs && str =~ '\u' ? '\C' : '' let pat = s:matcher == {} ? s:SplitPattern(str) : str - let lines = s:nolim == 1 && empty(str) ? copy(g:ctrlp_lines) - \ : s:MatchedItems(g:ctrlp_lines, pat, s:mw_res) - cal s:Render(lines, pat) + + if s:nolim == 1 && empty(str) + cal s:Render(copy(g:ctrlp_lines), pat) + else + cal s:MatchedItems(g:ctrlp_lines, pat, s:mw_res) endf fu! s:ForceUpdate() From 0b96bef804663d8ef950b887995f03b17589b351 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Fri, 28 Feb 2014 17:03:37 +0200 Subject: [PATCH 02/41] the process function needs to be called from other scripts --- autoload/ctrlp.vim | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 096dbc76..78b1d849 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -470,7 +470,7 @@ fu! s:MatchIt(items, pat, limit, exc) endfo let s:mdata = [s:dyncwd, s:itemtype, s:regexp, s:sublist(a:items, id, -1)] - cal s:ProcessMatches(lines, a:pat) + cal ctrlp#process(lines, a:pat) endf fu! s:MatchedItems(items, pat, limit) @@ -493,13 +493,6 @@ fu! s:MatchedItems(items, pat, limit) en endf -fu! s:ProcessMatches(lines, pat) - let s:matches = len(a:lines) - unl! s:did_exp - - cal s:Render(a:lines, a:pat) -endf - fu! s:SplitPattern(str) let str = a:str if s:migemo && s:regexp && len(str) > 0 && executable('cmigemo') @@ -595,6 +588,10 @@ fu! s:ForceUpdate() sil! cal s:Update(escape(s:getinput(), '\')) endf +fu! s:UpdateCursorHold() + sil! cal feedkeys("f\e") +endf + fu! s:BuildPrompt(upd) let base = ( s:regexp ? 'r' : '>' ).( s:byfname() ? 'd' : '>' ).'> ' let str = escape(s:getinput(), '\') @@ -2269,6 +2266,13 @@ fu! ctrlp#init(type, ...) cal s:BuildPrompt(1) if s:keyloop | cal s:KeyLoop() | en endf + +fu! ctrlp#process(lines, pat) + let s:matches = len(a:lines) + unl! s:did_exp + + cal s:Render(a:lines, a:pat) +endf " - Autocmds {{{1 if has('autocmd') aug CtrlPAug From 63d4f24b84fbc7778f9903e4230d0fac9d734b8e Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 2 Mar 2014 14:51:05 +0200 Subject: [PATCH 03/41] python code for filtering in a thread --- autoload/ctrlp.vim | 12 +++-- python/ctrlp/__init__.py | 0 python/ctrlp/ctrlp.py | 112 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 python/ctrlp/__init__.py create mode 100644 python/ctrlp/ctrlp.py diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 78b1d849..f606416a 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -588,10 +588,6 @@ fu! s:ForceUpdate() sil! cal s:Update(escape(s:getinput(), '\')) endf -fu! s:UpdateCursorHold() - sil! cal feedkeys("f\e") -endf - fu! s:BuildPrompt(upd) let base = ( s:regexp ? 'r' : '>' ).( s:byfname() ? 'd' : '>' ).'> ' let str = escape(s:getinput(), '\') @@ -2284,6 +2280,7 @@ if has('autocmd') en fu! s:autocmds() + let s:pymatcher = 0 if !has('autocmd') | retu | en if exists('#CtrlPLazy') au! CtrlPLazy @@ -2293,6 +2290,13 @@ fu! s:autocmds() au! au CursorHold ControlP cal s:ForceUpdate() aug END + + if has("python") + exe 'python sys.path.insert( 0, "' . s:script_folder_path . '/../python" )' + py from ctrlp import CtrlP + py ctrlp = Ctrlp() + let s:pymatcher = 1 + en en endf "}}} diff --git a/python/ctrlp/__init__.py b/python/ctrlp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/ctrlp/ctrlp.py b/python/ctrlp/ctrlp.py new file mode 100644 index 00000000..d9d2da34 --- /dev/null +++ b/python/ctrlp/ctrlp.py @@ -0,0 +1,112 @@ +from Queue import Empty, Queue +from threading import Thread + +import re, os, vim + +class CtrlP: + def __init__(self): + self.queue = Queue() + + def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname=False): + processed = False + if self.process(): + processed = True + + if self.lastPat == pat: + if self.process() and self.queue.qsize() == 0 and not self.thread.isAlive(): + self.lastPat = None + elif not processed: + self.forceCursorHold() + elif pat: + self.thread = Thread(target=threadWorker, args=( + self.queue, items, pat, limit, exc, + itemtype, mtype, ispath, byfname, vim.eval('&ic'), vim.eval('&scs') + )) + self.thread.daemon = True + self.thread.start() + + self.lastPat = pat + self.forceCursorHold() + + def process(self): + try: + lines = self.queue.get(False) + self.queue.task_done() + + callback = vim.bindeval('function("ctrlp#process")') + lines = vim.List(lines) + + callback(lines, pat) + + return True + except Empty: + return False + + def forceCursorHold(self): + vim.command("call feedkeys(\"f\e\")") + +def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname, scs): + patterns = splitPattern(pat, byfname, scs) + + id = 0 + matchedItems = [] + for item in items: + id += 1 + if ispath and item == exc: + continue + + if byfname: + dirname = os.path.dirname(item) + basename = os.path.basename(item) + + match = patterns[0].match(basename) + + if len(patterns) == 2 and match is not None: + match = patterns[1].match(dirname) + else: + if itemtype > 2 and mtype == 'tabs': + match = patterns[1].match(re.split('\t+', item)[0]) + elif itemtype > 2 and mtype == 'tabe': + match = patterns[1].match(re.split('\t+[^\t]+$', item)[0]) + else: + match = patterns[0].match(item) + + if match is None: + continue + + matchedItems.append(item) + + if limit > 0 and len(matchedItems) >= limit: + break + + queue.put(matchedItems, timeout=1) + +def splitPattern(pat, byfname, ic, scs): + chars = [re.escape(c) for c in pat] + + patterns = [] + builder = lambda c: c + '[^' + c + ']*?' + + flags = 0 + if ic: + if scs: + upper = any(c.isupper() for c in pat) + if upper: + flags = re.I + else: + flags = re.I + + try: + if byfname: + delim = chars.index(';') + filechars = chars[:delim] + dirchars = chars[delim+1:] + patterns.append(re.compile(''.join(map(builder, filechars)), flags)) + + if dirchars: + patterns.append(re.compile(''.join(map(builder, dirchars)), flags)) + finally: + if not len(patterns): + patterns.append(re.compile(''.join(map(builder, chars)), flags)) + + return patterns From 9ae4b5a76e7f9c8612dd488a49e04a75ccce5535 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 2 Mar 2014 14:53:13 +0200 Subject: [PATCH 04/41] define the script folder path --- autoload/ctrlp.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index f606416a..a4cfc438 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -2292,7 +2292,7 @@ fu! s:autocmds() aug END if has("python") - exe 'python sys.path.insert( 0, "' . s:script_folder_path . '/../python" )' + exe 'python sys.path.insert( 0, "' . escape(expand(':p:h'), '\') . '/../python" )' py from ctrlp import CtrlP py ctrlp = Ctrlp() let s:pymatcher = 1 From d57e516a7cb2971382f0245aff0d49c574d97631 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 2 Mar 2014 15:00:02 +0200 Subject: [PATCH 05/41] add some missing python imports --- autoload/ctrlp.vim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index a4cfc438..8aa4a5d2 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -2292,10 +2292,11 @@ fu! s:autocmds() aug END if has("python") - exe 'python sys.path.insert( 0, "' . escape(expand(':p:h'), '\') . '/../python" )' - py from ctrlp import CtrlP - py ctrlp = Ctrlp() - let s:pymatcher = 1 + py import sys + exe 'python sys.path.insert( 0, "' . escape(expand(':p:h'), '\') . '/../python" )' + py from ctrlp.ctrlp import CtrlP + py ctrlp = Ctrlp() + let s:pymatcher = 1 en en endf From 7f21c560a21b729c60a14cdffdf0b856043a6803 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 2 Mar 2014 17:27:28 +0200 Subject: [PATCH 06/41] use the python matcher --- autoload/ctrlp.vim | 26 +++++++++++++++++--------- python/ctrlp/ctrlp.py | 1 + 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 8aa4a5d2..31fa7244 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -488,6 +488,8 @@ fu! s:MatchedItems(items, pat, limit) \ 'regex': s:regexp, \ }] : [items, a:pat, a:limit, s:mmode(), s:ispath, exc, s:regexp] call(s:matcher['match'], argms, s:matcher) + elsei s:pymatcher && !s:regexp + py ctrlp.filter(items, a:pat, a:limit, exc, s:itemtype, s:matchtype, s:ispath, s:byfname()) el cal s:MatchIt(items, a:pat, a:limit, exc) en @@ -576,7 +578,10 @@ fu! s:Update(str) " Stop if the string's unchanged if str == oldstr && !empty(str) && !exists('s:force') | retu | en let s:martcs = &scs && str =~ '\u' ? '\C' : '' - let pat = s:matcher == {} ? s:SplitPattern(str) : str + let pat = s:matcher == {} + ? s:pymatcher && !s:regexp + ? str : s:SplitPattern(str) + : str if s:nolim == 1 && empty(str) cal s:Render(copy(g:ctrlp_lines), pat) @@ -2252,6 +2257,7 @@ fu! ctrlp#init(type, ...) let [s:ermsg, v:errmsg] = [v:errmsg, ''] let [s:matches, s:init] = [1, 1] cal s:Reset(a:0 ? a:1 : {}) + cal s:pymatcherinit() noa cal s:Open() cal s:SetWD(a:0 ? a:1 : {}) cal s:MapNorms() @@ -2290,16 +2296,18 @@ fu! s:autocmds() au! au CursorHold ControlP cal s:ForceUpdate() aug END - - if has("python") - py import sys - exe 'python sys.path.insert( 0, "' . escape(expand(':p:h'), '\') . '/../python" )' - py from ctrlp.ctrlp import CtrlP - py ctrlp = Ctrlp() - let s:pymatcher = 1 - en en endf + +fu! s:pymatcherinit() + if !has('autocmd') || !has("python") | retu | en + + py import sys + exe 'python sys.path.insert( 0, "' . escape(expand(':p:h'), '\') . '/../python" )' + py from ctrlp.ctrlp import CtrlP + py ctrlp = Ctrlp() + let s:pymatcher = 1 +endf "}}} " vim:fen:fdm=marker:fmr={{{,}}}:fdl=0:fdc=1:ts=2:sw=2:sts=2 diff --git a/python/ctrlp/ctrlp.py b/python/ctrlp/ctrlp.py index d9d2da34..dca9a413 100644 --- a/python/ctrlp/ctrlp.py +++ b/python/ctrlp/ctrlp.py @@ -6,6 +6,7 @@ class CtrlP: def __init__(self): self.queue = Queue() + self.lastPat = None def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname=False): processed = False From 5cf1993956fe897cf7da16689a2c77819e605358 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 2 Mar 2014 17:29:50 +0200 Subject: [PATCH 07/41] rename the python class --- autoload/ctrlp.vim | 4 ++-- python/ctrlp/{ctrlp.py => matcher.py} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename python/ctrlp/{ctrlp.py => matcher.py} (99%) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 31fa7244..4f311591 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -2304,8 +2304,8 @@ fu! s:pymatcherinit() py import sys exe 'python sys.path.insert( 0, "' . escape(expand(':p:h'), '\') . '/../python" )' - py from ctrlp.ctrlp import CtrlP - py ctrlp = Ctrlp() + py from ctrlp.matcher import CtrlPMatcher + py ctrlp = CtrlPMatcher() let s:pymatcher = 1 endf "}}} diff --git a/python/ctrlp/ctrlp.py b/python/ctrlp/matcher.py similarity index 99% rename from python/ctrlp/ctrlp.py rename to python/ctrlp/matcher.py index dca9a413..a95586b0 100644 --- a/python/ctrlp/ctrlp.py +++ b/python/ctrlp/matcher.py @@ -3,7 +3,7 @@ import re, os, vim -class CtrlP: +class CtrlPMatcher: def __init__(self): self.queue = Queue() self.lastPat = None From 4b2611ed7c6f67fd25af83ff9c89b98f68555d12 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 2 Mar 2014 19:03:45 +0200 Subject: [PATCH 08/41] add debug logging --- python/ctrlp/matcher.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index a95586b0..731ea4d9 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -1,12 +1,22 @@ from Queue import Empty, Queue from threading import Thread -import re, os, vim +import logging, re, os, tempfile, vim class CtrlPMatcher: - def __init__(self): + def __init__(self, debug=False): self.queue = Queue() self.lastPat = None + self.debug = debug + + self.logger = logging.getLogger('ctrlp') + if debug: + self.logger.setLevel(logging.DEBUG) + + hdlr = logging.FileHandler(os.path.join(tempfile.gettempdir(), 'ctrlp-py.log')) + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + self.logger.addHandler(hdlr) def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname=False): processed = False @@ -15,13 +25,19 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= if self.lastPat == pat: if self.process() and self.queue.qsize() == 0 and not self.thread.isAlive(): + self.logger.debug("Thread job is processed for {pat}".format(pat=pat)) self.lastPat = None elif not processed: + self.logger.debug("Waiting for thread job for {pat}".format(pat=pat)) self.forceCursorHold() + else: + self.logger.debug("The same pattern '{pat}'".format(pat=pat)) elif pat: + self.logger.debug("Starting thread for {pat}".format(pat=pat)) self.thread = Thread(target=threadWorker, args=( self.queue, items, pat, limit, exc, - itemtype, mtype, ispath, byfname, vim.eval('&ic'), vim.eval('&scs') + itemtype, mtype, ispath, byfname, vim.eval('&ic'), vim.eval('&scs'), + self.logger )) self.thread.daemon = True self.thread.start() @@ -46,11 +62,12 @@ def process(self): def forceCursorHold(self): vim.command("call feedkeys(\"f\e\")") -def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname, scs): +def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname, scs, logger): patterns = splitPattern(pat, byfname, scs) id = 0 matchedItems = [] + logger.debug("Matching against {number} items using {pat}".format(number=len(items), pat=pat)) for item in items: id += 1 if ispath and item == exc: @@ -81,6 +98,7 @@ def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname break queue.put(matchedItems, timeout=1) + logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) def splitPattern(pat, byfname, ic, scs): chars = [re.escape(c) for c in pat] From 0cf4b0f938c6ffe752d03816a632912b3191346c Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 2 Mar 2014 23:34:57 +0200 Subject: [PATCH 09/41] refactor the matcher --- autoload/ctrlp.vim | 26 +++++++------ python/ctrlp/matcher.py | 86 ++++++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 47 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 4f311591..8ba49a6a 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -144,6 +144,15 @@ let [s:lcmap, s:prtmaps] = ['nn ', { \ 'PrtExit()': ['', '', ''], \ }] +let s:scriptpath = expand(':p:h') +if has('autocmd') && has('python') + py import sys + exe 'python sys.path.insert( 0, "' . escape(s:scriptpath, '\') . '/../python" )' + py from ctrlp.matcher import CtrlPMatcher + py ctrlp = CtrlPMatcher(debug=True) + let s:pymatcher = 1 +en + if !has('gui_running') cal add(s:prtmaps['PrtBS()'], remove(s:prtmaps['PrtCurLeft()'], 0)) en @@ -489,7 +498,11 @@ fu! s:MatchedItems(items, pat, limit) \ }] : [items, a:pat, a:limit, s:mmode(), s:ispath, exc, s:regexp] call(s:matcher['match'], argms, s:matcher) elsei s:pymatcher && !s:regexp - py ctrlp.filter(items, a:pat, a:limit, exc, s:itemtype, s:matchtype, s:ispath, s:byfname()) + py <:p:h'), '\') . '/../python" )' - py from ctrlp.matcher import CtrlPMatcher - py ctrlp = CtrlPMatcher() - let s:pymatcher = 1 -endf "}}} " vim:fen:fdm=marker:fmr={{{,}}}:fdl=0:fdc=1:ts=2:sw=2:sts=2 diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 731ea4d9..5339c3a9 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -19,6 +19,8 @@ def __init__(self, debug=False): self.logger.addHandler(hdlr) def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname=False): + self.logger.debug("Filtering {number} items using {pat}".format(number = len(items), pat=pat)) + processed = False if self.process(): processed = True @@ -39,6 +41,11 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= itemtype, mtype, ispath, byfname, vim.eval('&ic'), vim.eval('&scs'), self.logger )) + # self.thread = Thread(target=lambda a,b,c,d,e,f,g,h,i,j,k,l: a, args=( + # self.queue, items, pat, limit, exc, + # itemtype, mtype, ispath, byfname, vim.eval('&ic'), vim.eval('&scs'), + # self.logger + # )) self.thread.daemon = True self.thread.start() @@ -60,16 +67,55 @@ def process(self): return False def forceCursorHold(self): - vim.command("call feedkeys(\"f\e\")") + # col = vim.eval("col('.')") + # if col == 1: + + # pass + vim.command('call feedkeys("f\e")', 'n') + +def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname, ic, scs, logger): + logger.debug("Splitting pattern {pat}".format(pat=pat)) + + chars = [re.escape(c) for c in pat] + + logger.debug("Chars: {chars}".format(chars=chars)) + + patterns = [] + builder = lambda c: c + '[^' + c + ']*?' + + flags = 0 + if ic: + if scs: + upper = any(c.isupper() for c in pat) + if upper: + flags = re.I + else: + flags = re.I + + logger.debug("Flags: {flags}".format(flags=flags)) + + try: + if byfname: + delim = chars.index(';') + logger.debug("Creating filename patterns") + filechars = chars[:delim] + dirchars = chars[delim+1:] + patterns.append(re.compile(''.join(map(builder, filechars)), flags)) + + if dirchars: + patterns.append(re.compile(''.join(map(builder, dirchars)), flags)) + except ValueError: + pass -def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname, scs, logger): - patterns = splitPattern(pat, byfname, scs) + if not len(patterns): + patterns.append(re.compile(''.join(map(builder, chars)), flags)) + logger.debug("Creating normal patterns") - id = 0 + itemId = 0 matchedItems = [] logger.debug("Matching against {number} items using {pat}".format(number=len(items), pat=pat)) for item in items: - id += 1 + itemId += 1 if ispath and item == exc: continue @@ -99,33 +145,3 @@ def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname queue.put(matchedItems, timeout=1) logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) - -def splitPattern(pat, byfname, ic, scs): - chars = [re.escape(c) for c in pat] - - patterns = [] - builder = lambda c: c + '[^' + c + ']*?' - - flags = 0 - if ic: - if scs: - upper = any(c.isupper() for c in pat) - if upper: - flags = re.I - else: - flags = re.I - - try: - if byfname: - delim = chars.index(';') - filechars = chars[:delim] - dirchars = chars[delim+1:] - patterns.append(re.compile(''.join(map(builder, filechars)), flags)) - - if dirchars: - patterns.append(re.compile(''.join(map(builder, dirchars)), flags)) - finally: - if not len(patterns): - patterns.append(re.compile(''.join(map(builder, chars)), flags)) - - return patterns From fe565d00869e89c52f2474719ea04a0f21d40708 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 2 Mar 2014 23:46:47 +0200 Subject: [PATCH 10/41] do not reset the pymatcher variable --- autoload/ctrlp.vim | 1 - python/ctrlp/matcher.py | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 8ba49a6a..5b8749fc 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -2298,7 +2298,6 @@ if has('autocmd') en fu! s:autocmds() - let s:pymatcher = 0 if !has('autocmd') | retu | en if exists('#CtrlPLazy') au! CtrlPLazy diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 5339c3a9..da820ee0 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -7,17 +7,16 @@ class CtrlPMatcher: def __init__(self, debug=False): self.queue = Queue() self.lastPat = None - self.debug = debug self.logger = logging.getLogger('ctrlp') - if debug: - self.logger.setLevel(logging.DEBUG) - hdlr = logging.FileHandler(os.path.join(tempfile.gettempdir(), 'ctrlp-py.log')) formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') hdlr.setFormatter(formatter) self.logger.addHandler(hdlr) + if debug: + self.logger.setLevel(logging.DEBUG) + def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname=False): self.logger.debug("Filtering {number} items using {pat}".format(number = len(items), pat=pat)) From 907788752e6e44bb8120adab10f00dcaf76ba81e Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 2 Mar 2014 23:52:05 +0200 Subject: [PATCH 11/41] correctly set the pattern --- autoload/ctrlp.vim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 5b8749fc..e1bbb1c1 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -591,10 +591,10 @@ fu! s:Update(str) " Stop if the string's unchanged if str == oldstr && !empty(str) && !exists('s:force') | retu | en let s:martcs = &scs && str =~ '\u' ? '\C' : '' - let pat = s:matcher == {} - ? s:pymatcher && !s:regexp - ? str : s:SplitPattern(str) - : str + let pat = str + if s:matcher == {} && (!s:pymatcher || s:regexp) + let pat = s:SplitPattern(str) + en if s:nolim == 1 && empty(str) cal s:Render(copy(g:ctrlp_lines), pat) From 97dc4f1eef3392a38fd75fe92ea8511113ca311d Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Mon, 3 Mar 2014 00:41:34 +0200 Subject: [PATCH 12/41] change the refresh feedkeys --- python/ctrlp/matcher.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index da820ee0..1605e0b7 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -40,11 +40,6 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= itemtype, mtype, ispath, byfname, vim.eval('&ic'), vim.eval('&scs'), self.logger )) - # self.thread = Thread(target=lambda a,b,c,d,e,f,g,h,i,j,k,l: a, args=( - # self.queue, items, pat, limit, exc, - # itemtype, mtype, ispath, byfname, vim.eval('&ic'), vim.eval('&scs'), - # self.logger - # )) self.thread.daemon = True self.thread.start() @@ -66,19 +61,16 @@ def process(self): return False def forceCursorHold(self): - # col = vim.eval("col('.')") - # if col == 1: + col = vim.eval("col('.')") - # pass - vim.command('call feedkeys("f\e")', 'n') + if col == 1: + vim.command('call feedkeys("\\")') + else: + vim.command('call feedkeys("\\")') def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname, ic, scs, logger): - logger.debug("Splitting pattern {pat}".format(pat=pat)) - chars = [re.escape(c) for c in pat] - logger.debug("Chars: {chars}".format(chars=chars)) - patterns = [] builder = lambda c: c + '[^' + c + ']*?' @@ -91,8 +83,6 @@ def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname else: flags = re.I - logger.debug("Flags: {flags}".format(flags=flags)) - try: if byfname: delim = chars.index(';') From e494a29cfd8c2d108c6429f95db5cd39f6e30d84 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Mon, 3 Mar 2014 03:13:51 +0200 Subject: [PATCH 13/41] force the update to go through --- autoload/ctrlp.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index e1bbb1c1..b3a71110 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -589,7 +589,9 @@ fu! s:Update(str) " Get the new string sans tail let str = s:sanstail(a:str) " Stop if the string's unchanged - if str == oldstr && !empty(str) && !exists('s:force') | retu | en + if str == oldstr && !empty(str) && !exists('s:force') && (!s:pymatcher || s:regexp) + retu + en let s:martcs = &scs && str =~ '\u' ? '\C' : '' let pat = str if s:matcher == {} && (!s:pymatcher || s:regexp) From 2d79500a35f4c8163ee44badb8eb090fbfb57487 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Mon, 3 Mar 2014 13:00:33 +0200 Subject: [PATCH 14/41] start showing the items --- python/ctrlp/matcher.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 1605e0b7..5fb03cad 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -18,14 +18,24 @@ def __init__(self, debug=False): self.logger.setLevel(logging.DEBUG) def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname=False): + limit = int(limit) if limit else None + if not pat: + self.logger.debug("No pattern, returning original items") + self.lastPat = None + self.queue.put(items[:limit], timeout=1) + + self.process(pat) + + return + self.logger.debug("Filtering {number} items using {pat}".format(number = len(items), pat=pat)) processed = False - if self.process(): + if self.process(pat): processed = True if self.lastPat == pat: - if self.process() and self.queue.qsize() == 0 and not self.thread.isAlive(): + if self.process(pat) and self.queue.qsize() == 0 and not self.thread.isAlive(): self.logger.debug("Thread job is processed for {pat}".format(pat=pat)) self.lastPat = None elif not processed: @@ -46,7 +56,7 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= self.lastPat = pat self.forceCursorHold() - def process(self): + def process(self, pat): try: lines = self.queue.get(False) self.queue.task_done() From 6d0dcd6b31cfa8ff375949b41d731e7534305315 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Tue, 4 Mar 2014 13:54:09 +0200 Subject: [PATCH 15/41] fix case matching --- autoload/ctrlp.vim | 1 + python/ctrlp/matcher.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index b3a71110..b855f2cf 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -145,6 +145,7 @@ let [s:lcmap, s:prtmaps] = ['nn ', { \ }] let s:scriptpath = expand(':p:h') +let s:pymatcher = 0 if has('autocmd') && has('python') py import sys exe 'python sys.path.insert( 0, "' . escape(s:scriptpath, '\') . '/../python" )' diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 5fb03cad..f52c8d51 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -38,7 +38,7 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= if self.process(pat) and self.queue.qsize() == 0 and not self.thread.isAlive(): self.logger.debug("Thread job is processed for {pat}".format(pat=pat)) self.lastPat = None - elif not processed: + elif not processed and self.thread.isAlive(): self.logger.debug("Waiting for thread job for {pat}".format(pat=pat)) self.forceCursorHold() else: @@ -71,6 +71,7 @@ def process(self, pat): return False def forceCursorHold(self): + # TODO: needs to be a function in the vimscript col = vim.eval("col('.')") if col == 1: @@ -88,7 +89,7 @@ def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname if ic: if scs: upper = any(c.isupper() for c in pat) - if upper: + if not upper: flags = re.I else: flags = re.I @@ -122,17 +123,17 @@ def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname dirname = os.path.dirname(item) basename = os.path.basename(item) - match = patterns[0].match(basename) + match = patterns[0].search(basename) if len(patterns) == 2 and match is not None: - match = patterns[1].match(dirname) + match = patterns[1].search(dirname) else: if itemtype > 2 and mtype == 'tabs': - match = patterns[1].match(re.split('\t+', item)[0]) + match = patterns[1].search(re.split('\t+', item)[0]) elif itemtype > 2 and mtype == 'tabe': - match = patterns[1].match(re.split('\t+[^\t]+$', item)[0]) + match = patterns[1].search(re.split('\t+[^\t]+$', item)[0]) else: - match = patterns[0].match(item) + match = patterns[0].search(item) if match is None: continue From 1362cc2db2830f71095a71a4fd91a1da11b99392 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Tue, 4 Mar 2014 14:09:44 +0200 Subject: [PATCH 16/41] set the metadata on process --- autoload/ctrlp.vim | 13 +++++++++---- python/ctrlp/matcher.py | 11 ++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index b855f2cf..862826c4 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -478,9 +478,8 @@ fu! s:MatchIt(items, pat, limit, exc) en | cat | brea | endt if a:limit > 0 && len(lines) >= a:limit | brea | en endfo - let s:mdata = [s:dyncwd, s:itemtype, s:regexp, s:sublist(a:items, id, -1)] - cal ctrlp#process(lines, a:pat) + cal ctrlp#process(lines, a:pat, 0, s:sublist(a:items, id, -1)) endf fu! s:MatchedItems(items, pat, limit) @@ -2284,11 +2283,17 @@ fu! ctrlp#init(type, ...) if s:keyloop | cal s:KeyLoop() | en endf -fu! ctrlp#process(lines, pat) +fu! ctrlp#process(lines, pat, split, subitems) let s:matches = len(a:lines) unl! s:did_exp - cal s:Render(a:lines, a:pat) + let pat = a:pat + + if a:split | let pat = s:SplitPattern(pat) | en + + let s:mdata = [s:dyncwd, s:itemtype, s:regexp, a:subitems] + + cal s:Render(a:lines, pat) endf " - Autocmds {{{1 if has('autocmd') diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index f52c8d51..3bd5d473 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -22,7 +22,7 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= if not pat: self.logger.debug("No pattern, returning original items") self.lastPat = None - self.queue.put(items[:limit], timeout=1) + self.queue.put({"items": items[:limit], "subitems": items[limit-1:]}, timeout=1) self.process(pat) @@ -58,13 +58,14 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= def process(self, pat): try: - lines = self.queue.get(False) + data = self.queue.get(False) self.queue.task_done() callback = vim.bindeval('function("ctrlp#process")') - lines = vim.List(lines) + lines = vim.List(data["items"]) + subitems = vim.List(data["subitems"]) - callback(lines, pat) + callback(lines, pat, 1, subitems) return True except Empty: @@ -143,5 +144,5 @@ def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname if limit > 0 and len(matchedItems) >= limit: break - queue.put(matchedItems, timeout=1) + queue.put({"items": matchedItems, "subitems": items[itemId:]}, timeout=1) logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) From 2c609b17dd73869ee781c0da4c5f77107d2f0493 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Tue, 4 Mar 2014 23:17:17 +0200 Subject: [PATCH 17/41] create a cursorhold refresh function --- autoload/ctrlp.vim | 11 +++++++++++ python/ctrlp/matcher.py | 7 +------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 862826c4..1d62df03 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -2295,6 +2295,17 @@ fu! ctrlp#process(lines, pat, split, subitems) cal s:Render(a:lines, pat) endf + +fu! ctrlp#forcecursorhold() + if s:prompt[0] + cal feedkeys("\\") + elsei s:prompt[2] + cal feedkeys("\\") + el + cal feedkeys("\") + en +endf + " - Autocmds {{{1 if has('autocmd') aug CtrlPAug diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 3bd5d473..fe850566 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -72,13 +72,8 @@ def process(self, pat): return False def forceCursorHold(self): - # TODO: needs to be a function in the vimscript - col = vim.eval("col('.')") + vim.bindeval('function("ctrlp#forcecursorhold")')() - if col == 1: - vim.command('call feedkeys("\\")') - else: - vim.command('call feedkeys("\\")') def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname, ic, scs, logger): chars = [re.escape(c) for c in pat] From 45fabcb10b5a73fa88bef0d12f43d7ff0bd53d51 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Tue, 4 Mar 2014 23:32:51 +0200 Subject: [PATCH 18/41] always show the matches in order --- python/ctrlp/matcher.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index fe850566..d09c6200 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -6,6 +6,7 @@ class CtrlPMatcher: def __init__(self, debug=False): self.queue = Queue() + self.patterns = [] self.lastPat = None self.logger = logging.getLogger('ctrlp') @@ -22,7 +23,7 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= if not pat: self.logger.debug("No pattern, returning original items") self.lastPat = None - self.queue.put({"items": items[:limit], "subitems": items[limit-1:]}, timeout=1) + self.queue.put({"items": items[:limit], "subitems": items[limit-1:], "pat": ""}, timeout=1) self.process(pat) @@ -30,21 +31,20 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= self.logger.debug("Filtering {number} items using {pat}".format(number = len(items), pat=pat)) - processed = False - if self.process(pat): - processed = True + self.process(pat) if self.lastPat == pat: if self.process(pat) and self.queue.qsize() == 0 and not self.thread.isAlive(): self.logger.debug("Thread job is processed for {pat}".format(pat=pat)) self.lastPat = None - elif not processed and self.thread.isAlive(): + elif self.thread.isAlive() or self.queue.qsize() > 0: self.logger.debug("Waiting for thread job for {pat}".format(pat=pat)) self.forceCursorHold() else: self.logger.debug("The same pattern '{pat}'".format(pat=pat)) elif pat: self.logger.debug("Starting thread for {pat}".format(pat=pat)) + self.patterns.append(pat) self.thread = Thread(target=threadWorker, args=( self.queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname, vim.eval('&ic'), vim.eval('&scs'), @@ -61,12 +61,21 @@ def process(self, pat): data = self.queue.get(False) self.queue.task_done() + try: + index = self.patterns.index(data["pat"]) + self.patterns = self.patterns[index+1:] + except ValueError: + return False + callback = vim.bindeval('function("ctrlp#process")') lines = vim.List(data["items"]) subitems = vim.List(data["subitems"]) callback(lines, pat, 1, subitems) + if data["pat"] == pat: + self.queue = Queue() + return True except Empty: return False @@ -139,5 +148,5 @@ def threadWorker(queue, items, pat, limit, exc, itemtype, mtype, ispath, byfname if limit > 0 and len(matchedItems) >= limit: break - queue.put({"items": matchedItems, "subitems": items[itemId:]}, timeout=1) + queue.put({"items": matchedItems, "subitems": items[itemId:], "pat": pat}, timeout=1) logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) From 7d2f28801d13d5cbf316e3f5c5a9ae992b70deca Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Wed, 5 Mar 2014 11:48:02 +0200 Subject: [PATCH 19/41] do not process linues if the buffer doesn't exist --- autoload/ctrlp.vim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 1d62df03..c8f202bf 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -2284,6 +2284,8 @@ fu! ctrlp#init(type, ...) endf fu! ctrlp#process(lines, pat, split, subitems) + if !exists('s:init') | retu | en + let s:matches = len(a:lines) unl! s:did_exp From d778be6b88e20801f04a0c6f4afee1f4aff59312 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Wed, 5 Mar 2014 11:56:23 +0200 Subject: [PATCH 20/41] clear the sequence if there is no pattern --- python/ctrlp/matcher.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index d09c6200..2d4e6a18 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -22,7 +22,6 @@ def filter(self, items, pat, limit, exc, itemtype, mtype, ispath=False, byfname= limit = int(limit) if limit else None if not pat: self.logger.debug("No pattern, returning original items") - self.lastPat = None self.queue.put({"items": items[:limit], "subitems": items[limit-1:], "pat": ""}, timeout=1) self.process(pat) @@ -62,8 +61,12 @@ def process(self, pat): self.queue.task_done() try: - index = self.patterns.index(data["pat"]) - self.patterns = self.patterns[index+1:] + if data["pat"]: + index = self.patterns.index(data["pat"]) + self.patterns = self.patterns[index+1:] + else: + self.lastPat = None + self.patterns = [] except ValueError: return False From fe16d70ba9902cbf8ae3963bd5587dd23dd52294 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Wed, 5 Mar 2014 17:10:25 +0200 Subject: [PATCH 21/41] make the python interface be the same as the matcher plugin --- autoload/ctrlp.vim | 5 ++--- python/ctrlp/matcher.py | 27 +++++++++++++-------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index c8f202bf..14c77582 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -499,9 +499,8 @@ fu! s:MatchedItems(items, pat, limit) call(s:matcher['match'], argms, s:matcher) elsei s:pymatcher && !s:regexp py < 2 and mtype == 'tabs': - match = patterns[1].search(re.split('\t+', item)[0]) - elif itemtype > 2 and mtype == 'tabe': - match = patterns[1].search(re.split('\t+[^\t]+$', item)[0]) - else: - match = patterns[0].search(item) + match = patterns[0].search(item) if match is None: continue From 64fc4445bf57af6df73ce63809747bd5a480c772 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Wed, 5 Mar 2014 17:17:48 +0200 Subject: [PATCH 22/41] allow matcher plugins to force the update --- autoload/ctrlp.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 14c77582..67c2f4c8 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -588,7 +588,9 @@ fu! s:Update(str) " Get the new string sans tail let str = s:sanstail(a:str) " Stop if the string's unchanged - if str == oldstr && !empty(str) && !exists('s:force') && (!s:pymatcher || s:regexp) + if str == oldstr && !empty(str) && !exists('s:force') + \ && (!has_key(s:matcher, 'force_update') || s:matcher['force_update'] == 1) + \ && (!s:pymatcher || s:regexp) retu en let s:martcs = &scs && str =~ '\u' ? '\C' : '' From aff18e8848210498f43540bb087f3a95b978aa77 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Fri, 7 Mar 2014 01:29:20 +0200 Subject: [PATCH 23/41] sort the matched items in python for more accuracy --- python/ctrlp/matcher.py | 67 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 65fac005..04baa03d 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -145,10 +145,69 @@ def threadWorker(queue, items, pat, limit, mmode, ispath, crfile, regexp, ic, sc if match is None: continue - matchedItems.append(item) + span = match.span() + matchedItems.append({"line": item, "matlen": span[1] - span[0]}) - if limit > 0 and len(matchedItems) >= limit: - break + matchedItems = sorted(matchedItems, cmp=sortItems(crfile, mmode, ispath, len(matchedItems))) + if limit > 0: + matchedItems = matchedItems[:limit] - queue.put({"items": matchedItems, "subitems": items[itemId:], "pat": pat}, timeout=1) + queue.put({"items": [i["line"] for i in matchedItems], "subitems": items[itemId:], "pat": pat}, timeout=1) logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) + +def sortItems(crfile, mmode, ispath, total): + crdir = os.path.dirname(crfile) + + def cmpFunc(a, b): + line1 = a["line"] + line2 = b["line"] + len1 = len(line1) + len2 = len(line2) + + lanesort = 0 if len1 == len2 else 1 if len1 > len2 else -1 + + len1 = a["matlen"] + len2 = b["matlen"] + + patsort = 0 if len1 == len2 else 1 if len1 > len2 else -1 + + if ispath: + ms = [] + + fnlen = 0 + mtime = 0 + pcomp = 0 + + if total < 21: + len1 = len(os.path.basename(line1)) + len2 = len(os.path.basename(line2)) + fnlen = 0 if len1 == len2 else 1 if len1 > len2 else -1 + + if mmode == 'full-line': + try: + len1 = os.path.getmtime(line1) + len2 = os.path.getmtime(line2) + mtime = 0 if len1 == len2 else 1 if len1 > len2 else -1 + + dir1 = os.path.dirname(line1) + dir2 = os.path.dirname(line2) + + if dir1.endswith(crdir) and not dir2.endswith(crdir): + pcomp = -1 + elif dir2.endswith(crdir) and not dir1.endswith(crdir): + pcomp = 1 + + except OSError: + pass + + ms.extend([fnlen, mtime, pcomp, patsort]) + mp = [2 if ms[0] else 0] + mp.append(1 + (mp[0] if mp[0] else 1) if ms[1] else 0) + mp.append(1 + (mp[0] + mp[1] if mp[0] + mp[1] else 1) if ms[2] else 0) + mp.append(1 + (mp[0] + mp[1] + mp[2] if mp[0] + mp[1] + mp[2] else 1) if ms[3] else 0) + + return lanesort + reduce(lambda x, y: x + y[0]*y[1], zip(ms, mp), 0) + else: + return lanesort + patsort * 2 + + return cmpFunc From 38a83fff16b72c0d0221b230ecba42bc077a220c Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Fri, 7 Mar 2014 02:32:33 +0200 Subject: [PATCH 24/41] python module for translating vim regex syntax to python --- python/ctrlp/regex.py | 309 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 python/ctrlp/regex.py diff --git a/python/ctrlp/regex.py b/python/ctrlp/regex.py new file mode 100644 index 00000000..d3385cdb --- /dev/null +++ b/python/ctrlp/regex.py @@ -0,0 +1,309 @@ +import re + +def from_vim(pat, ignorecase=False, smartcase=False): + r""" + Returns a pattern object based on the vim-style regular expression pattern string + + >>> from_vim('\w\+').pattern == '\w+' + True + >>> from_vim('\w\+').flags + 0 + >>> from_vim('\c\w\+').flags + 2 + >>> from_vim('\C\w\+').flags + 0 + >>> from_vim('\C\w\+', ignorecase=True).flags + 0 + >>> from_vim('\w\+', ignorecase=True).flags + 2 + >>> from_vim('a', ignorecase=True, smartcase=True).flags + 2 + >>> from_vim('A', ignorecase=True, smartcase=True).flags + 0 + >>> from_vim('foo\=').pattern == 'foo?' + True + >>> from_vim('foo\?').pattern == 'foo?' + True + >>> from_vim('foo\{1,5}b').pattern == 'foo{1,5}b' + True + >>> from_vim('foo\{1,\}b').pattern == 'foo{1,}b' + True + >>> from_vim('foo\{-\}b').pattern == 'foo*?b' + True + >>> from_vim('foo\{-,1\}b').pattern == 'foo??b' + True + >>> from_vim('foo\{-1,\}b').pattern == 'foo+?b' + True + >>> from_vim('foo{1,}b').pattern == 'foo\{1,}b' + True + >>> from_vim('foo\>').pattern == r'foo\b' + True + >>> from_vim('\>> from_vim('\').pattern == r'\bfoo\b' + True + >>> from_vim('foo\|bar').pattern == r'foo|bar' + True + >>> from_vim('\(foo\)').pattern == r'(foo)' + True + >>> from_vim('\(f(o)o\)').pattern == r'(f\(o\)o)' + True + >>> from_vim(r'\%(foo\)').pattern == r'(?:foo)' + True + >>> from_vim(r'\%(fo\(oba\)r\)').pattern == r'(?:fo(oba)r)' + True + >>> from_vim(r'foo\@=').pattern == r'fo(?=o)' + True + >>> from_vim('\(foo\)\@=').pattern == r'(?=foo)' + True + >>> from_vim(r'\%(foo\)\@=').pattern == r'(?=foo)' + True + >>> from_vim('foo\@!').pattern == r'fo(?!o)' + True + >>> from_vim('\(foo\)\@<=').pattern == r'(?<=foo)' + True + >>> from_vim(r'\%(foo\)\@>> from_vim(r'[a-z]').pattern == r'[a-z]' + True + """ + + flags = 0 + + if pat.find("\c") != -1: + pat = pat.replace("\c", "") + flags |= re.IGNORECASE + elif pat.find("\C") != -1: + pat = pat.replace("\C", "") + elif ignorecase: + if smartcase: + if not any(c.isupper() for c in pat): + flags |= re.IGNORECASE + else: + flags |= re.IGNORECASE + + regex = process_group(pat) + + return re.compile(regex, flags) + +def process_group(pat): + special = False + index = 0 + + regex = r"" + incurly = False + nongreedy = False + nomemory = False + + skip = {} + + for char in pat: + try: + if skip[index]: + index += 1 + continue + except KeyError: + pass + + if special: + special = False + + if char == r'+': + regex += char + elif char == r'=' or char == r'?': + regex += r'?' + elif char == r'<' or char == r'>': + regex += r'\b' + elif char == r'|': + regex += char + elif char == r'{': + if pat[index+1] == '-': + skip[index+1] = True + + if non_greedy_skip(pat, index, skip): + regex += r'*?' + elif pat[index+2] == '1' and pat[index+3] == ',' and non_greedy_skip(pat, index + 2, skip): + skip.update(skip.fromkeys([index+2, index+3], True)) + regex += r'+?' + elif pat[index+2] == ',' and pat[index+3] == '1' and non_greedy_skip(pat, index + 2, skip): + skip.update(skip.fromkeys([index+2, index+3], True)) + regex += r'??' + else: + nongreedy = True + else: + incurly = True + regex += r'{' + + elif char == r'%': + if pat[index+1] == '(': + special = True + nomemory = True + elif char == r'(': + closing = find_matching(pat, index, r'\(', r'\)') + + regex += r'(' + if pat[closing+2:closing+5] == r'\@=': + regex += r'?=' + skip.update(skip.fromkeys([closing+2, closing+3, closing+4], True)) + elif pat[closing+2:closing+5] == r'\@!': + regex += r'?!' + skip.update(skip.fromkeys([closing+2, closing+3, closing+4], True)) + elif pat[closing+2:closing+6] == r'\@<=': + regex += r'?<=' + skip.update(skip.fromkeys([closing+2, closing+3, closing+4, closing+5], True)) + elif pat[closing+2:closing+6] == r'\@>> find_matching("foo \( bar \) alpha", 5, "\(", "\)") + 11 + >>> find_matching("foo \( bar alpha", 5, "\(", "\)") + -1 + >>> find_matching("foo \( bar \( inner 1 \) after \) alpha", 5, "\(", "\)") + 31 + >>> find_matching("foo \( bar \( in \( n \) er 1 \) after \) alpha", 5, "\(", "\)") + 39 + >>> find_matching("foo \( bar \( inner 1 \) after \) alpha \( another \( one \) no \) yes ", 5, "\(", "\)") + 31 + >>> find_matching("foo \( bar \( inner 1 after \) alpha", 5, "\(", "\)") + 28 + >>> find_matching(r"foo \( bar \\) inner 1 after \) alpha", 5, "\(", "\)") + 29 + """ + + cursor = end = start - len(opening) + + while True: + while end != -1: + end = string.find(closing, end) + if end != -1 and is_escaped(string, end): + end += 1 + else: + break + + while cursor != -1: + cursor = string.find(opening, cursor) + if cursor != -1 and is_escaped(string, cursor): + cursor += 1 + else: + break + + if not start: + start = cursos + + if end < 0: + end = string.rfind(closing) + break + + next_cursor = cursor + 1 + while next_cursor != -1: + next_cursor = string.find(opening, next_cursor) + if next_cursor != -1 and is_escaped(string, next_cursor): + next_cursor = next_cursor + 1 + else: + break + + if cursor > -1 and next_cursor > -1 and next_cursor < end and cursor < end: + cursor += 1 + end += 1 + else: + break + + return end + +def is_escaped(string, position): + r""" + Checks whether the entity at the given position is escaped + + >>> is_escaped(r"foo bar", 4) + False + >>> is_escaped(r"foo \\bar", 5) + True + >>> is_escaped(r"foo \\\\bar", 6) + False + >>> is_escaped(r"foo \\\\\\bar", 7) + True + """ + + chars = list(string[:position]) + chars.reverse() + + counter = 0 + for c in chars: + if c == '\\': + counter += 1 + else: + break + + return counter % 2 != 0 + +def non_greedy_skip(chars, index, skip): + if chars[index+2] == '}': + skip[index+2] = True + return True + elif chars[index+2] == '\\' and chars[index+3] == '}': + skip[index+2] = True + skip[index+3] = True + return True + skip[index+2] = True + + return False + +if __name__ == "__main__": + import doctest + doctest.testmod() From c16f1b9776da0d1117181df099685267e23290d6 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Fri, 7 Mar 2014 11:42:55 +0200 Subject: [PATCH 25/41] turn on regex support --- autoload/ctrlp.vim | 6 +-- python/ctrlp/matcher.py | 88 ++++++++++++++++++++++++----------------- python/ctrlp/regex.py | 8 ++-- 3 files changed, 59 insertions(+), 43 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 67c2f4c8..01c5a1f6 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -497,7 +497,7 @@ fu! s:MatchedItems(items, pat, limit) \ 'regex': s:regexp, \ }] : [items, a:pat, a:limit, s:mmode(), s:ispath, exc, s:regexp] call(s:matcher['match'], argms, s:matcher) - elsei s:pymatcher && !s:regexp + elsei s:pymatcher && s:lazy > 1 py < 0: self.logger.debug("Waiting for thread job for {pat}".format(pat=pat)) self.forceCursorHold() @@ -44,7 +54,7 @@ def filter(self, items, pat, limit, mmode, ispath, crfile, regexp): elif pat: self.logger.debug("Starting thread for {pat}".format(pat=pat)) self.patterns.append(pat) - self.thread = Thread(target=threadWorker, args=( + self.thread = Thread(target=thread_worker, args=( self.queue, items, pat, limit, mmode, ispath, crfile, regexp, vim.eval('&ic'), vim.eval('&scs'), self.logger @@ -52,7 +62,7 @@ def filter(self, items, pat, limit, mmode, ispath, crfile, regexp): self.thread.daemon = True self.thread.start() - self.lastPat = pat + self.lastpat = pat self.forceCursorHold() def process(self, pat): @@ -65,7 +75,7 @@ def process(self, pat): index = self.patterns.index(data["pat"]) self.patterns = self.patterns[index+1:] else: - self.lastPat = None + self.lastpat = None self.patterns = [] except ValueError: return False @@ -87,37 +97,41 @@ def forceCursorHold(self): vim.bindeval('function("ctrlp#forcecursorhold")')() -def threadWorker(queue, items, pat, limit, mmode, ispath, crfile, regexp, ic, scs, logger): - chars = [re.escape(c) for c in pat] +def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, ic, scs, logger): + if ispath and mmode == 'filename-only': + semi = 0 + while semi != -1: + semi = pat.find(';', semi) + if semi != -1 and is_escaped(pat, semi): + semi += 1 + else: + break + else: + semi = -1 - patterns = [] - builder = lambda c: c + '[^' + c + ']*?' + if semi != -1: + pats = [pat[:semi], pat[semi+1:]] if pat[semi+1:] else [pat[:semi]] + else: + pats = [pat] - flags = 0 - if ic: - if scs: - upper = any(c.isupper() for c in pat) - if not upper: + patterns = [] + if regexp: + patterns = [from_vim(p, ignorecase=ic, smartcase=scs) for p in pats] + else: + if ic: + if scs: + upper = any(c.isupper() for c in pat) + if not upper: + flags = re.I + else: flags = re.I - else: - flags = re.I - - try: - if mmode == 'filename-only': - delim = chars.index(';') - logger.debug("Creating filename patterns") - filechars = chars[:delim] - dirchars = chars[delim+1:] - patterns.append(re.compile(''.join(map(builder, filechars)), flags)) - if dirchars: - patterns.append(re.compile(''.join(map(builder, dirchars)), flags)) - except ValueError: - pass + for p in pats: + chars = [re.escape(c) for c in pat] + builder = lambda c: c + '[^' + c + ']*?' + flags = 0 - if not len(patterns): - patterns.append(re.compile(''.join(map(builder, chars)), flags)) - logger.debug("Creating normal patterns") + patterns.append(re.compile(''.join(map(builder, chars)), flags)) itemId = 0 matchedItems = [] @@ -148,17 +162,17 @@ def threadWorker(queue, items, pat, limit, mmode, ispath, crfile, regexp, ic, sc span = match.span() matchedItems.append({"line": item, "matlen": span[1] - span[0]}) - matchedItems = sorted(matchedItems, cmp=sortItems(crfile, mmode, ispath, len(matchedItems))) + matchedItems = sorted(matchedItems, cmp=sort_items(crfile, mmode, ispath, len(matchedItems))) if limit > 0: matchedItems = matchedItems[:limit] queue.put({"items": [i["line"] for i in matchedItems], "subitems": items[itemId:], "pat": pat}, timeout=1) logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) -def sortItems(crfile, mmode, ispath, total): +def sort_items(crfile, mmode, ispath, total): crdir = os.path.dirname(crfile) - def cmpFunc(a, b): + def cmp_func(a, b): line1 = a["line"] line2 = b["line"] len1 = len(line1) @@ -210,4 +224,4 @@ def cmpFunc(a, b): else: return lanesort + patsort * 2 - return cmpFunc + return cmp_func diff --git a/python/ctrlp/regex.py b/python/ctrlp/regex.py index d3385cdb..ec97115f 100644 --- a/python/ctrlp/regex.py +++ b/python/ctrlp/regex.py @@ -179,9 +179,11 @@ def process_group(pat): regex += atom + r')' else: - regex += '\\' + char - elif char == '\\': - if pat[index+1] == '}'and incurly: + regex += r'\\' + char + elif char == r'\\': + if len(pat) == index + 1: + regex += r'\\' + elif pat[index+1] == '}'and incurly: special = False else: special = True From 66f468c1c764f234ddb75da3d12b61698f93396c Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Fri, 7 Mar 2014 12:02:18 +0200 Subject: [PATCH 26/41] fix processing of \\ --- python/ctrlp/regex.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/ctrlp/regex.py b/python/ctrlp/regex.py index ec97115f..c0b21cc2 100644 --- a/python/ctrlp/regex.py +++ b/python/ctrlp/regex.py @@ -6,6 +6,8 @@ def from_vim(pat, ignorecase=False, smartcase=False): >>> from_vim('\w\+').pattern == '\w+' True + >>> from_vim('foo\\').pattern == r'foo\\' + True >>> from_vim('\w\+').flags 0 >>> from_vim('\c\w\+').flags @@ -179,8 +181,8 @@ def process_group(pat): regex += atom + r')' else: - regex += r'\\' + char - elif char == r'\\': + regex += '\\' + char + elif char == '\\': if len(pat) == index + 1: regex += r'\\' elif pat[index+1] == '}'and incurly: From 334fa377f964aec5dc8d1bfcd14ef54db48a17f1 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Fri, 7 Mar 2014 13:38:07 +0200 Subject: [PATCH 27/41] use bindeval to get the corrent type --- autoload/ctrlp.vim | 6 ++++-- python/ctrlp/matcher.py | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 01c5a1f6..36a061bd 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -499,8 +499,10 @@ fu! s:MatchedItems(items, pat, limit) call(s:matcher['match'], argms, s:matcher) elsei s:pymatcher && s:lazy > 1 py < Date: Fri, 7 Mar 2014 17:59:27 +0200 Subject: [PATCH 28/41] correctly force a new cursorhold without changing the cursor --- autoload/ctrlp.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 36a061bd..34f5546d 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -2302,9 +2302,9 @@ fu! ctrlp#process(lines, pat, split, subitems) endf fu! ctrlp#forcecursorhold() - if s:prompt[0] + if len(s:prompt[0]) cal feedkeys("\\") - elsei s:prompt[2] + elsei len(s:prompt[2]) cal feedkeys("\\") el cal feedkeys("\") From 5028ff4b48f207a67ceb124701e2cda589a43e11 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Fri, 7 Mar 2014 18:09:03 +0200 Subject: [PATCH 29/41] fix the flag initialization --- python/ctrlp/matcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index f511c389..1c074fa8 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -119,6 +119,7 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, ic, s patterns = [from_vim(p, ignorecase=ic, smartcase=scs) for p in pats] else: logger.debug("Fuzzy matching") + flags = 0 if ic: if scs: upper = any(c.isupper() for c in pat) @@ -130,7 +131,6 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, ic, s for p in pats: chars = [re.escape(c) for c in pat] builder = lambda c: c + '[^' + c + ']*?' - flags = 0 patterns.append(re.compile(''.join(map(builder, chars)), flags)) From b4a1e15c77fb905c27f2d50fac11af742d29bcd2 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Fri, 7 Mar 2014 22:42:24 +0200 Subject: [PATCH 30/41] use the correct string when creating a pattern --- python/ctrlp/matcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 1c074fa8..f6f6fe83 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -129,7 +129,7 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, ic, s flags = re.I for p in pats: - chars = [re.escape(c) for c in pat] + chars = [re.escape(c) for c in p] builder = lambda c: c + '[^' + c + ']*?' patterns.append(re.compile(''.join(map(builder, chars)), flags)) From ebf2ab76c0be563b10610ba6f3098a14eea75f45 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sat, 8 Mar 2014 15:07:57 +0200 Subject: [PATCH 31/41] do not sort if the pymatcher is used --- autoload/ctrlp.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 34f5546d..d7daf999 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -1929,7 +1929,8 @@ fu! s:modevar() endf fu! s:nosort() - retu s:matcher != {} || s:nolim == 1 || ( s:itemtype == 2 && s:mrudef ) + retu s:matcher != {} || (s:pymatcher && s:lazy > 1) + \ || s:nolim == 1 || ( s:itemtype == 2 && s:mrudef ) \ || ( s:itemtype =~ '\v^(1|2)$' && s:prompt == ['', '', ''] ) || !s:dosort endf From 2f349f92c337f975d71d611da911002e7b4e6e99 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sat, 8 Mar 2014 20:08:26 +0200 Subject: [PATCH 32/41] give priority to mru files --- python/ctrlp/matcher.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index f6f6fe83..84cb28b7 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -53,9 +53,13 @@ def filter(self, items, pat, limit, mmode, ispath, crfile, regexp): elif pat: self.logger.debug("Starting thread for {pat}".format(pat=pat)) self.patterns.append(pat) + + mru = vim.bindeval('ctrlp#mrufiles#list()') + mru = list(mru)[:50] if isinstance(mru, vim.List) else [] + self.thread = Thread(target=thread_worker, args=( self.queue, items, pat, limit, - mmode, ispath, crfile, regexp, + mmode, ispath, crfile, regexp, mru, vim.bindeval('&ic'), vim.bindeval('&scs'), self.logger )) self.thread.daemon = True @@ -96,7 +100,7 @@ def forceCursorHold(self): vim.bindeval('function("ctrlp#forcecursorhold")')() -def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, ic, scs, logger): +def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, ic, scs, logger): if ispath and mmode == 'filename-only': semi = 0 while semi != -1: @@ -163,14 +167,20 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, ic, s span = match.span() matchedItems.append({"line": item, "matlen": span[1] - span[0]}) - matchedItems = sorted(matchedItems, cmp=sort_items(crfile, mmode, ispath, len(matchedItems))) + mrudict = {} + index = 0 + for f in mru: + mrudict[f] = index + index += 1 + + matchedItems = sorted(matchedItems, cmp=sort_items(crfile, mmode, ispath, mrudict, len(matchedItems))) if limit > 0: matchedItems = matchedItems[:limit] queue.put({"items": [i["line"] for i in matchedItems], "subitems": items[itemId:], "pat": pat}, timeout=1) logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) -def sort_items(crfile, mmode, ispath, total): +def sort_items(crfile, mmode, ispath, mrudict, total): crdir = os.path.dirname(crfile) def cmp_func(a, b): @@ -211,15 +221,24 @@ def cmp_func(a, b): pcomp = -1 elif dir2.endswith(crdir) and not dir1.endswith(crdir): pcomp = 1 - + except OSError: pass - ms.extend([fnlen, mtime, pcomp, patsort]) + mrucomp = 0 + if mrudict: + len1 = mrudict.get(line1, -1) + len2 = mrudict.get(line2, -1) + + mrucomp = 0 if len1 == len2 else 1 if len1 == -1 else -1 if len2 == -1 \ + else 1 if len1 > len2 else -1 + + ms.extend([fnlen, mtime, pcomp, patsort, mrucomp]) mp = [2 if ms[0] else 0] mp.append(1 + (mp[0] if mp[0] else 1) if ms[1] else 0) mp.append(1 + (mp[0] + mp[1] if mp[0] + mp[1] else 1) if ms[2] else 0) mp.append(1 + (mp[0] + mp[1] + mp[2] if mp[0] + mp[1] + mp[2] else 1) if ms[3] else 0) + mp.append(1 + (mp[0] + mp[1] + mp[2] + mp[3] if mp[0] + mp[1] + mp[2] + mp[3] else 1) if ms[4] else 0) return lanesort + reduce(lambda x, y: x + y[0]*y[1], zip(ms, mp), 0) else: From a98723acc542902e158af85ad321e0dd178d1543 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sat, 8 Mar 2014 20:09:43 +0200 Subject: [PATCH 33/41] turn off debugging --- autoload/ctrlp.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index d7daf999..74b93ec2 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -150,7 +150,7 @@ if has('autocmd') && has('python') py import sys exe 'python sys.path.insert( 0, "' . escape(s:scriptpath, '\') . '/../python" )' py from ctrlp.matcher import CtrlPMatcher - py ctrlp = CtrlPMatcher(debug=True) + py ctrlp = CtrlPMatcher() let s:pymatcher = 1 en From 43a627862141ff5bc37c0d91eb6e87f99ecba1ae Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sat, 8 Mar 2014 22:38:45 +0200 Subject: [PATCH 34/41] fix the tag filter --- python/ctrlp/matcher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 84cb28b7..8aabc6ea 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -155,9 +155,9 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, if len(patterns) == 2 and match is not None: match = patterns[1].search(dirname) elif mmode == 'first-non-tab': - match = patterns[1].search(re.split('\t+', item)[0]) + match = patterns[0].search(re.split('\t+', item)[0]) elif mmode == 'until-last-tab': - match = patterns[1].search(re.split('\t+[^\t]+$', item)[0]) + match = patterns[0].search(re.split('\t+[^\t]+$', item)[0]) else: match = patterns[0].search(item) From a77c7337411da5b598bc0b84b9ea3091b7d87b94 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sat, 8 Mar 2014 23:57:56 +0200 Subject: [PATCH 35/41] only process the last entry in the done queue limit the number of matched items prior to sorting to trade accuracy for performance --- python/ctrlp/matcher.py | 64 ++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 8aabc6ea..59f7f0fb 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -18,6 +18,7 @@ def __init__(self, debug=False): self.queue = Queue() self.patterns = [] self.lastpat = None + self.thread = None self.logger = logging.getLogger('ctrlp') hdlr = logging.FileHandler(os.path.join(tempfile.gettempdir(), 'ctrlp-py.log')) @@ -66,36 +67,53 @@ def filter(self, items, pat, limit, mmode, ispath, crfile, regexp): self.thread.start() self.lastpat = pat + self.forceCursorHold() def process(self, pat): - try: - data = self.queue.get(False) - self.queue.task_done() - + queue = [] + while True: try: - if data["pat"]: - index = self.patterns.index(data["pat"]) - self.patterns = self.patterns[index+1:] - else: - self.lastpat = None - self.patterns = [] - except ValueError: - return False + queue.append(self.queue.get(False)) + self.queue.task_done() + except Empty: + break - callback = vim.bindeval('function("ctrlp#process")') - lines = vim.List(data["items"]) - subitems = vim.List(data["subitems"]) + if not queue: + self.logger.debug("Empty queue") + return False - callback(lines, pat, 1, subitems) + data = None - if data["pat"] == pat: - self.queue = Queue() + for d in queue: + if d["pat"]: + try: + index = self.patterns.index(d["pat"]) + self.patterns = self.patterns[index+1:] + data = d + except ValueError: + continue + else: + data = d + self.lastpat = None + self.patterns = [] + break - return True - except Empty: + if not data: + self.logger.debug("No valid data entry") return False + callback = vim.bindeval('function("ctrlp#process")') + lines = vim.List(data["items"]) + subitems = vim.List(data["subitems"]) + + callback(lines, pat, 1, subitems) + + if data["pat"] == pat: + self.queue = Queue() + + return True + def forceCursorHold(self): vim.bindeval('function("ctrlp#forcecursorhold")')() @@ -139,6 +157,7 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, patterns.append(re.compile(''.join(map(builder, chars)), flags)) itemId = 0 + index = 0 matchedItems = [] logger.debug("Matching against {number} items using {pat}".format(number=len(items), pat=pat)) for item in items: @@ -167,6 +186,11 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, span = match.span() matchedItems.append({"line": item, "matlen": span[1] - span[0]}) + if limit and index >= limit * 3: + break + + index += 1 + mrudict = {} index = 0 for f in mru: From 2ab408fc64f7101b9f136ac9c48051d8ee728907 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 9 Mar 2014 00:05:04 +0200 Subject: [PATCH 36/41] check for differences in the mode and path that way changing from directory to file based searching will trigger an update --- python/ctrlp/matcher.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 59f7f0fb..4bdd879d 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -17,9 +17,12 @@ def __init__(self, debug=False): self.queue = Queue() self.patterns = [] - self.lastpat = None self.thread = None + self.lastpat = None + self.lastmmode = None + self.lastispath = None + self.logger = logging.getLogger('ctrlp') hdlr = logging.FileHandler(os.path.join(tempfile.gettempdir(), 'ctrlp-py.log')) formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') @@ -42,7 +45,7 @@ def filter(self, items, pat, limit, mmode, ispath, crfile, regexp): self.process(pat) - if self.lastpat == pat: + if self.lastpat == pat and self.lastmmode == mmode and self.lastispath == ispath: if self.process(pat) and self.queue.qsize() == 0 and not self.thread.isAlive(): self.logger.debug("Thread job is processed for {pat}".format(pat=pat)) self.lastpat = None @@ -67,6 +70,8 @@ def filter(self, items, pat, limit, mmode, ispath, crfile, regexp): self.thread.start() self.lastpat = pat + self.lastmmode = mmode + self.lastispath = ispath self.forceCursorHold() From 93aba0fcd829ef2e9203b33a6fc35738e58ad633 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 9 Mar 2014 00:41:18 +0200 Subject: [PATCH 37/41] add more vim atoms --- python/ctrlp/regex.py | 115 +++++++++++++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 18 deletions(-) diff --git a/python/ctrlp/regex.py b/python/ctrlp/regex.py index c0b21cc2..daac417b 100644 --- a/python/ctrlp/regex.py +++ b/python/ctrlp/regex.py @@ -8,6 +8,24 @@ def from_vim(pat, ignorecase=False, smartcase=False): True >>> from_vim('foo\\').pattern == r'foo\\' True + >>> from_vim('foo\(').pattern == r'foo\(' + True + >>> from_vim('foo(').pattern == r'foo\(' + True + >>> from_vim('foo\[').pattern == r'foo\[' + True + >>> from_vim('foo[').pattern == r'foo\[' + True + >>> from_vim('foo\(a').pattern == r'foo\(a' + True + >>> from_vim('foo(a').pattern == r'foo\(a' + True + >>> from_vim('foo\[a').pattern == r'foo\[a' + True + >>> from_vim('foo[a').pattern == r'foo\[a' + True + >>> from_vim('\zsfoo\ze').pattern == r'foo' + True >>> from_vim('\w\+').flags 0 >>> from_vim('\c\w\+').flags @@ -68,6 +86,30 @@ def from_vim(pat, ignorecase=False, smartcase=False): True >>> from_vim(r'[a-z]').pattern == r'[a-z]' True + >>> from_vim(r'\x').pattern == r'[0-9A-Fa-f]' + True + >>> from_vim(r'\X').pattern == r'[^0-9A-Fa-f]' + True + >>> from_vim(r'\o').pattern == r'[0-7]' + True + >>> from_vim(r'\O').pattern == r'[^0-7]' + True + >>> from_vim(r'\h').pattern == r'[A-Za-z_]' + True + >>> from_vim(r'\H').pattern == r'[^A-Za-z_]' + True + >>> from_vim(r'\a').pattern == r'[A-Za-z]' + True + >>> from_vim(r'\A').pattern == r'[^A-Za-z]' + True + >>> from_vim(r'\l').pattern == r'[a-z]' + True + >>> from_vim(r'\L').pattern == r'[^a-z]' + True + >>> from_vim(r'\u').pattern == r'[A-Z]' + True + >>> from_vim(r'\U').pattern == r'[^A-Z]' + True """ flags = 0 @@ -143,23 +185,26 @@ def process_group(pat): elif char == r'(': closing = find_matching(pat, index, r'\(', r'\)') - regex += r'(' - if pat[closing+2:closing+5] == r'\@=': - regex += r'?=' - skip.update(skip.fromkeys([closing+2, closing+3, closing+4], True)) - elif pat[closing+2:closing+5] == r'\@!': - regex += r'?!' - skip.update(skip.fromkeys([closing+2, closing+3, closing+4], True)) - elif pat[closing+2:closing+6] == r'\@<=': - regex += r'?<=' - skip.update(skip.fromkeys([closing+2, closing+3, closing+4, closing+5], True)) - elif pat[closing+2:closing+6] == r'\@>> find_matching(r"foo \( bar \\) inner 1 after \) alpha", 5, "\(", "\)") 29 + >>> find_matching(r"foo \( bar ", 5, "\(", "\)") + -1 """ cursor = end = start - len(opening) @@ -248,7 +327,7 @@ def find_matching(string, start, opening, closing): break if not start: - start = cursos + start = cursor if end < 0: end = string.rfind(closing) From 400494d1da0c2f8874a05613130d447edb6b6758 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 9 Mar 2014 01:29:09 +0200 Subject: [PATCH 38/41] use a noop func to force the cursorhold event the previous method with moving the virtual cursor causes flickering --- autoload/ctrlp.vim | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/autoload/ctrlp.vim b/autoload/ctrlp.vim index 74b93ec2..959df8cf 100644 --- a/autoload/ctrlp.vim +++ b/autoload/ctrlp.vim @@ -142,6 +142,7 @@ let [s:lcmap, s:prtmaps] = ['nn ', { \ 'MarkToOpen()': [''], \ 'OpenMulti()': [''], \ 'PrtExit()': ['', '', ''], + \ 'PrtNoop()': [''], \ }] let s:scriptpath = expand(':p:h') @@ -858,6 +859,9 @@ fu! s:PrtExit() en endf +fu! s:PrtNoop() +endf + fu! s:PrtHistory(...) if !s:focus || !s:maxhst | retu | en let [str, hst, s:matches] = [join(s:prompt, ''), s:hstry, 1] @@ -2303,13 +2307,7 @@ fu! ctrlp#process(lines, pat, split, subitems) endf fu! ctrlp#forcecursorhold() - if len(s:prompt[0]) - cal feedkeys("\\") - elsei len(s:prompt[2]) - cal feedkeys("\\") - el - cal feedkeys("\") - en + cal feedkeys("\") endf " - Autocmds {{{1 From af5bb890bb59f783ecb1af43fedbdb30481a2be0 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Sun, 9 Mar 2014 15:31:00 +0200 Subject: [PATCH 39/41] always sort first by filename, for more accurate results --- python/ctrlp/matcher.py | 105 +++++++++++++++++++++++++++++----------- python/ctrlp/regex.py | 9 ++-- 2 files changed, 80 insertions(+), 34 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index 4bdd879d..b3b1a28a 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -22,6 +22,7 @@ def __init__(self, debug=False): self.lastpat = None self.lastmmode = None self.lastispath = None + self.lastregexp = None self.logger = logging.getLogger('ctrlp') hdlr = logging.FileHandler(os.path.join(tempfile.gettempdir(), 'ctrlp-py.log')) @@ -45,7 +46,8 @@ def filter(self, items, pat, limit, mmode, ispath, crfile, regexp): self.process(pat) - if self.lastpat == pat and self.lastmmode == mmode and self.lastispath == ispath: + if self.lastpat == pat and self.lastmmode == mmode \ + and self.lastispath == ispath and self.lastregexp == regexp: if self.process(pat) and self.queue.qsize() == 0 and not self.thread.isAlive(): self.logger.debug("Thread job is processed for {pat}".format(pat=pat)) self.lastpat = None @@ -72,6 +74,7 @@ def filter(self, items, pat, limit, mmode, ispath, crfile, regexp): self.lastpat = pat self.lastmmode = mmode self.lastispath = ispath + self.lastregexp = regexp self.forceCursorHold() @@ -161,40 +164,80 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, patterns.append(re.compile(''.join(map(builder, chars)), flags)) - itemId = 0 - index = 0 + fileId = 0 + count = 0 + index = -1 matchedItems = [] + skip = {} + logger.debug("Matching against {number} items using {pat}".format(number=len(items), pat=pat)) - for item in items: - itemId += 1 - if ispath and item == crfile: - continue - if mmode == 'filename-only': - dirname = os.path.dirname(item) + if ispath and (mmode == 'filename-only' or mmode == 'full-line'): + for item in items: + index += 1 + fileId += 1 + + if item == crfile: + continue + basename = os.path.basename(item) + span = () + match = None - match = patterns[0].search(basename) + if mmode == 'filename-only': + match = patterns[0].search(basename) + if match: + span = match.span() - if len(patterns) == 2 and match is not None: - match = patterns[1].search(dirname) - elif mmode == 'first-non-tab': - match = patterns[0].search(re.split('\t+', item)[0]) - elif mmode == 'until-last-tab': - match = patterns[0].search(re.split('\t+[^\t]+$', item)[0]) - else: - match = patterns[0].search(item) + if len(patterns) == 2: + dirname = os.path.dirname(item) + match = patterns[1].search(dirname) - if match is None: - continue + elif mmode == 'full-line': + match = patterns[0].search(basename) - span = match.span() - matchedItems.append({"line": item, "matlen": span[1] - span[0]}) + if not match: + continue - if limit and index >= limit * 3: - break + if not span: + span = match.span() - index += 1 + matchedItems.append({"line": item, "matlen": span[1] - span[0]}) + skip[index] = True + + if limit and count >= limit: + break + + count += 1 + + index = -1 + itemId = 0 + + if count < limit - 1 and (not ispath or mmode != 'filename-only'): + for item in items: + index += 1 + itemId += 1 + + if skip.get(index, False) or ispath and item == crfile: + continue + + if mmode == 'first-non-tab': + match = patterns[0].search(re.split('\t+', item)[0]) + elif mmode == 'until-last-tab': + match = patterns[0].search(re.split('\t+[^\t]+$', item)[0]) + else: + match = patterns[0].search(item) + + if match is None: + continue + + span = match.span() + matchedItems.append({"line": item, "matlen": span[1] - span[0]}) + + if limit and count >= limit: + break + + count += 1 mrudict = {} index = 0 @@ -202,11 +245,17 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, mrudict[f] = index index += 1 - matchedItems = sorted(matchedItems, cmp=sort_items(crfile, mmode, ispath, mrudict, len(matchedItems))) - if limit > 0: + matchedItems = sorted(matchedItems, cmp=sort_items(crfile, mmode, ispath, + mrudict, len(matchedItems))) + + if limit: matchedItems = matchedItems[:limit] - queue.put({"items": [i["line"] for i in matchedItems], "subitems": items[itemId:], "pat": pat}, timeout=1) + queue.put({ + "items": [i["line"] for i in matchedItems], + "subitems": items[itemId if itemId > fileId else fileId:], + "pat": pat + }, timeout=1) logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) def sort_items(crfile, mmode, ispath, mrudict, total): diff --git a/python/ctrlp/regex.py b/python/ctrlp/regex.py index daac417b..452ae813 100644 --- a/python/ctrlp/regex.py +++ b/python/ctrlp/regex.py @@ -142,12 +142,9 @@ def process_group(pat): skip = {} for char in pat: - try: - if skip[index]: - index += 1 - continue - except KeyError: - pass + if skip.get(index, False): + index += 1 + continue if special: special = False From 7471f29c62b045fbe71e06a6a04e82d048051724 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Tue, 18 Mar 2014 12:27:43 +0200 Subject: [PATCH 40/41] filter by recent files in the project first --- python/ctrlp/matcher.py | 73 +++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index b3b1a28a..ee692307 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -164,20 +164,57 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, patterns.append(re.compile(''.join(map(builder, chars)), flags)) - fileId = 0 + index = 0 + mrudict = {} + for f in mru: + mrudict[f] = index + index += 1 + + recent = [] + if ispath and mrudict: + for item in items: + recent.append(item if mrudict.has_key(item) else None) + count = 0 - index = -1 matchedItems = [] skip = {} + itemIds = [0, 0] logger.debug("Matching against {number} items using {pat}".format(number=len(items), pat=pat)) + if recent: + count = reducer(recent, patterns, limit, mmode, ispath, crfile, matchedItems, count, skip, itemIds) + + if count < limit - 1: + count = reducer(items, patterns, limit, mmode, ispath, crfile, matchedItems, count, skip, itemIds) + + matchedItems = matchedItems[:limit] + matchedItems = sorted(matchedItems, cmp=sort_items(crfile, mmode, ispath, + mrudict, len(matchedItems))) + + if limit: + matchedItems = matchedItems[:limit] + + queue.put({ + "items": [i["line"] for i in matchedItems], + "subitems": items[max(itemIds)], + "pat": pat + }, timeout=1) + logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) + +def reducer(items, patterns, limit, mmode, ispath, crfile, matchedItems, count, skip, itemIds): + index = -1 if ispath and (mmode == 'filename-only' or mmode == 'full-line'): for item in items: index += 1 - fileId += 1 - if item == crfile: + if item is None: + continue + + if itemIds[0] < index: + itemIds[0] = index + + if skip.get(index, False) or ispath and item == crfile: continue basename = os.path.basename(item) @@ -211,12 +248,15 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, count += 1 index = -1 - itemId = 0 - if count < limit - 1 and (not ispath or mmode != 'filename-only'): for item in items: index += 1 - itemId += 1 + + if item is None: + continue + + if itemIds[1] < index: + itemIds[1] = index if skip.get(index, False) or ispath and item == crfile: continue @@ -239,24 +279,7 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, count += 1 - mrudict = {} - index = 0 - for f in mru: - mrudict[f] = index - index += 1 - - matchedItems = sorted(matchedItems, cmp=sort_items(crfile, mmode, ispath, - mrudict, len(matchedItems))) - - if limit: - matchedItems = matchedItems[:limit] - - queue.put({ - "items": [i["line"] for i in matchedItems], - "subitems": items[itemId if itemId > fileId else fileId:], - "pat": pat - }, timeout=1) - logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) + return count def sort_items(crfile, mmode, ispath, mrudict, total): crdir = os.path.dirname(crfile) From 09b3bcab0c767e117ed7c4ab3caf7ab11a54edd2 Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Tue, 18 Mar 2014 16:51:09 +0200 Subject: [PATCH 41/41] show the mru files for the current project in the initial list first --- python/ctrlp/matcher.py | 44 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/python/ctrlp/matcher.py b/python/ctrlp/matcher.py index ee692307..20a1b901 100644 --- a/python/ctrlp/matcher.py +++ b/python/ctrlp/matcher.py @@ -36,7 +36,7 @@ def __init__(self, debug=False): def filter(self, items, pat, limit, mmode, ispath, crfile, regexp): if not pat: self.logger.debug("No pattern, returning original items") - self.queue.put({"items": items[:limit], "subitems": items[limit-1:], "pat": ""}, timeout=1) + self.queue.put(self.initialList(items, limit, ispath, crfile), timeout=1) self.process(pat) @@ -125,6 +125,46 @@ def process(self, pat): def forceCursorHold(self): vim.bindeval('function("ctrlp#forcecursorhold")')() + def initialList(self, items, limit, ispath, crfile): + mru = None + if ispath: + mru = vim.bindeval('ctrlp#mrufiles#list()') + + if isinstance(mru, vim.List) and mru: + mrudict = {} + mrucount = len(mru) + index = 0 + for f in mru: + mrudict[f] = index + index += 1 + + recentCount = 0 + restCount = 0 + + recent = [] + rest = [] + for item in items: + if item == crfile: + continue + + if mrudict.get(item, -1) != -1: + recent.append(item) + recentCount += 1 + mrucount -= 1 + + elif restCount + recentCount < limit: + rest.append(item) + restCount += 1 + + if recentCount == limit or not mrucount and restCount + recentCount >= limit: + break + + recent = sorted(recent, cmp=lambda a, b: mrudict.get(a, -1) - mrudict.get(b, -1)) + return {"items": recent + rest[:limit - restCount], "subitems": items[recentCount-1:], "pat": ""} + + else: + return {"items": items[:limit], "subitems": items[limit-1:], "pat": ""} + def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, ic, scs, logger): if ispath and mmode == 'filename-only': @@ -202,6 +242,7 @@ def thread_worker(queue, items, pat, limit, mmode, ispath, crfile, regexp, mru, }, timeout=1) logger.debug("Got {number} matched items using {pat}".format(number=len(matchedItems), pat=pat)) + def reducer(items, patterns, limit, mmode, ispath, crfile, matchedItems, count, skip, itemIds): index = -1 if ispath and (mmode == 'filename-only' or mmode == 'full-line'): @@ -281,6 +322,7 @@ def reducer(items, patterns, limit, mmode, ispath, crfile, matchedItems, count, return count + def sort_items(crfile, mmode, ispath, mrudict, total): crdir = os.path.dirname(crfile)