Ejemplo n.º 1
0
 def __init__(self):
     self._bufName = vim.eval("expand('$VIMRUNTIME/[LeaderF]')")
     self._winPos = int(vim.eval("g:Lf_WindowPosition"))
     self._autochdir = 0
     self._cli = LfCli()
     self._explorer = None
     self._index = 0
     self._helpLength = 0
     self._showHelp = False
     self._selections = {}
     self._initStlVar()
     self._setStlMode()
     self._getExplClass()
Ejemplo n.º 2
0
 def __init__(self):
     self._buffer_name = vim.eval("expand('$VIMRUNTIME/LeaderF')")
     self._win_pos = int(vim.eval("g:Lf_WindowPosition"))
     self._autochdir = 0
     self._cli = LfCli()
     self._explorer = None
     self._content = []
     self._index = 0
     self._help_length = 0
     self._show_help = False
     self._selections = {}
     self._highlight_pos = []
     self._highlight_refine_pos = []
     self._highlight_ids = []
     self._initStlVar()
     self._setStlMode()
     self._getExplClass()
Ejemplo n.º 3
0
 def __init__(self):
     self._bufName = vim.eval("expand('$VIMRUNTIME/[LeaderF]')")
     self._winPos = int(vim.eval("g:Lf_WindowPosition"))
     self._autochdir = 0
     self._cli = LfCli()
     self._explorer = None
     self._index = 0
     self._helpLength = 0
     self._showHelp = False
     self._selections = {}
     self._initStlVar()
     self._setStlMode()
     self._getExplClass()
Ejemplo n.º 4
0
 def __init__(self):
     self._autochdir = 0
     self._instance = None
     self._cli = LfCli()
     self._explorer = None
     self._content = []
     self._index = 0
     self._help_length = 0
     self._show_help = False
     self._selections = {}
     self._highlight_pos = []
     self._highlight_refine_pos = []
     self._highlight_ids = []
     self._getExplClass()
Ejemplo n.º 5
0
class Manager(object):
    def __init__(self):
        self._bufName = vim.eval("expand('$VIMRUNTIME/[LeaderF]')")
        self._winPos = int(vim.eval("g:Lf_WindowPosition"))
        self._maxLines = int(vim.eval("g:Lf_MaxLines"))
        self._autochdir = 0
        self._cli = LfCli()
        self._explorer = None
        self._index = 0
        self._helpLength = 0
        self._showHelp = False
        self._selections = {}
        self._initStlVar()
        self._setStlMode()
        self._getExplClass()

    #*****************************************************
    # abstract methods, in fact all the functions can be overridden
    #*****************************************************
    def _getExplClass(self):
        '''
        this function MUST be overridden
        return the name of Explorer class
        '''
        raise NotImplementedError("Can't instantiate abstract class Manager with abstract methods _getExplClass")

    def _defineMaps(self):
        pass

    def _cmdExtension(self, cmd):
        '''
        this function can be overridden to add new cmd
        if return true, exit the input loop
        '''
        pass

    def _getDigest(self, line):
        '''
        this function can be overridden
        specify what part to match regex for a line in the match window
        '''
        return line

    def _createHelp(self):
        return []

    #*****************************************************
    # private methods
    #*****************************************************
    def _createHelpHint(self):
        help = []
        if not self._showHelp:
            help.append('" Press <F1> for help')
            help.append('" ---------------------------------------------------')
        else:
            help += self._createHelp()
        self._helpLength = len(help)
        vim.command("setlocal modifiable")
        vim.current.buffer.append(help, 0)
        vim.current.window.cursor = (self._helpLength + 1, 0)
        vim.command("setlocal nomodifiable")

    def _hideHelp(self):
        for i in range(self._helpLength):
            del vim.current.buffer[0]
        self._helpLength = 0

    def _getExplorer(self):
        if self._explorer is None:
            self._explorer = self._getExplClass()()
        return self._explorer

    def _initStlVar(self):
        vim.command("let g:Lf_statusline_function = '-'")
        vim.command("let g:Lf_statusline_curDir = '-'")
        vim.command("let g:Lf_statusline_total = '0'")

    def _setStlMode(self):
        if self._cli.isFuzzy:
            if self._cli.isFileNameOnly:
                vim.command("let g:Lf_statusline_mode = 'NameOnly'")
            else:
                vim.command("let g:Lf_statusline_mode = 'FullPath'")
        else:
            vim.command("let g:Lf_statusline_mode = 'Regexp'")

    def _bufwinnr(self, name):
        nr = 1
        for w in vim.windows:
            if w.buffer.name is not None and os.path.abspath(w.buffer.name) == os.path.abspath(name):
                return nr
            nr += 1
        return 0

    def _gotoBuffer(self):
        if int(vim.eval("&autochdir")) == 1:
            self._autochdir = 1
            vim.command("set noautochdir")
        else:
            self._autochdir = 0

        self._origBuf = vim.current.buffer.name
        self._origWinNr = int(vim.eval("winnr()"))
        nr = self._bufwinnr(self._bufName)
        if nr == 0:
            self._createBufWindow()
        else:
            vim.command("exec '%d wincmd w'" % nr)
        self._setAttributes()
        self._setStatusline()
        self._defineMaps()

    def _createBufWindow(self):
        if self._winPos == 0:
            vim.command("silent! noa keepj hide edit %s" % self._bufName)
        elif self._winPos == 1:
            vim.command("silent! noa keepj bo sp %s" % self._bufName)
        elif self._winPos == 2:
            vim.command("silent! noa keepj to sp %s" % self._bufName)
        else:
            vim.command("silent! noa keepj to vsp %s" % self._bufName)

    def _setAttributes(self):
        vim.command("setlocal nobuflisted")
        vim.command("setlocal buftype=nofile")
        vim.command("setlocal bufhidden=hide")
        vim.command("setlocal noswapfile")
        vim.command("setlocal nolist")
        vim.command("setlocal nonumber")
        vim.command("setlocal nospell")
        vim.command("setlocal nowrap")
        vim.command("setlocal nofoldenable")
        vim.command("setlocal foldcolumn=1")
        vim.command("setlocal cursorline")
        vim.command("setlocal filetype=LeaderF")

    def _setStatusline(self):
        vim.command("setlocal statusline=LeaderF:\ [%#Lf_hl_stlFunction#%{g:Lf_statusline_function}%#Lf_hl_none#,")
        vim.command("setlocal statusline+=\ %#Lf_hl_stlMode#%-9(%{g:Lf_statusline_mode}%#Lf_hl_none#]%)")
        vim.command("setlocal statusline+=\ \ %<%#Lf_hl_stlCurDir#%{g:Lf_statusline_curDir}%#Lf_hl_none#")
        vim.command("setlocal statusline+=%=%lL/%-5L\ \ Total:%{g:Lf_statusline_total}\ ")
        vim.command("redraw!")

    def _toUp(self):
        vim.command("norm! k")

    def _toDown(self):
        vim.command("norm! j")

    def _leftClick(self):
        nr = self._bufwinnr(self._bufName)
        if nr == int(vim.eval("v:mouse_win")):
            vim.command("exec v:mouse_lnum")
            vim.command("exec 'norm!'.v:mouse_col.'|'")
            self.clearSelections()
        else:
            self.quit()
            self._exitLoop = True

    def _filter(self, iterable, regex):
        if self._cli.isFuzzy:
            if self._cli.isFileNameOnly:
                if self._cli.isMixed:
                    if regex[0] == '':
                        return [line for line in iterable if re.search(regex[1], self._getDigest(line), re.I)]
                    elif regex[1] == '':
                        return [line for line in iterable if re.search(regex[0], os.path.basename(self._getDigest(line)), re.I)]
                    else:
                        iterable = [line for line in iterable if re.search(regex[0], os.path.basename(self._getDigest(line)), re.I)]
                        return [line for line in iterable if re.search(regex[1], os.path.dirname(self._getDigest(line)), re.I)]
                else:
                    return [line for line in iterable if re.search(regex, os.path.basename(self._getDigest(line)), re.I)]
            else:
                return [line for line in iterable if re.search(regex, self._getDigest(line), re.I)]
        else:
            try:
                if '-2' == vim.eval("g:LfNoErrMsgMatch('', '%s')" % escQuote(regex)):
                    return []
                else:
                    return [line for line in iterable if '-1' != vim.eval("g:LfNoErrMsgMatch('%s', '%s')" % (escQuote(self._getDigest(line).strip()), escQuote(regex)))]
            except vim.error:
                pass

    def _isPrefix(self, regex): #assume that there are no \%23l, \%23c, \%23v, \%...
        pos = self._cli.cursorPos
        if pos > 1:
            if regex[pos - 2] != '\\' and (regex[pos - 1].isalnum() or regex[pos - 1] in r'`$%*(-_+[\;:,. /?'):
                if regex[pos - 2] == '_':
                    if pos > 2 and regex[pos - 3] == '\\': #\_x
                        return False
                    else:
                        return True
                if regex.endswith(r'\zs') or regex.endswith(r'\ze'):
                    return False
                return True
        return False

    def _search(self, content, regex):
        self.clearSelections()
        self._cli.highlightMatches()
        cb = vim.current.buffer
        if not regex:
            if self._maxLines > 0:
                setBuffer(cb, content[:self._maxLines])
            else:
                setBuffer(cb, content)
            return

        if not self._cli.isFuzzy:
            if not self._isPrefix(regex):
                self._index = 0

        step = int(vim.eval("g:Lf_SearchStep"))
        length = len(content)
        if self._index == 0:
            self._index = step
            if self._index < length:
                setBuffer(cb, self._filter(content[:self._index], regex))
            else:
                setBuffer(cb, self._filter(content, regex))
        else:
            setBuffer(cb, self._filter(cb[:], regex))

        while len(cb) < 1000 and self._index < length:
            end = self._index + step
            startLine = 0 if cb[0] == '' else len(cb)
            if end < length:
                appendBuffer(cb, self._filter(content[self._index:end], regex), startLine)
            else:
                appendBuffer(cb, self._filter(content[self._index:], regex), startLine)
            self._index = end
            if len(cb) > vim.current.window.height * 2:
                break

        if self._getExplorer().supportsSort():
            num = int(vim.eval("g:Lf_NumberOfSort"))
            if num == -1:
                self._sortResult(len(cb))
            elif num == 0:
                pass
            elif num == 1:
                self._sortResult(vim.current.window.height)
            else:
                self._sortResult(num)
            vim.current.window.cursor = (self._helpLength + 1, 0)

    def _getWeight(self, str, t):
        '''
        this function can be overridden to get the weight of the line,
        so that it can be applied in the sort algorithm
        '''
        pre = str.lower().index(t[0].lower())
        s = str[pre:]
        sl = len(s)
        tl = len(t)
        val = [[0 for col in range(tl)] for row in range(sl)]
        val[0][0] = 1
        JJ = 2
        for i in range(1, sl):
            J = JJ
            for j in range(min(J, tl)):
                if s[i].lower() != t[j].lower():
                    val[i][j] = val[i-1][j]
                else:
                    if j == JJ-1:
                        JJ += 1
                    if val[i-1][j] == (j+1)*(j+1):              # prune
                        val[i][j] = (j+1)*(j+1)
                        continue
                    val[i][j] = val[i-1][j]
                    ii = i
                    jj = j
                    k = 0
                    while ii >= 0 and jj >= 0:
                        if s[ii].lower() == t[jj].lower():
                            if i == j and val[i-1][j-1] == i*i: # prune
                                val[i][j] = (i+1)*(i+1)
                                break
                            k += 1
                            if j-k >= 0:
                                if val[i-k][j-k] + k*k > val[i][j]:
                                    val[i][j] = val[i-k][j-k] + k*k
                            else:
                                val[i][j] = k*k
                            ii -= 1
                            jj -= 1
                        else:
                            break
                if val[i][j] == tl*tl:                          # prune
                    return val[i][j] + 1.0/(2*pre + len(str))
        return val[sl-1][tl-1] + 1.0/(2*pre + len(str))

    def _sortResult(self, num):
        '''
        this function can be overridden to customize the sort algorithm
        '''
        cb = vim.current.buffer
        if len(cb) == 1 and cb[0] == '' or num == 0:
            return
        if self._cli.isFuzzy:
            if self._cli.isFileNameOnly:
                if self._cli.isMixed:
                    pass
                else:
                    pairs = [(i, self._getWeight(os.path.basename(self._getDigest(cb[i])), self._cli.cmdline)) for i in range(len(cb))]
                    pairs = heapq.nlargest(num, pairs, key = lambda x: x[1])
                    lines = [cb[i[0]] for i in pairs]
                    append(cb, lines, 0)
                    pairs.sort(key = lambda x: x[0], reverse = True)
                    for i in pairs:
                        del cb[i[0] + len(pairs)]
            else:
                pass

    def _accept(self, file, mode):
        try:
            if file:
                if mode == '':
                    pass
                elif mode == 'h':
                    vim.command("split")
                elif mode == 'v':
                    vim.command("vsplit")
                elif mode == 't':
                    vim.command("tabedit")
                    tabPos = int(vim.eval("g:Lf_TabpagePosition"))
                    if tabPos == 0:
                        vim.command("tabm 0")
                    elif tabPos == 1:
                        vim.command("tabm -1")
                    elif tabPos == 3:
                        vim.command("tabm")
                self._getExplorer().acceptSelection(file)
        except vim.error:
            pass

    def clearSelections(self):
        for i in self._selections.values():
            vim.command("call matchdelete(%d)" % i)
        self._selections.clear()


    #*****************************************************
    # public methods
    #*****************************************************
    def toggleHelp(self):
        vim.command("setlocal modifiable")
        self._showHelp = not self._showHelp
        for i in range(self._helpLength):
            del vim.current.buffer[0]
        self._createHelpHint()
        self.clearSelections()
        vim.command("setlocal nomodifiable")

    def accept(self, mode = ''):
        if vim.current.window.cursor[0] <= self._helpLength:
            vim.command("norm! j")
            return
        if len(self._selections) > 0:
            files = []
            for i in self._selections.keys():
                files.append(os.path.abspath(vim.current.buffer[i-1]))
            self.quit()
            if mode == '':
                vim.command("argdelete *")
                for file in files:
                    vim.command("argadd %s" % escSpecial(file))
                self._accept(files[0], mode)
            else:
                for file in files:
                    self._accept(file, mode)
        else:
            file = os.path.abspath(vim.current.line)
            self.quit()
            self._accept(file, mode)

    def quit(self):
        self._cli.clear()
        self.clearSelections()
        if self._winPos != 0 and len(vim.windows) > 1:
            vim.command("hide")
            vim.command("exec '%d wincmd w'" % self._origWinNr)
        else:
            if self._origBuf is None or vim.eval("bufexists('%s')" % escQuote(self._origBuf)) == '0':
                vim.command("bd")
            else:
                vim.command("hide edit %s" % escSpecial(self._origBuf))
        if self._winPos != 0:
            vim.command("call getchar(0) | redraw | echo")
        else:
            vim.command("call getchar(0)")
        if self._autochdir == 1:
            cwd = os.getcwd()
            vim.command("set autochdir")    # I think vim has a bug here
            os.chdir(cwd)

    def refresh(self, content = None):
        rContent = self._getExplorer().getFreshContent()
        if rContent is None:
            return
        if content is None:
            vim.command("setlocal modifiable")
            setBuffer(vim.current.buffer, rContent)
            self._content = rContent
            if self._cli.regex:
                self._index = 0
                self._search(rContent, self._cli.regex)
            vim.command("setlocal nomodifiable")
        else:
            setBuffer(vim.current.buffer, rContent)
            content[:] = rContent      #use slice to change content
            if self._cli.regex:
                self._index = 0
                self._search(rContent, self._cli.regex)

    def addSelections(self):
        nr = self._bufwinnr(self._bufName)
        if int(vim.eval("v:mouse_win")) != 0 and nr != int(vim.eval("v:mouse_win")):
            return
        elif nr == int(vim.eval("v:mouse_win")):
            vim.command("exec v:mouse_lnum")
            vim.command("exec 'norm!'.v:mouse_col.'|'")
        lineNr = vim.current.window.cursor[0]
        if lineNr <= self._helpLength:
            vim.command("norm! j")
            return
        
        if lineNr in self._selections:
            vim.command("call matchdelete(%d)" % self._selections[lineNr])
            del self._selections[lineNr]
        else:
            id = int(vim.eval("matchadd('Lf_hl_selection', '\%%%dl.')" % lineNr))
            self._selections[lineNr] = id

        if int(vim.eval("v:mouse_win")) != 0:
            vim.command('call feedkeys("\<C-@>")')  #vim has bug, so add this line

    def selectMulti(self):
        origLine = vim.current.window.cursor[0]
        nr = self._bufwinnr(self._bufName)
        if int(vim.eval("v:mouse_win")) != 0 and nr != int(vim.eval("v:mouse_win")):
            return
        elif nr == int(vim.eval("v:mouse_win")):
            vim.command("exec v:mouse_lnum")
            vim.command("exec 'norm!'.v:mouse_col.'|'")
        self.clearSelections()
        curLine = vim.current.window.cursor[0]
        for i in range(min(origLine, curLine), max(origLine, curLine)+1):
            if i > self._helpLength and i not in self._selections:
                id = int(vim.eval("matchadd('Lf_hl_selection', '\%%%dl.')" % (i)))
                self._selections[i] = id
        vim.command('call feedkeys("\<C-@>")')  #vim has bug, so add this line

    def selectAll(self):
        for i in range(len(vim.current.buffer)):
            if i >= self._helpLength and i+1 not in self._selections:
                id = int(vim.eval("matchadd('Lf_hl_selection', '\%%%dl.')" % (i+1)))
                self._selections[i+1] = id

    def startExplorer(self, *args, **kwargs):
        self._cli.setFullPathFeature(self._getExplorer().supportsFullPath())
        vim.command("let g:Lf_statusline_function = '%s'" % self._getExplorer().getStlFunction())
        vim.command("echohl WarningMsg | redraw | echo ' searching ...' | echohl NONE")
        content = self._getExplorer().getContent(*args, **kwargs)
        if content is None:
            return
        self._gotoBuffer()
        vim.command("let g:Lf_statusline_curDir = '%s'" % self._getExplorer().getStlCurDir())
        vim.command("let g:Lf_statusline_total = '%d'" % len(content))
        self.startExplAction(content)

    def startExplAction(self, content = None):
        vim.command("setlocal modifiable")
        self._hideHelp()
        if content is None:
            content = self._content
        else:
            if self._maxLines > 0:
                setBuffer(vim.current.buffer, content[:self._maxLines])
            else:
                setBuffer(vim.current.buffer, content)
            self._index = 0
        quit = False
        for cmd in self._cli.input():
            if equal(cmd, '<Update>'):
                self._search(content, self._cli.regex)
            elif equal(cmd, '<Shorten>'):
                self._index = 0
                self._search(content, self._cli.regex)
            elif equal(cmd, '<Mode>'):
                self._setStlMode()
                self._index = 0
                if self._cli.regex:
                    self._search(content, self._cli.regex)
            elif equal(cmd, '<Up>'):
                self._toUp()
            elif equal(cmd, '<Down>'):
                self._toDown()
            elif equal(cmd, '<LeftMouse>'):
                self._exitLoop = False
                self._leftClick()
                if self._exitLoop:
                    break
            elif equal(cmd, '<2-LeftMouse>'):
                self._leftClick()
                self.accept()
                break
            elif equal(cmd, '<CR>'):
                self.accept()
                break
            elif equal(cmd, '<C-X>'):
                self.accept('h')
                break
            elif equal(cmd, '<C-]>'):
                self.accept('v')
                break
            elif equal(cmd, '<C-T>'):
                self.accept('t')
                break
            elif equal(cmd, '<Quit>'):
                quit = True
                break
            elif equal(cmd, '<Esc>'):
                self.clearSelections()
                self._cli.hideCursor()
                vim.command("setlocal nomodifiable")
                self._content = content
                self._createHelpHint()
            elif equal(cmd, '<F5>'):
                self.refresh(content)
            elif equal(cmd, '<C-LeftMouse>'):
                if self._getExplorer().supportsMulti():
                    self.addSelections()
            elif equal(cmd, '<S-LeftMouse>'):
                if self._getExplorer().supportsMulti():
                    self.selectMulti()
            elif equal(cmd, '<C-A>'):
                if self._getExplorer().supportsMulti():
                    self.selectAll()
            elif equal(cmd, '<C-L>'):
                self.clearSelections()
            else:
                if self._cmdExtension(cmd):
                    break
        if quit: #due to a bug, I have to write this ugly code
            self.quit()
Ejemplo n.º 6
0
class Manager(object):
    def __init__(self):
        self._autochdir = 0
        self._instance = None
        self._cli = LfCli()
        self._explorer = None
        self._content = []
        self._index = 0
        self._help_length = 0
        self._show_help = False
        self._selections = {}
        self._highlight_pos = []
        self._highlight_refine_pos = []
        self._highlight_ids = []
        self._getExplClass()

    #**************************************************************
    # abstract methods, in fact all the functions can be overridden
    #**************************************************************
    def _getExplClass(self):
        """
        this function MUST be overridden
        return the name of Explorer class
        """
        raise NotImplementedError("Can't instantiate abstract class Manager "
                                  "with abstract methods _getExplClass")

    def _defineMaps(self):
        pass

    def _cmdExtension(self, cmd):
        """
        this function can be overridden to add new cmd
        if return true, exit the input loop
        """
        pass

    def _argaddFiles(self, files):
        # It will raise E480 without 'silent!'
        lfCmd("silent! argdelete *")
        for file in files:
            lfCmd("argadd %s" % escSpecial(file))

    def _acceptSelection(self, *args, **kwargs):
        if len(args) == 0:
            return
        file = args[0]
        lfCmd("hide edit %s" % escSpecial(file))

    def _getDigest(self, line, mode):
        """
        this function can be overridden
        specify what part in the line to be processed and highlighted
        Args:
            mode: 0, return the full path
                  1, return the name only
                  2, return the directory name
        """
        if mode == 0:
            return line
        elif mode == 1:
            return getBasename(line)
        else:
            return getDirname(line)

    def _getDigestStartPos(self, line, mode):
        """
        this function can be overridden
        return the start position of the digest returned by _getDigest()
        Args:
            mode: 0, return the start postion of full path
                  1, return the start postion of name only
                  2, return the start postion of directory name
        """
        if mode == 0 or mode == 2:
            return 0
        else:
            return lfBytesLen(getDirname(line))

    def _createHelp(self):
        return []

    def _setStlMode(self):
        if self._cli.isFuzzy:
            if self._cli.isFullPath:
                mode = 'FullPath'
            else:
                mode = 'NameOnly'
        else:
            mode = 'Regex'
        self._getInstance().setStlMode(mode)

    def _beforeEnter(self):
        self._resetAutochdir()

    def _afterEnter(self):
        self._cleanup()
        self._defineMaps()

    def _beforeExit(self):
        self._cleanup()

    def _afterExit(self):
        pass

    #**************************************************************

    def _getInstance(self):
        if self._instance is None:
            self._instance = LfInstance(self._getExplorer().getStlCategory(),
                                        self._beforeEnter,
                                        self._afterEnter,
                                        self._beforeExit,
                                        self._afterExit)
        return self._instance

    def _createHelpHint(self):
        help = []
        if not self._show_help:
            help.append('" Press <F1> for help')
            help.append('" ---------------------------------------------------------')
        else:
            help += self._createHelp()
        self._help_length = len(help)
        self._getInstance().buffer.append(help, 0)
        self._getInstance().window.cursor = (self._help_length + 1, 0)

    def _hideHelp(self):
        del self._getInstance().buffer[:self._help_length]
        self._help_length = 0

    def _getExplorer(self):
        if self._explorer is None:
            self._explorer = self._getExplClass()()
        return self._explorer

    def _resetAutochdir(self):
        if int(lfEval("&autochdir")) == 1:
            self._autochdir = 1
            lfCmd("set noautochdir")
        else:
            self._autochdir = 0

    def _setAutochdir(self):
        if self._autochdir == 1:
            # When autochdir is set, Vim will change the current working directory
            # to the directory containing the file which was opened or selected.
            lfCmd("set autochdir")

    def _toUp(self):
        lfCmd("norm! k")

    def _toDown(self):
        lfCmd("norm! j")

    def _leftClick(self):
        if self._getInstance().window.number == int(lfEval("v:mouse_win")):
            lfCmd("exec v:mouse_lnum")
            lfCmd("exec 'norm!'.v:mouse_col.'|'")
            self.clearSelections()
            exit_loop = False
        else:
            self.quit()
            exit_loop = True
        return exit_loop

    def _search(self, content):
        self.clearSelections()
        self._cli.highlightMatches()
        if not self._cli.pattern:   # e.g., when <BS> or <Del> is typed
            self._getInstance().setBuffer(content)
            self._clearHighlights()
            self._clearHighlightsPos()
            return

        if self._cli.isFuzzy:
            self._fuzzySearch(content)
        else:
            self._regexSearch(content)

    def _filter(self, step, filter_method, content):
        """ Construct a list from result of filter_method(content).

        Args:
            step: An integer to indicate the number of lines to filter one time.
            filter_method: A function to apply `content` as parameter and
                return an iterable.
            content: The list to be filtered.
        """
        length = len(content)
        cb = self._getInstance().buffer
        result = []
        if self._index == 0:
            self._index = step
            if self._index < length:
                result.extend(filter_method(content[:self._index]))
            else:
                result.extend(filter_method(content))
        else:
            result.extend(filter_method(cb[:]))
            if self._index < length:
                end = self._index + step - len(cb)
                result.extend(filter_method(content[self._index:end]))
                self._index = end

        while len(result) < 200 and self._index < length:
            end = self._index + 5000
            result.extend(filter_method(content[self._index:end]))
            self._index = end

        return result

    def _fuzzyFilter(self, is_full_path, get_weight, iterable):
        """
        return a list, each item is a pair (weight, line)
        """
        getDigest = partial(self._getDigest, mode=0 if is_full_path else 1)
        pairs = ((get_weight(getDigest(line)), line) for line in iterable)
        return (p for p in pairs if p[0])

    def _refineFilter(self, first_get_weight, get_weight, iterable):
        getDigest = self._getDigest
        triples = ((first_get_weight(getDigest(line, 1)),
                    get_weight(getDigest(line, 2)), line)
                    for line in iterable)
        return ((i[0] + i[1], i[2]) for i in triples if i[0] and i[1])

    def _fuzzySearch(self, content):
        encoding = lfEval("&encoding")
        if self._cli.isRefinement:
            if self._cli.pattern[1] == '':      # e.g. abc;
                fuzzy_match = FuzzyMatch(self._cli.pattern[0], encoding)
                filter_method = partial(self._fuzzyFilter,
                                        False,
                                        fuzzy_match.getWeight)
                highlight_method = partial(self._highlight,
                                           False,
                                           fuzzy_match.getHighlights)
            elif self._cli.pattern[0] == '':    # e.g. ;abc
                fuzzy_match = FuzzyMatch(self._cli.pattern[1], encoding)
                filter_method = partial(self._fuzzyFilter,
                                        True,
                                        fuzzy_match.getWeight)
                highlight_method = partial(self._highlight,
                                           True,
                                           fuzzy_match.getHighlights)
            else:
                fuzzy_match0 = FuzzyMatch(self._cli.pattern[0], encoding)
                fuzzy_match1 = FuzzyMatch(self._cli.pattern[1], encoding)
                filter_method = partial(self._refineFilter,
                                        fuzzy_match0.getWeight,
                                        fuzzy_match1.getWeight)
                highlight_method = partial(self._highlightRefine,
                                           fuzzy_match0.getHighlights,
                                           fuzzy_match1.getHighlights)
        else:
            fuzzy_match = FuzzyMatch(self._cli.pattern, encoding)
            filter_method = partial(self._fuzzyFilter,
                                    self._cli.isFullPath,
                                    fuzzy_match.getWeight)
            highlight_method = partial(self._highlight,
                                       self._cli.isFullPath,
                                       fuzzy_match.getHighlights)

        pairs = self._filter(30000, filter_method, content)
        pairs.sort(key=operator.itemgetter(0), reverse=True)
        self._getInstance().setBuffer([p[1] for p in pairs])
        highlight_method()

    def _clearHighlights(self):
        for i in self._highlight_ids:
            lfCmd("silent! call matchdelete(%d)" % i)
        self._highlight_ids = []

    def _clearHighlightsPos(self):
        self._highlight_pos = []
        self._highlight_refine_pos = []

    def _resetHighlights(self):
        self._clearHighlights()
        for i, pos in enumerate(self._highlight_pos, self._help_length + 1):
            pos = [[i] + p for p in pos]
            # The maximum number of positions is 8 in matchaddpos().
            for j in range(0, len(pos), 8):
                id = int(lfEval("matchaddpos('Lf_hl_match', %s)" % str(pos[j:j+8])))
                self._highlight_ids.append(id)

        for i, pos in enumerate(self._highlight_refine_pos, self._help_length + 1):
            pos = [[i] + p for p in pos]
            # The maximum number of positions is 8 in matchaddpos().
            for j in range(0, len(pos), 8):
                id = int(lfEval("matchaddpos('Lf_hl_matchRefine', %s)" % str(pos[j:j+8])))
                self._highlight_ids.append(id)

    def _highlight(self, is_full_path, get_highlights):
        # matchaddpos() is introduced by Patch 7.4.330
        if (lfEval("exists('*matchaddpos')") == '0' or
                lfEval("g:Lf_HighlightIndividual") == '0'):
            return
        cb = self._getInstance().buffer
        if len(cb) == 1 and cb[0] == '': # buffer is empty.
            return

        highlight_number = int(lfEval("g:Lf_NumberOfHighlight"))
        self._clearHighlights()

        getDigest = partial(self._getDigest, mode=0 if is_full_path else 1)

        # e.g., self._highlight_pos = [ [ [2,3], [6,2] ], [ [1,4], [7,6], ... ], ... ]
        # where [2, 3] indicates the highlight starts at the 2nd column with the
        # length of 3 in bytes
        self._highlight_pos = [get_highlights(getDigest(line))
                               for line in cb[:highlight_number]]
        for i, pos in enumerate(self._highlight_pos, 1):
            start_pos = self._getDigestStartPos(cb[i-1], 0 if is_full_path else 1)
            if start_pos > 0:
                for j in range(len(pos)):
                    pos[j][0] += start_pos
            pos = [[i] + p for p in pos]
            # The maximum number of positions is 8 in matchaddpos().
            for j in range(0, len(pos), 8):
                id = int(lfEval("matchaddpos('Lf_hl_match', %s)" % str(pos[j:j+8])))
                self._highlight_ids.append(id)

    def _highlightRefine(self, first_get_highlights, get_highlights):
        # matchaddpos() is introduced by Patch 7.4.330
        if (lfEval("exists('*matchaddpos')") == '0' or
                lfEval("g:Lf_HighlightIndividual") == '0'):
            return
        cb = self._getInstance().buffer
        if len(cb) == 1 and cb[0] == '': # buffer is empty.
            return

        highlight_number = int(lfEval("g:Lf_NumberOfHighlight"))
        self._clearHighlights()

        getDigest = self._getDigest
        self._highlight_pos = [first_get_highlights(getDigest(line, 1))
                               for line in cb[:highlight_number]]
        for i, pos in enumerate(self._highlight_pos, 1):
            start_pos = self._getDigestStartPos(cb[i-1], 1)
            if start_pos > 0:
                for j in range(len(pos)):
                    pos[j][0] += start_pos
            pos = [[i] + p for p in pos]
            # The maximum number of positions is 8 in matchaddpos().
            for j in range(0, len(pos), 8):
                id = int(lfEval("matchaddpos('Lf_hl_match', %s)" % str(pos[j:j+8])))
                self._highlight_ids.append(id)

        self._highlight_refine_pos = [get_highlights(getDigest(line, 2))
                                      for line in cb[:highlight_number]]
        for i, pos in enumerate(self._highlight_refine_pos, 1):
            start_pos = self._getDigestStartPos(cb[i-1], 2)
            if start_pos > 0:
                for j in range(len(pos)):
                    pos[j][0] += start_pos
            pos = [[i] + p for p in pos]
            # The maximum number of positions is 8 in matchaddpos().
            for j in range(0, len(pos), 8):
                id = int(lfEval("matchaddpos('Lf_hl_matchRefine', %s)" % str(pos[j:j+8])))
                self._highlight_ids.append(id)

    def _regexFilter(self, iterable):
        try:
            if ('-2' == lfEval("g:LfNoErrMsgMatch('', '%s')" % escQuote(self._cli.pattern))):
                return iter([])
            else:
                return (line for line in iterable
                        if '-1' != lfEval("g:LfNoErrMsgMatch('%s', '%s')" %
                        (escQuote(self._getDigest(line, 0).strip()),
                        escQuote(self._cli.pattern))))
        except vim.error:
            return iter([])

    def _regexSearch(self, content):
        if not self._cli.isPrefix:
            self._index = 0
        lines = self._filter(8000, self._regexFilter, content)
        self._getInstance().setBuffer(lines)

    def clearSelections(self):
        for i in self._selections.values():
            lfCmd("call matchdelete(%d)" % i)
        self._selections.clear()

    def _cleanup(self):
        self._cli.clear()
        self.clearSelections()
        self._clearHighlights()
        self._clearHighlightsPos()

    @modifiableController
    def toggleHelp(self):
        self._show_help = not self._show_help
        del self._getInstance().buffer[:self._help_length]
        self._createHelpHint()
        self.clearSelections()
        self._resetHighlights()

    def _accept(self, file, mode):
        try:
            if file:
                if mode == '':
                    pass
                elif mode == 'h':
                    lfCmd("split")
                elif mode == 'v':
                    lfCmd("vsplit")
                elif mode == 't':
                    lfCmd("tabedit")
                    tab_pos = int(lfEval("g:Lf_TabpagePosition"))
                    if tab_pos == 0:
                        lfCmd("tabm 0")
                    elif tab_pos == 1:
                        lfCmd("tabm -1")
                    elif tab_pos == 3:
                        lfCmd("tabm")
                self._acceptSelection(file)
        except (KeyboardInterrupt, vim.error):
            pass

    def accept(self, mode=''):
        if self._getInstance().window.cursor[0] <= self._help_length:
            lfCmd("norm! j")
            return
        if len(self._selections) > 0:
            files = []
            for i in sorted(self._selections.keys()):
                files.append(self._getInstance().buffer[i-1])
            self._getInstance().exitBuffer()
            if mode == '':
                self._argaddFiles(files)
                self._accept(files[0], mode)
            else:
                for file in files:
                    self._accept(file, mode)
        else:
            file = self._getInstance().currentLine
            self._getInstance().exitBuffer()
            self._accept(file, mode)

        self._setAutochdir()

    def quit(self):
        self._getInstance().exitBuffer()
        self._setAutochdir()

    def refresh(self, normal_mode=True):
        self._content = self._getExplorer().getFreshContent()
        if self._content is None:
            return

        if normal_mode: # when called in Normal mode
            self._getInstance().buffer.options['modifiable'] = True

        self._getInstance().setBuffer(self._content)
        if self._cli.pattern:
            self._index = 0
            self._search(self._content)

        if normal_mode: # when called in Normal mode
            self._createHelpHint()
            self._resetHighlights()
            self._getInstance().buffer.options['modifiable'] = False

    def addSelections(self):
        nr = self._getInstance().window.number
        if (int(lfEval("v:mouse_win")) != 0 and
                nr != int(lfEval("v:mouse_win"))):
            return
        elif nr == int(lfEval("v:mouse_win")):
            lfCmd("exec v:mouse_lnum")
            lfCmd("exec 'norm!'.v:mouse_col.'|'")
        line_nr = self._getInstance().window.cursor[0]
        if line_nr <= self._help_length:
            lfCmd("norm! j")
            return

        if line_nr in self._selections:
            lfCmd("call matchdelete(%d)" % self._selections[line_nr])
            del self._selections[line_nr]
        else:
            id = int(lfEval("matchadd('Lf_hl_selection', '\%%%dl.')" % line_nr))
            self._selections[line_nr] = id

    def selectMulti(self):
        orig_line = self._getInstance().window.cursor[0]
        nr = self._getInstance().window.number
        if (int(lfEval("v:mouse_win")) != 0 and
                nr != int(lfEval("v:mouse_win"))):
            return
        elif nr == int(lfEval("v:mouse_win")):
            cur_line = int(lfEval("v:mouse_lnum"))
        self.clearSelections()
        for i in range(min(orig_line, cur_line), max(orig_line, cur_line)+1):
            if i > self._help_length and i not in self._selections:
                id = int(lfEval("matchadd('Lf_hl_selection', '\%%%dl.')" % (i)))
                self._selections[i] = id

    def selectAll(self):
        line_num = len(self._getInstance().buffer)
        if line_num > 300:
            lfCmd("echohl Error | redraw | echo ' Too many files selected!' | echohl NONE")
            lfCmd("sleep 1")
            return
        for i in range(line_num):
            if i >= self._help_length and i+1 not in self._selections:
                id = int(lfEval("matchadd('Lf_hl_selection', '\%%%dl.')" % (i+1)))
                self._selections[i+1] = id

    def startExplorer(self, win_pos, *args, **kwargs):
        self._cli.setNameOnlyFeature(self._getExplorer().supportsNameOnly())
        lfCmd("echohl WarningMsg | redraw |"
              "echo ' searching ...' | echohl NONE")
        self._content = self._getExplorer().getContent(*args, **kwargs)
        if not self._content:
            lfCmd("echohl Error | redraw | echo ' No content!' | echohl NONE")
            return
        self._getInstance().enterBuffer(win_pos)

        self._getInstance().setStlCategory(self._getExplorer().getStlCategory())
        self._setStlMode()
        self._getInstance().setStlCwd(self._getExplorer().getStlCurDir())
        self._getInstance().setStlTotal(len(self._content))

        self._getInstance().setBuffer(self._content)
        lfCmd("normal! gg")
        self._index = 0

        self.input(False)

    @modifiableController
    def input(self, normal_mode=True):
        self._hideHelp()
        self._resetHighlights()

        for cmd in self._cli.input():
            if equal(cmd, '<Update>'):
                self._search(self._content)
            elif equal(cmd, '<Shorten>'):
                self._index = 0 # search from beginning
                self._search(self._content)
            elif equal(cmd, '<Mode>'):
                self._setStlMode()
                self._index = 0 # search from beginning
                if self._cli.pattern:
                    self._search(self._content)
            elif equal(cmd, '<Up>'):
                self._toUp()
            elif equal(cmd, '<Down>'):
                self._toDown()
            elif equal(cmd, '<LeftMouse>'):
                if self._leftClick():
                    break
            elif equal(cmd, '<2-LeftMouse>'):
                self._leftClick()
                self.accept()
                break
            elif equal(cmd, '<CR>'):
                self.accept()
                break
            elif equal(cmd, '<C-X>'):
                self.accept('h')
                break
            elif equal(cmd, '<C-]>'):
                self.accept('v')
                break
            elif equal(cmd, '<C-T>'):
                self.accept('t')
                break
            elif equal(cmd, '<Quit>'):
                self.quit()
                break
            elif equal(cmd, '<Esc>'):   # switch to Normal mode
                self.clearSelections()
                self._cli.hideCursor()
                self._createHelpHint()
                self._resetHighlights()
                break
            elif equal(cmd, '<F5>'):
                self.refresh(False)
            elif equal(cmd, '<C-LeftMouse>'):
                if self._getExplorer().supportsMulti():
                    self.addSelections()
            elif equal(cmd, '<S-LeftMouse>'):
                if self._getExplorer().supportsMulti():
                    self.selectMulti()
            elif equal(cmd, '<C-A>'):
                if self._getExplorer().supportsMulti():
                    self.selectAll()
            elif equal(cmd, '<C-L>'):
                self.clearSelections()
            else:
                if self._cmdExtension(cmd):
                    break
Ejemplo n.º 7
0
class Manager(object):
    def __init__(self):
        self._buffer_name = vim.eval("expand('$VIMRUNTIME/LeaderF')")
        self._win_pos = int(vim.eval("g:Lf_WindowPosition"))
        self._autochdir = 0
        self._cli = LfCli()
        self._explorer = None
        self._content = []
        self._index = 0
        self._help_length = 0
        self._show_help = False
        self._selections = {}
        self._highlight_pos = []
        self._highlight_refine_pos = []
        self._highlight_ids = []
        self._initStlVar()
        self._setStlMode()
        self._getExplClass()

    #**************************************************************
    # abstract methods, in fact all the functions can be overridden
    #**************************************************************
    def _getExplClass(self):
        """
        this function MUST be overridden
        return the name of Explorer class
        """
        raise NotImplementedError("Can't instantiate abstract class Manager "
                                  "with abstract methods _getExplClass")

    def _defineMaps(self):
        pass

    def _cmdExtension(self, cmd):
        """
        this function can be overridden to add new cmd
        if return true, exit the input loop
        """
        pass

    def _getDigest(self, line):
        """
        this function can be overridden
        specify what part to match regex for a line in the match window
        """
        return line

    def _createHelp(self):
        return []

    #**************************************************************

    def _createHelpHint(self):
        help = []
        if not self._show_help:
            help.append('" Press <F1> for help')
            help.append('" -------------------------------------------------'
                        '--------')
        else:
            help += self._createHelp()
        self._help_length = len(help)
        vim.current.buffer.append(help, 0)
        vim.current.window.cursor = (self._help_length + 1, 0)

    def _hideHelp(self):
        # should be 'del vim.current.buffer[:self._help_length]',
        # but there is bug in vim7.3
        for i in range(self._help_length):
            del vim.current.buffer[0]
        self._help_length = 0

    def _getExplorer(self):
        if self._explorer is None:
            self._explorer = self._getExplClass()()
        return self._explorer

    def _initStlVar(self):
        vim.command("let g:Lf_statusline_function = '-'")
        vim.command("let g:Lf_statusline_curDir = '-'")
        vim.command("let g:Lf_statusline_total = '0'")

    def _setStlMode(self):
        if self._cli.isFuzzy:
            if self._cli.isFullPath:
                vim.command("let g:Lf_statusline_mode = 'FullPath'")
            else:
                vim.command("let g:Lf_statusline_mode = 'NameOnly'")
        else:
            vim.command("let g:Lf_statusline_mode = 'Regexp'")

    def _bufwinnr(self, name):
        nr = 1
        for w in vim.windows:
            if (w.buffer.name is not None and os.path.abspath(w.buffer.name)
                    == os.path.abspath(name)):
                return nr
            nr += 1
        return 0

    def _resetAutochdir(self):
        if int(vim.eval("&autochdir")) == 1:
            self._autochdir = 1
            vim.command("set noautochdir")
        else:
            self._autochdir = 0

    def _setAutochdir(self):
        if self._autochdir == 1:
            # When autochdir is set, Vim will change the current working directory
            # to the directory containing the file which was opened or selected.
            vim.command("set autochdir")

    def _gotoBuffer(self):
        self._resetAutochdir()
        self._orig_buffer = vim.current.buffer.name
        self._orig_win_nr = int(vim.eval("winnr()"))
        nr = self._bufwinnr(self._buffer_name)
        if nr == 0:
            self._createBufWindow()
        else:
            vim.command("exec '%d wincmd w'" % nr)
        self._setAttributes()
        self._setStatusline()
        self._defineMaps()
        self._cli.clear()
        self.clearSelections()
        self._clearHighlights()
        self._highlight_pos = []
        self._highlight_refine_pos = []

    def _createBufWindow(self):
        if self._win_pos == 0:
            vim.command("silent! noa keepj hide edit %s" % self._buffer_name)
        elif self._win_pos == 1:
            vim.command("silent! noa keepj bo sp %s" % self._buffer_name)
        elif self._win_pos == 2:
            vim.command("silent! noa keepj to sp %s" % self._buffer_name)
        else:
            vim.command("silent! noa keepj to vsp %s" % self._buffer_name)

    def _setAttributes(self):
        vim.command("setlocal nobuflisted")
        vim.command("setlocal buftype=nofile")
        vim.command("setlocal bufhidden=hide")
        vim.command("setlocal noswapfile")
        vim.command("setlocal nolist")
        vim.command("setlocal nonumber")
        vim.command("setlocal norelativenumber")
        vim.command("setlocal nospell")
        vim.command("setlocal nowrap")
        vim.command("setlocal nofoldenable")
        vim.command("setlocal foldcolumn=1")
        vim.command("setlocal cursorline")
        vim.command("setlocal filetype=leaderf")

    def _setStatusline(self):
        if vim.eval("exists('g:loaded_airline')") == '1':
            return

        vim.command("setlocal statusline=LeaderF:\ [%#Lf_hl_stlFunction#"
                    "%{g:Lf_statusline_function}%#Lf_hl_none#,")
        vim.command("setlocal statusline+=\ %#Lf_hl_stlMode#%-9("
                    "%{g:Lf_statusline_mode}%#Lf_hl_none#]%)")
        vim.command("setlocal statusline+=\ \ %<%#Lf_hl_stlCurDir#"
                    "%{g:Lf_statusline_curDir}%#Lf_hl_none#")
        vim.command("setlocal statusline+=%=%lL/%-5L\ \ Total:"
                    "%{g:Lf_statusline_total}\ ")
        vim.command("redraw!")

    def _toUp(self):
        vim.command("norm! k")

    def _toDown(self):
        vim.command("norm! j")

    def _leftClick(self):
        nr = self._bufwinnr(self._buffer_name)
        if nr == int(vim.eval("v:mouse_win")):
            vim.command("exec v:mouse_lnum")
            vim.command("exec 'norm!'.v:mouse_col.'|'")
            self.clearSelections()
        else:
            self.quit()
            self._exit_loop = True

    def _search(self, content):
        self.clearSelections()
        self._cli.highlightMatches()
        cb = vim.current.buffer
        if not self._cli.pattern:  # e.g., when <BS> or <Del> is typed
            setBuffer(cb, content)
            self._clearHighlights()
            return

        if self._cli.isFuzzy:
            self._fuzzySearch(content)
        else:
            self._regexSearch(content)

    def _filter(self, step, filter_method, content):
        """ Construct a list from result of filter_method(content).

        Args:
            step: An integer to indicate the number of lines to filter one time.
            filter_method: A function to apply `content` as parameter and
                return an iterable.
            content: The list to be filtered.
        """
        length = len(content)
        cb = vim.current.buffer
        result = []
        if self._index == 0:
            self._index = step
            if self._index < length:
                result.extend(filter_method(content[:self._index]))
            else:
                result.extend(filter_method(content))
        else:
            result.extend(filter_method(cb[:]))
            if self._index < length:
                end = self._index + step - len(cb)
                result.extend(filter_method(content[self._index:end]))
                self._index = end

        while len(result) < 200 and self._index < length:
            end = self._index + 5000
            result.extend(filter_method(content[self._index:end]))
            self._index = end

        return result

    def _fuzzyFilter(self, is_full_path, get_weight, iterable):
        """
        return a list, each item is a pair (weight, line)
        """
        getDigest = self._getDigest
        if is_full_path:
            pairs = ((get_weight(getDigest(line)), line) for line in iterable)
        else:
            pairs = ((get_weight(getBasename(getDigest(line))), line)
                     for line in iterable)
        return (p for p in pairs if p[0])

    def _refineFilter(self, first_get_weight, get_weight, iterable):
        getDigest = self._getDigest
        triples = ((first_get_weight(getBasename(getDigest(line))),
                    get_weight(getDirname(getDigest(line))), line)
                   for line in iterable)
        return ((i[0] + i[1], i[2]) for i in triples if i[0] and i[1])

    def _fuzzySearch(self, content):
        encoding = vim.eval("&encoding")
        if self._cli.isRefinement:
            if self._cli.pattern[1] == '':  # e.g. abc;
                fuzzy_match = FuzzyMatch(self._cli.pattern[0], encoding)
                filter_method = partial(self._fuzzyFilter, False,
                                        fuzzy_match.getWeight)
                highlight_method = partial(self._highlight, False,
                                           fuzzy_match.getHighlights)
            elif self._cli.pattern[0] == '':  # e.g. ;abc
                fuzzy_match = FuzzyMatch(self._cli.pattern[1], encoding)
                filter_method = partial(self._fuzzyFilter, True,
                                        fuzzy_match.getWeight)
                highlight_method = partial(self._highlight, True,
                                           fuzzy_match.getHighlights)
            else:
                fuzzy_match0 = FuzzyMatch(self._cli.pattern[0], encoding)
                fuzzy_match1 = FuzzyMatch(self._cli.pattern[1], encoding)
                filter_method = partial(self._refineFilter,
                                        fuzzy_match0.getWeight,
                                        fuzzy_match1.getWeight)
                highlight_method = partial(self._highlightRefine,
                                           fuzzy_match0.getHighlights,
                                           fuzzy_match1.getHighlights)
        else:
            fuzzy_match = FuzzyMatch(self._cli.pattern, encoding)
            filter_method = partial(self._fuzzyFilter, self._cli.isFullPath,
                                    fuzzy_match.getWeight)
            highlight_method = partial(self._highlight, self._cli.isFullPath,
                                       fuzzy_match.getHighlights)

        pairs = self._filter(30000, filter_method, content)
        pairs.sort(key=operator.itemgetter(0), reverse=True)
        setBuffer(vim.current.buffer, [p[1] for p in pairs])
        highlight_method()

    def _clearHighlights(self):
        for i in self._highlight_ids:
            vim.command("silent! call matchdelete(%d)" % i)
        self._highlight_ids = []

    def _resetHighlights(self):
        self._clearHighlights()
        for i, pos in enumerate(self._highlight_pos, self._help_length + 1):
            pos = [[i] + p for p in pos]
            # The maximum number of positions is 8 in matchaddpos().
            for j in range(0, len(pos), 8):
                id = int(
                    vim.eval("matchaddpos('Lf_hl_match', %s)" %
                             str(pos[j:j + 8])))
                self._highlight_ids.append(id)

        for i, pos in enumerate(self._highlight_refine_pos,
                                self._help_length + 1):
            pos = [[i] + p for p in pos]
            # The maximum number of positions is 8 in matchaddpos().
            for j in range(0, len(pos), 8):
                id = int(
                    vim.eval("matchaddpos('Lf_hl_match_refine', %s)" %
                             str(pos[j:j + 8])))
                self._highlight_ids.append(id)

    def _highlight(self, is_full_path, get_highlights):
        # matchaddpos() is introduced by Patch 7.4.330
        if (vim.eval("exists('*matchaddpos')") == '0'
                or vim.eval("g:Lf_HighlightIndividual") == '0'):
            return
        cb = vim.current.buffer
        if len(cb) == 1 and cb[0] == '':  # buffer is empty.
            return

        highlight_number = int(vim.eval("g:Lf_NumberOfHighlight"))
        self._clearHighlights()

        getDigest = self._getDigest
        if is_full_path:
            # e.g., self._highlight_pos = [ [ [2,3], [6,2] ], [ [1,4], [7,6], ... ], ... ]
            # where [2, 3] indicates the highlight starts at the 2nd column with the
            # length of 3 in bytes
            self._highlight_pos = [
                get_highlights(getDigest(line))
                for line in cb[:highlight_number]
            ]
            for i, pos in enumerate(self._highlight_pos, 1):
                pos = [[i] + p for p in pos]
                # The maximum number of positions is 8 in matchaddpos().
                for j in range(0, len(pos), 8):
                    id = int(
                        vim.eval("matchaddpos('Lf_hl_match', %s)" %
                                 str(pos[j:j + 8])))
                    self._highlight_ids.append(id)
        else:
            self._highlight_pos = [
                get_highlights(getBasename(getDigest(line)))
                for line in cb[:highlight_number]
            ]
            for i, pos in enumerate(self._highlight_pos, 1):
                dir_len = len(getDirname(cb[i - 1]))
                if dir_len > 0:
                    for j in range(len(pos)):
                        pos[j][0] += dir_len + 1
                pos = [[i] + p for p in pos]
                # The maximum number of positions is 8 in matchaddpos().
                for j in range(0, len(pos), 8):
                    id = int(
                        vim.eval("matchaddpos('Lf_hl_match', %s)" %
                                 str(pos[j:j + 8])))
                    self._highlight_ids.append(id)

    def _highlightRefine(self, first_get_highlights, get_highlights):
        # matchaddpos() is introduced by Patch 7.4.330
        if (vim.eval("exists('*matchaddpos')") == '0'
                or vim.eval("g:Lf_HighlightIndividual") == '0'):
            return
        cb = vim.current.buffer
        if len(cb) == 1 and cb[0] == '':  # buffer is empty.
            return

        highlight_number = int(vim.eval("g:Lf_NumberOfHighlight"))
        self._clearHighlights()

        getDigest = self._getDigest
        self._highlight_pos = [
            first_get_highlights(getBasename(getDigest(line)))
            for line in cb[:highlight_number]
        ]
        for i, pos in enumerate(self._highlight_pos, 1):
            dir_len = len(getDirname(cb[i - 1]))
            if dir_len > 0:
                for j in range(len(pos)):
                    pos[j][0] += dir_len + 1
            pos = [[i] + p for p in pos]
            # The maximum number of positions is 8 in matchaddpos().
            for j in range(0, len(pos), 8):
                id = int(
                    vim.eval("matchaddpos('Lf_hl_match', %s)" %
                             str(pos[j:j + 8])))
                self._highlight_ids.append(id)

        self._highlight_refine_pos = [
            get_highlights(getDirname(getDigest(line)))
            for line in cb[:highlight_number]
        ]
        for i, pos in enumerate(self._highlight_refine_pos, 1):
            pos = [[i] + p for p in pos]
            # The maximum number of positions is 8 in matchaddpos().
            for j in range(0, len(pos), 8):
                id = int(
                    vim.eval("matchaddpos('Lf_hl_match_refine', %s)" %
                             str(pos[j:j + 8])))
                self._highlight_ids.append(id)

    def _regexFilter(self, iterable):
        try:
            if ('-2' == vim.eval("g:LfNoErrMsgMatch('', '%s')" %
                                 escQuote(self._cli.pattern))):
                return iter([])
            else:
                return (line for line in iterable if '-1' !=
                        vim.eval("g:LfNoErrMsgMatch('%s', '%s')" %
                                 (escQuote(self._getDigest(line).strip()),
                                  escQuote(self._cli.pattern))))
        except vim.error:
            return iter([])

    def _regexSearch(self, content):
        if not self._cli.isPrefix:
            self._index = 0
        lines = self._filter(8000, self._regexFilter, content)
        setBuffer(vim.current.buffer, lines)

    def clearSelections(self):
        for i in self._selections.values():
            vim.command("call matchdelete(%d)" % i)
        self._selections.clear()

    def toggleHelp(self):
        vim.command("setlocal modifiable")
        self._show_help = not self._show_help
        for i in range(self._help_length):
            del vim.current.buffer[0]
        self._createHelpHint()
        self.clearSelections()
        self._resetHighlights()
        vim.command("setlocal nomodifiable")

    def _accept(self, file, mode):
        try:
            if file:
                if mode == '':
                    pass
                elif mode == 'h':
                    vim.command("split")
                elif mode == 'v':
                    vim.command("vsplit")
                elif mode == 't':
                    vim.command("tabedit")
                    tab_pos = int(vim.eval("g:Lf_TabpagePosition"))
                    if tab_pos == 0:
                        vim.command("tabm 0")
                    elif tab_pos == 1:
                        vim.command("tabm -1")
                    elif tab_pos == 3:
                        vim.command("tabm")
                self._getExplorer().acceptSelection(file)
        except (KeyboardInterrupt, vim.error):
            pass

    def accept(self, mode=''):
        if vim.current.window.cursor[0] <= self._help_length:
            vim.command("norm! j")
            return
        if len(self._selections) > 0:
            files = []
            for i in self._selections:
                files.append(vim.current.buffer[i - 1])
            self._quit()
            if mode == '':
                # It will raise E480 without 'silent!'
                vim.command("silent! argdelete *")
                for file in files:
                    vim.command("argadd %s" % escSpecial(file))
                self._accept(files[0], mode)
            else:
                for file in files:
                    self._accept(file, mode)
        else:
            file = '' if vim.current.line == '' else vim.current.line
            self._quit()
            self._accept(file, mode)
        self._setAutochdir()

    def _quit(self):
        self._cli.clear()
        self.clearSelections()
        self._clearHighlights()
        if self._win_pos != 0 and len(vim.windows) > 1:
            vim.command("hide")
            # 'silent!' is used to skip error E16.
            vim.command("silent! exec '%d wincmd w'" % self._orig_win_nr)
        else:
            if self._orig_buffer is None or vim.eval(
                    "bufexists('%s')" % escQuote(self._orig_buffer)) == '0':
                vim.command("bd")
            else:
                vim.command("hide edit %s" % escSpecial(self._orig_buffer))
        if self._win_pos != 0:
            vim.command("call getchar(0) | redraw | echo")
        else:
            vim.command("call getchar(0)")

    def quit(self):
        self._quit()
        self._setAutochdir()

    def refresh(self, normal_mode=True):
        self._content = self._getExplorer().getFreshContent()
        if self._content is None:
            return

        if normal_mode:  # when called in Normal mode
            vim.command("setlocal modifiable")

        setBuffer(vim.current.buffer, self._content)
        if self._cli.pattern:
            self._index = 0
            self._search(self._content)

        if normal_mode:  # when called in Normal mode
            self._createHelpHint()
            self._resetHighlights()
            vim.command("setlocal nomodifiable")

    def addSelections(self):
        nr = self._bufwinnr(self._buffer_name)
        if (int(vim.eval("v:mouse_win")) != 0
                and nr != int(vim.eval("v:mouse_win"))):
            return
        elif nr == int(vim.eval("v:mouse_win")):
            vim.command("exec v:mouse_lnum")
            vim.command("exec 'norm!'.v:mouse_col.'|'")
        line_nr = vim.current.window.cursor[0]
        if line_nr <= self._help_length:
            vim.command("norm! j")
            return

        if line_nr in self._selections:
            vim.command("call matchdelete(%d)" % self._selections[line_nr])
            del self._selections[line_nr]
        else:
            id = int(
                vim.eval("matchadd('Lf_hl_selection', '\%%%dl.')" % line_nr))
            self._selections[line_nr] = id

    def selectMulti(self):
        orig_line = vim.current.window.cursor[0]
        nr = self._bufwinnr(self._buffer_name)
        if (int(vim.eval("v:mouse_win")) != 0
                and nr != int(vim.eval("v:mouse_win"))):
            return
        elif nr == int(vim.eval("v:mouse_win")):
            cur_line = int(vim.eval("v:mouse_lnum"))
        self.clearSelections()
        for i in range(min(orig_line, cur_line), max(orig_line, cur_line) + 1):
            if i > self._help_length and i not in self._selections:
                id = int(
                    vim.eval("matchadd('Lf_hl_selection', '\%%%dl.')" % (i)))
                self._selections[i] = id

    def selectAll(self):
        for i in range(len(vim.current.buffer)):
            if i >= self._help_length and i + 1 not in self._selections:
                id = int(
                    vim.eval("matchadd('Lf_hl_selection', '\%%%dl.')" %
                             (i + 1)))
                self._selections[i + 1] = id

    def startExplorer(self, *args, **kwargs):
        self._cli.setNameOnlyFeature(self._getExplorer().supportsNameOnly())
        vim.command("let g:Lf_statusline_function = '%s'" %
                    self._getExplorer().getStlFunction())
        vim.command("echohl WarningMsg | redraw |"
                    "echo ' searching ...' | echohl NONE")
        self._content = self._getExplorer().getContent(*args, **kwargs)
        if not self._content:
            vim.command("echohl Error | redraw | echo ' No content!'")
            return

        vim.command("let g:Lf_statusline_curDir = '%s'" %
                    self._getExplorer().getStlCurDir())
        vim.command("let g:Lf_statusline_total = '%d'" % len(self._content))
        self._gotoBuffer()
        self.input(False)

    def input(self, normal_mode=True):
        vim.command("setlocal modifiable")
        self._hideHelp()
        self._resetHighlights()

        if not normal_mode:
            setBuffer(vim.current.buffer, self._content)
            self._index = 0

        quit = False
        for cmd in self._cli.input():
            if equal(cmd, '<Update>'):
                self._search(self._content)
            elif equal(cmd, '<Shorten>'):
                self._index = 0  # search from beginning
                self._search(self._content)
            elif equal(cmd, '<Mode>'):
                self._setStlMode()
                self._index = 0  # search from beginning
                if self._cli.pattern:
                    self._search(self._content)
            elif equal(cmd, '<Up>'):
                self._toUp()
            elif equal(cmd, '<Down>'):
                self._toDown()
            elif equal(cmd, '<LeftMouse>'):
                self._exit_loop = False
                self._leftClick()
                if self._exit_loop:
                    break
            elif equal(cmd, '<2-LeftMouse>'):
                self._leftClick()
                self.accept()
                break
            elif equal(cmd, '<CR>'):
                self.accept()
                break
            elif equal(cmd, '<C-X>'):
                self.accept('h')
                break
            elif equal(cmd, '<C-]>'):
                self.accept('v')
                break
            elif equal(cmd, '<C-T>'):
                self.accept('t')
                break
            elif equal(cmd, '<Quit>'):
                quit = True
                break
            elif equal(cmd, '<Esc>'):  # switch to Normal mode
                self.clearSelections()
                self._cli.hideCursor()
                self._createHelpHint()
                self._resetHighlights()
                vim.command("setlocal nomodifiable")
                break
            elif equal(cmd, '<F5>'):
                self.refresh(False)
            elif equal(cmd, '<C-LeftMouse>'):
                if self._getExplorer().supportsMulti():
                    self.addSelections()
            elif equal(cmd, '<S-LeftMouse>'):
                if self._getExplorer().supportsMulti():
                    self.selectMulti()
            elif equal(cmd, '<C-A>'):
                if self._getExplorer().supportsMulti():
                    self.selectAll()
            elif equal(cmd, '<C-L>'):
                self.clearSelections()
            else:
                if self._cmdExtension(cmd):
                    break
        if quit:  # due to a bug which is fixed by Patch 7.4.084, I have to write this ugly code
            self.quit()
Ejemplo n.º 8
0
class Manager(object):
    def __init__(self):
        self._bufName = vim.eval("expand('$VIMRUNTIME/[LeaderF]')")
        self._winPos = int(vim.eval("g:Lf_WindowPosition"))
        self._winHeight = int(vim.eval("g:Lf_WindowHeight"))
        self._maxLines = int(vim.eval("g:Lf_MaxLines"))
        self._autochdir = 0
        self._cli = LfCli()
        self._explorer = None
        self._index = 0
        self._helpLength = 0
        self._showHelp = False
        self._selections = {}
        self._initStlVar()
        self._setStlMode()
        self._getExplClass()

    #*****************************************************
    # abstract methods, in fact all the functions can be overridden
    #*****************************************************
    def _getExplClass(self):
        '''
        this function MUST be overridden
        return the name of Explorer class
        '''
        raise NotImplementedError(
            "Can't instantiate abstract class Manager with abstract methods _getExplClass"
        )

    def _defineMaps(self):
        pass

    def _cmdExtension(self, cmd):
        '''
        this function can be overridden to add new cmd
        if return true, exit the input loop
        '''
        pass

    def _getDigest(self, line):
        '''
        this function can be overridden
        specify what part to match regex for a line in the match window
        '''
        return line

    def _createHelp(self):
        return []

    #*****************************************************
    # private methods
    #*****************************************************
    def _createHelpHint(self):
        help = []
        if not self._showHelp:
            help.append('" Press <F1> for help')
            help.append(
                '" ---------------------------------------------------')
        else:
            help += self._createHelp()
        self._helpLength = len(help)
        vim.command("setlocal modifiable")
        vim.current.buffer.append(help, 0)
        vim.current.window.cursor = (self._helpLength + 1, 0)
        vim.command("setlocal nomodifiable")

    def _hideHelp(self):
        for i in range(self._helpLength):
            del vim.current.buffer[0]
        self._helpLength = 0

    def _getExplorer(self):
        if self._explorer is None:
            self._explorer = self._getExplClass()()
        return self._explorer

    def _initStlVar(self):
        vim.command("let g:Lf_statusline_function = '-'")
        vim.command("let g:Lf_statusline_curDir = '-'")
        vim.command("let g:Lf_statusline_total = '0'")

    def _setStlMode(self):
        if self._cli.isFuzzy:
            if self._cli.isFileNameOnly:
                vim.command("let g:Lf_statusline_mode = 'NameOnly'")
            else:
                vim.command("let g:Lf_statusline_mode = 'FullPath'")
        else:
            vim.command("let g:Lf_statusline_mode = 'Regexp'")

    def _bufwinnr(self, name):
        nr = 1
        for w in vim.windows:
            if w.buffer.name is not None and os.path.abspath(
                    w.buffer.name) == os.path.abspath(name):
                return nr
            nr += 1
        return 0

    def _gotoBuffer(self):
        if int(vim.eval("&autochdir")) == 1:
            self._autochdir = 1
            vim.command("set noautochdir")
        else:
            self._autochdir = 0

        self._origBuf = vim.current.buffer.name
        self._origWinNr = int(vim.eval("winnr()"))
        nr = self._bufwinnr(self._bufName)
        if nr == 0:
            self._createBufWindow()
        else:
            vim.command("exec '%d wincmd w'" % nr)
        self._setAttributes()
        self._setStatusline()
        self._defineMaps()

    def _createBufWindow(self):
        if self._winPos == 0:
            vim.command("silent! noa keepj hide edit %s" % self._bufName)
        elif self._winPos == 1:
            vim.command("silent! noa keepj bo %d sp %s" %
                        (self._winHeight, self._bufName))
        elif self._winPos == 2:
            vim.command("silent! noa keepj to %d sp %s" %
                        (self._winHeight, self._bufName))
        else:
            vim.command("silent! noa keepj to %d vsp %s" %
                        (self._winHeight, self._bufName))

    def _setAttributes(self):
        vim.command("setlocal nobuflisted")
        vim.command("setlocal buftype=nofile")
        vim.command("setlocal bufhidden=hide")
        vim.command("setlocal noswapfile")
        vim.command("setlocal nolist")
        vim.command("setlocal nonumber")
        vim.command("setlocal nospell")
        vim.command("setlocal nowrap")
        vim.command("setlocal nofoldenable")
        vim.command("setlocal foldcolumn=1")
        vim.command("setlocal cursorline")
        vim.command("setlocal filetype=LeaderF")

    def _setStatusline(self):
        vim.command(
            "setlocal statusline=LeaderF:\ [%#Lf_hl_stlFunction#%{g:Lf_statusline_function}%#Lf_hl_none#,"
        )
        vim.command(
            "setlocal statusline+=\ %#Lf_hl_stlMode#%-9(%{g:Lf_statusline_mode}%#Lf_hl_none#]%)"
        )
        vim.command(
            "setlocal statusline+=\ \ %<%#Lf_hl_stlCurDir#%{g:Lf_statusline_curDir}%#Lf_hl_none#"
        )
        vim.command(
            "setlocal statusline+=%=%lL/%-5L\ \ Total:%{g:Lf_statusline_total}\ "
        )
        vim.command("redraw!")

    def _toUp(self):
        vim.command("norm! k")

    def _toDown(self):
        vim.command("norm! j")

    def _leftClick(self):
        nr = self._bufwinnr(self._bufName)
        if nr == int(vim.eval("v:mouse_win")):
            vim.command("exec v:mouse_lnum")
            vim.command("exec 'norm!'.v:mouse_col.'|'")
            self.clearSelections()
        else:
            self.quit()
            self._exitLoop = True

    def _filter(self, iterable, regex):
        if self._cli.isFuzzy:
            if self._cli.isFileNameOnly:
                if self._cli.isMixed:
                    if regex[0] == '':
                        return [
                            line for line in iterable
                            if re.search(regex[1], self._getDigest(line), re.I)
                        ]
                    elif regex[1] == '':
                        return [
                            line for line in iterable if re.search(
                                regex[0],
                                os.path.basename(self._getDigest(line)), re.I)
                        ]
                    else:
                        iterable = [
                            line for line in iterable if re.search(
                                regex[0],
                                os.path.basename(self._getDigest(line)), re.I)
                        ]
                        return [
                            line for line in iterable if re.search(
                                regex[1], os.path.dirname(self._getDigest(
                                    line)), re.I)
                        ]
                else:
                    return [
                        line for line in iterable if re.search(
                            regex, os.path.basename(self._getDigest(line)),
                            re.I)
                    ]
            else:
                return [
                    line for line in iterable
                    if re.search(regex, self._getDigest(line), re.I)
                ]
        else:
            try:
                if '-2' == vim.eval("g:LfNoErrMsgMatch('', '%s')" %
                                    escQuote(regex)):
                    return []
                else:
                    return [
                        line for line in iterable if '-1' !=
                        vim.eval("g:LfNoErrMsgMatch('%s', '%s')" %
                                 (escQuote(self._getDigest(line).strip()),
                                  escQuote(regex)))
                    ]
            except vim.error:
                pass

    def _isPrefix(self,
                  regex):  #assume that there are no \%23l, \%23c, \%23v, \%...
        pos = self._cli.cursorPos
        if pos > 1:
            if regex[pos - 2] != '\\' and (regex[pos - 1].isalnum()
                                           or regex[pos - 1]
                                           in r'`$%*(-_+[\;:,. /?'):
                if regex[pos - 2] == '_':
                    if pos > 2 and regex[pos - 3] == '\\':  #\_x
                        return False
                    else:
                        return True
                if regex.endswith(r'\zs') or regex.endswith(r'\ze'):
                    return False
                return True
        return False

    def _search(self, content, regex):
        self.clearSelections()
        self._cli.highlightMatches()
        cb = vim.current.buffer
        if not regex:
            if self._maxLines > 0:
                setBuffer(cb, content[:self._maxLines])
            else:
                setBuffer(cb, content)
            return

        if not self._cli.isFuzzy:
            if not self._isPrefix(regex):
                self._index = 0

        step = int(vim.eval("g:Lf_SearchStep"))
        length = len(content)
        if self._index == 0:
            self._index = step
            if self._index < length:
                setBuffer(cb, self._filter(content[:self._index], regex))
            else:
                setBuffer(cb, self._filter(content, regex))
        else:
            setBuffer(cb, self._filter(cb[:], regex))

        while len(cb) < 1000 and self._index < length:
            end = self._index + step
            startLine = 0 if cb[0] == '' else len(cb)
            if end < length:
                appendBuffer(cb, self._filter(content[self._index:end], regex),
                             startLine)
            else:
                appendBuffer(cb, self._filter(content[self._index:], regex),
                             startLine)
            self._index = end
            if len(cb) > vim.current.window.height * 2:
                break

        if self._getExplorer().supportsSort():
            num = int(vim.eval("g:Lf_NumberOfSort"))
            if num == -1:
                self._sortResult(len(cb))
            elif num == 0:
                pass
            elif num == 1:
                self._sortResult(vim.current.window.height)
            else:
                self._sortResult(num)
            vim.current.window.cursor = (self._helpLength + 1, 0)

    def _getWeight(self, str, t):
        '''
        this function can be overridden to get the weight of the line,
        so that it can be applied in the sort algorithm
        '''
        pre = str.lower().index(t[0].lower())
        s = str[pre:]
        sl = len(s)
        tl = len(t)
        val = [[0 for col in range(tl)] for row in range(sl)]
        val[0][0] = 1
        JJ = 2
        for i in range(1, sl):
            J = JJ
            for j in range(min(J, tl)):
                if s[i].lower() != t[j].lower():
                    val[i][j] = val[i - 1][j]
                else:
                    if j == JJ - 1:
                        JJ += 1
                    if val[i - 1][j] == (j + 1) * (j + 1):  # prune
                        val[i][j] = (j + 1) * (j + 1)
                        continue
                    val[i][j] = val[i - 1][j]
                    ii = i
                    jj = j
                    k = 0
                    while ii >= 0 and jj >= 0:
                        if s[ii].lower() == t[jj].lower():
                            if i == j and val[i - 1][j - 1] == i * i:  # prune
                                val[i][j] = (i + 1) * (i + 1)
                                break
                            k += 1
                            if j - k >= 0:
                                if val[i - k][j - k] + k * k > val[i][j]:
                                    val[i][j] = val[i - k][j - k] + k * k
                            else:
                                val[i][j] = k * k
                            ii -= 1
                            jj -= 1
                        else:
                            break
                if val[i][j] == tl * tl:  # prune
                    return val[i][j] + 1.0 / (2 * pre + len(str))
        return val[sl - 1][tl - 1] + 1.0 / (2 * pre + len(str))

    def _sortResult(self, num):
        '''
        this function can be overridden to customize the sort algorithm
        '''
        cb = vim.current.buffer
        if len(cb) == 1 and cb[0] == '' or num == 0:
            return
        if self._cli.isFuzzy:
            if self._cli.isFileNameOnly:
                if self._cli.isMixed:
                    pass
                else:
                    pairs = [(i,
                              self._getWeight(
                                  os.path.basename(self._getDigest(cb[i])),
                                  self._cli.cmdline)) for i in range(len(cb))]
                    pairs = heapq.nlargest(num, pairs, key=lambda x: x[1])
                    lines = [cb[i[0]] for i in pairs]
                    append(cb, lines, 0)
                    pairs.sort(key=lambda x: x[0], reverse=True)
                    for i in pairs:
                        del cb[i[0] + len(pairs)]
            else:
                pass

    def _accept(self, file, mode):
        try:
            if file:
                if mode == '':
                    pass
                elif mode == 'h':
                    vim.command("split")
                elif mode == 'v':
                    vim.command("vsplit")
                elif mode == 't':
                    vim.command("tabedit")
                    tabPos = int(vim.eval("g:Lf_TabpagePosition"))
                    if tabPos == 0:
                        vim.command("tabm 0")
                    elif tabPos == 1:
                        vim.command("tabm -1")
                    elif tabPos == 3:
                        vim.command("tabm")
                self._getExplorer().acceptSelection(file)
        except (KeyboardInterrupt, vim.error):
            pass

    def clearSelections(self):
        for i in self._selections.values():
            vim.command("call matchdelete(%d)" % i)
        self._selections.clear()

    #*****************************************************
    # public methods
    #*****************************************************
    def toggleHelp(self):
        vim.command("setlocal modifiable")
        self._showHelp = not self._showHelp
        for i in range(self._helpLength):
            del vim.current.buffer[0]
        self._createHelpHint()
        self.clearSelections()
        vim.command("setlocal nomodifiable")

    def accept(self, mode=''):
        if vim.current.window.cursor[0] <= self._helpLength:
            vim.command("norm! j")
            return
        if len(self._selections) > 0:
            files = []
            for i in self._selections.keys():
                files.append(os.path.abspath(vim.current.buffer[i - 1]))
            self.quit()
            if mode == '':
                vim.command("argdelete *")
                for file in files:
                    vim.command("argadd %s" % escSpecial(file))
                self._accept(files[0], mode)
            else:
                for file in files:
                    self._accept(file, mode)
        else:
            file = os.path.abspath(vim.current.line)
            self.quit()
            self._accept(file, mode)

    def quit(self):
        self._cli.clear()
        self.clearSelections()
        if self._winPos != 0 and len(vim.windows) > 1:
            vim.command("hide")
            vim.command("exec '%d wincmd w'" % self._origWinNr)
        else:
            if self._origBuf is None or vim.eval(
                    "bufexists('%s')" % escQuote(self._origBuf)) == '0':
                vim.command("bd")
            else:
                vim.command("hide edit %s" % escSpecial(self._origBuf))
        if self._winPos != 0:
            vim.command("call getchar(0) | redraw | echo")
        else:
            vim.command("call getchar(0)")
        if self._autochdir == 1:
            cwd = os.getcwd()
            vim.command("set autochdir")  # I think vim has a bug here
            os.chdir(cwd)

    def refresh(self, content=None):
        rContent = self._getExplorer().getFreshContent()
        if rContent is None:
            return
        if content is None:
            vim.command("setlocal modifiable")
            setBuffer(vim.current.buffer, rContent)
            self._content = rContent
            if self._cli.regex:
                self._index = 0
                self._search(rContent, self._cli.regex)
            vim.command("setlocal nomodifiable")
        else:
            setBuffer(vim.current.buffer, rContent)
            content[:] = rContent  #use slice to change content
            if self._cli.regex:
                self._index = 0
                self._search(rContent, self._cli.regex)

    def addSelections(self):
        nr = self._bufwinnr(self._bufName)
        if int(vim.eval("v:mouse_win")) != 0 and nr != int(
                vim.eval("v:mouse_win")):
            return
        elif nr == int(vim.eval("v:mouse_win")):
            vim.command("exec v:mouse_lnum")
            vim.command("exec 'norm!'.v:mouse_col.'|'")
        lineNr = vim.current.window.cursor[0]
        if lineNr <= self._helpLength:
            vim.command("norm! j")
            return

        if lineNr in self._selections:
            vim.command("call matchdelete(%d)" % self._selections[lineNr])
            del self._selections[lineNr]
        else:
            id = int(
                vim.eval("matchadd('Lf_hl_selection', '\%%%dl.')" % lineNr))
            self._selections[lineNr] = id

        if int(vim.eval("v:mouse_win")) != 0:
            vim.command(
                'call feedkeys("\<C-@>")')  #vim has bug, so add this line

    def selectMulti(self):
        origLine = vim.current.window.cursor[0]
        nr = self._bufwinnr(self._bufName)
        if int(vim.eval("v:mouse_win")) != 0 and nr != int(
                vim.eval("v:mouse_win")):
            return
        elif nr == int(vim.eval("v:mouse_win")):
            vim.command("exec v:mouse_lnum")
            vim.command("exec 'norm!'.v:mouse_col.'|'")
        self.clearSelections()
        curLine = vim.current.window.cursor[0]
        for i in range(min(origLine, curLine), max(origLine, curLine) + 1):
            if i > self._helpLength and i not in self._selections:
                id = int(
                    vim.eval("matchadd('Lf_hl_selection', '\%%%dl.')" % (i)))
                self._selections[i] = id
        vim.command('call feedkeys("\<C-@>")')  #vim has bug, so add this line

    def selectAll(self):
        for i in range(len(vim.current.buffer)):
            if i >= self._helpLength and i + 1 not in self._selections:
                id = int(
                    vim.eval("matchadd('Lf_hl_selection', '\%%%dl.')" %
                             (i + 1)))
                self._selections[i + 1] = id

    def startExplorer(self, *args, **kwargs):
        self._cli.setFullPathFeature(self._getExplorer().supportsFullPath())
        vim.command("let g:Lf_statusline_function = '%s'" %
                    self._getExplorer().getStlFunction())
        vim.command(
            "echohl WarningMsg | redraw | echo ' searching ...' | echohl NONE")
        content = self._getExplorer().getContent(*args, **kwargs)
        if content is None:
            return
        self._gotoBuffer()
        vim.command("let g:Lf_statusline_curDir = '%s'" %
                    self._getExplorer().getStlCurDir())
        vim.command("let g:Lf_statusline_total = '%d'" % len(content))
        self.startExplAction(content)

    def startExplAction(self, content=None):
        vim.command("setlocal modifiable")
        self._hideHelp()
        if content is None:
            content = self._content
        else:
            if self._maxLines > 0:
                setBuffer(vim.current.buffer, content[:self._maxLines])
            else:
                setBuffer(vim.current.buffer, content)
            self._index = 0
        quit = False
        for cmd in self._cli.input():
            if equal(cmd, '<Update>'):
                self._search(content, self._cli.regex)
            elif equal(cmd, '<Shorten>'):
                self._index = 0
                self._search(content, self._cli.regex)
            elif equal(cmd, '<Mode>'):
                self._setStlMode()
                self._index = 0
                if self._cli.regex:
                    self._search(content, self._cli.regex)
            elif equal(cmd, '<Up>'):
                self._toUp()
            elif equal(cmd, '<Down>'):
                self._toDown()
            elif equal(cmd, '<LeftMouse>'):
                self._exitLoop = False
                self._leftClick()
                if self._exitLoop:
                    break
            elif equal(cmd, '<2-LeftMouse>'):
                self._leftClick()
                self.accept()
                break
            elif equal(cmd, '<CR>'):
                self.accept()
                break
            elif equal(cmd, '<C-X>'):
                self.accept('h')
                break
            elif equal(cmd, '<C-]>'):
                self.accept('v')
                break
            elif equal(cmd, '<C-T>'):
                self.accept('t')
                break
            elif equal(cmd, '<Quit>'):
                quit = True
                break
            elif equal(cmd, '<Esc>'):
                self.clearSelections()
                self._cli.hideCursor()
                vim.command("setlocal nomodifiable")
                self._content = content
                self._createHelpHint()
            elif equal(cmd, '<F5>'):
                self.refresh(content)
            elif equal(cmd, '<C-LeftMouse>'):
                if self._getExplorer().supportsMulti():
                    self.addSelections()
            elif equal(cmd, '<S-LeftMouse>'):
                if self._getExplorer().supportsMulti():
                    self.selectMulti()
            elif equal(cmd, '<C-A>'):
                if self._getExplorer().supportsMulti():
                    self.selectAll()
            elif equal(cmd, '<C-L>'):
                self.clearSelections()
            else:
                if self._cmdExtension(cmd):
                    break
        if quit:  #due to a bug, I have to write this ugly code
            self.quit()