chatsiri rattana | f3c9433 | 2012-04-23 22:12:51 +0700 | [diff] [blame] | 1 | "============================================================================= |
| 2 | " Copyright (c) 2007-2010 Takeshi NISHIDA |
| 3 | " |
| 4 | "============================================================================= |
| 5 | " LOAD GUARD {{{1 |
| 6 | |
| 7 | if !l9#guardScriptLoading(expand('<sfile>:p'), 0, 0, []) |
| 8 | finish |
| 9 | endif |
| 10 | |
| 11 | " }}}1 |
| 12 | "============================================================================= |
| 13 | " GLOBAL FUNCTIONS {{{1 |
| 14 | |
| 15 | |
| 16 | " returns list of paths. |
| 17 | " An argument for glob() is normalized in order to avoid a bug on Windows. |
| 18 | function fuf#glob(expr) |
| 19 | " Substitutes "\", because on Windows, "**\" doesn't include ".\", |
| 20 | " but "**/" include "./". I don't know why. |
| 21 | return split(glob(substitute(a:expr, '\', '/', 'g')), "\n") |
| 22 | endfunction |
| 23 | |
| 24 | " |
| 25 | function fuf#countModifiedFiles(files, time) |
| 26 | return len(filter(copy(a:files), 'getftime(expand(v:val)) > a:time')) |
| 27 | endfunction |
| 28 | |
| 29 | " |
| 30 | function fuf#getCurrentTagFiles() |
| 31 | return sort(filter(map(tagfiles(), 'fnamemodify(v:val, '':p'')'), 'filereadable(v:val)')) |
| 32 | endfunction |
| 33 | |
| 34 | " |
| 35 | function fuf#mapToSetSerialIndex(in, offset) |
| 36 | for i in range(len(a:in)) |
| 37 | let a:in[i].index = i + a:offset |
| 38 | endfor |
| 39 | return a:in |
| 40 | endfunction |
| 41 | |
| 42 | " |
| 43 | function fuf#updateMruList(mrulist, newItem, maxItem, exclude) |
| 44 | let result = copy(a:mrulist) |
| 45 | let result = filter(result,'v:val.word !=# a:newItem.word') |
| 46 | let result = insert(result, a:newItem) |
| 47 | if len(a:exclude) |
| 48 | let result = filter(result, 'v:val.word !~ a:exclude') |
| 49 | endif |
| 50 | return result[0 : a:maxItem - 1] |
| 51 | endfunction |
| 52 | |
| 53 | " takes suffix number. if no digits, returns -1 |
| 54 | function fuf#suffixNumber(str) |
| 55 | let s = matchstr(a:str, '\d\+$') |
| 56 | return (len(s) ? str2nr(s) : -1) |
| 57 | endfunction |
| 58 | |
| 59 | " "foo/bar/buz/hoge" -> { head: "foo/bar/buz/", tail: "hoge" } |
| 60 | function fuf#splitPath(path) |
| 61 | let head = matchstr(a:path, '^.*[/\\]') |
| 62 | return { |
| 63 | \ 'head' : head, |
| 64 | \ 'tail' : a:path[strlen(head):] |
| 65 | \ } |
| 66 | endfunction |
| 67 | |
| 68 | " "foo/.../bar/...hoge" -> "foo/.../bar/../../hoge" |
| 69 | function fuf#expandTailDotSequenceToParentDir(pattern) |
| 70 | return substitute(a:pattern, '^\(.*[/\\]\)\?\zs\.\(\.\+\)\ze[^/\\]*$', |
| 71 | \ '\=repeat(".." . l9#getPathSeparator(), len(submatch(2)))', '') |
| 72 | endfunction |
| 73 | |
| 74 | " |
| 75 | function fuf#formatPrompt(prompt, partialMatching, otherString) |
| 76 | let indicator = escape((a:partialMatching ? '!' : '') . a:otherString, '\') |
| 77 | return substitute(a:prompt, '[]', indicator, 'g') |
| 78 | endfunction |
| 79 | |
| 80 | " |
| 81 | function fuf#getFileLines(file) |
| 82 | let bufnr = (type(a:file) ==# type(0) ? a:file : bufnr('^' . a:file . '$')) |
| 83 | let lines = getbufline(bufnr, 1, '$') |
| 84 | if !empty(lines) |
| 85 | return lines |
| 86 | endif |
| 87 | return l9#readFile(a:file) |
| 88 | endfunction |
| 89 | |
| 90 | " |
| 91 | function fuf#makePreviewLinesAround(lines, indices, page, maxHeight) |
| 92 | let index = ((empty(a:indices) ? 0 : a:indices[0]) |
| 93 | \ + a:page * a:maxHeight) % len(a:lines) |
| 94 | if empty(a:lines) || a:maxHeight <= 0 |
| 95 | return [] |
| 96 | endif |
| 97 | let beg = max([0, index - a:maxHeight / 2]) |
| 98 | let end = min([beg + a:maxHeight, len(a:lines)]) |
| 99 | let beg = max([0, end - a:maxHeight]) |
| 100 | let lines = [] |
| 101 | for i in range(beg, end - 1) |
| 102 | let mark = (count(a:indices, i) ? '>' : ' ') |
| 103 | call add(lines, printf('%s%4d ', mark, i + 1) . a:lines[i]) |
| 104 | endfor |
| 105 | return lines |
| 106 | endfunction |
| 107 | |
| 108 | " a:file: a path string or a buffer number |
| 109 | function fuf#makePreviewLinesForFile(file, count, maxHeight) |
| 110 | let lines = fuf#getFileLines(a:file) |
| 111 | if empty(lines) |
| 112 | return [] |
| 113 | endif |
| 114 | let bufnr = (type(a:file) ==# type(0) ? a:file : bufnr('^' . a:file . '$')) |
| 115 | if exists('s:bufferCursorPosMap[bufnr]') |
| 116 | let indices = [s:bufferCursorPosMap[bufnr][1] - 1] |
| 117 | else |
| 118 | let indices = [] |
| 119 | endif |
| 120 | return fuf#makePreviewLinesAround( |
| 121 | \ lines, indices, a:count, a:maxHeight) |
| 122 | endfunction |
| 123 | |
| 124 | " |
| 125 | function fuf#echoWarning(msg) |
| 126 | call l9#echoHl('WarningMsg', a:msg, '[fuf] ', 1) |
| 127 | endfunction |
| 128 | |
| 129 | " |
| 130 | function fuf#echoError(msg) |
| 131 | call l9#echoHl('ErrorMsg', a:msg, '[fuf] ', 1) |
| 132 | endfunction |
| 133 | |
| 134 | " |
| 135 | function fuf#openBuffer(bufNr, mode, reuse) |
| 136 | if a:reuse && ((a:mode ==# s:OPEN_TYPE_SPLIT && |
| 137 | \ l9#moveToBufferWindowInCurrentTabpage(a:bufNr)) || |
| 138 | \ (a:mode ==# s:OPEN_TYPE_VSPLIT && |
| 139 | \ l9#moveToBufferWindowInCurrentTabpage(a:bufNr)) || |
| 140 | \ (a:mode ==# s:OPEN_TYPE_TAB && |
| 141 | \ l9#moveToBufferWindowInOtherTabpage(a:bufNr))) |
| 142 | return |
| 143 | endif |
| 144 | execute printf({ |
| 145 | \ s:OPEN_TYPE_CURRENT : '%sbuffer' , |
| 146 | \ s:OPEN_TYPE_SPLIT : '%ssbuffer' , |
| 147 | \ s:OPEN_TYPE_VSPLIT : 'vertical %ssbuffer', |
| 148 | \ s:OPEN_TYPE_TAB : 'tab %ssbuffer' , |
| 149 | \ }[a:mode], a:bufNr) |
| 150 | endfunction |
| 151 | |
| 152 | " |
| 153 | function fuf#openFile(path, mode, reuse) |
| 154 | let bufNr = bufnr('^' . a:path . '$') |
| 155 | if bufNr > -1 |
| 156 | call fuf#openBuffer(bufNr, a:mode, a:reuse) |
| 157 | else |
| 158 | execute { |
| 159 | \ s:OPEN_TYPE_CURRENT : 'edit ' , |
| 160 | \ s:OPEN_TYPE_SPLIT : 'split ' , |
| 161 | \ s:OPEN_TYPE_VSPLIT : 'vsplit ' , |
| 162 | \ s:OPEN_TYPE_TAB : 'tabedit ', |
| 163 | \ }[a:mode] . fnameescape(fnamemodify(a:path, ':~:.')) |
| 164 | endif |
| 165 | endfunction |
| 166 | |
| 167 | " |
| 168 | function fuf#openTag(tag, mode) |
| 169 | execute { |
| 170 | \ s:OPEN_TYPE_CURRENT : 'tjump ' , |
| 171 | \ s:OPEN_TYPE_SPLIT : 'stjump ' , |
| 172 | \ s:OPEN_TYPE_VSPLIT : 'vertical stjump ', |
| 173 | \ s:OPEN_TYPE_TAB : 'tab stjump ' , |
| 174 | \ }[a:mode] . a:tag |
| 175 | endfunction |
| 176 | |
| 177 | " |
| 178 | function fuf#openHelp(tag, mode) |
| 179 | execute { |
| 180 | \ s:OPEN_TYPE_CURRENT : 'help ' , |
| 181 | \ s:OPEN_TYPE_SPLIT : 'help ' , |
| 182 | \ s:OPEN_TYPE_VSPLIT : 'vertical help ', |
| 183 | \ s:OPEN_TYPE_TAB : 'tab help ' , |
| 184 | \ }[a:mode] . a:tag |
| 185 | endfunction |
| 186 | |
| 187 | " |
| 188 | function fuf#prejump(mode) |
| 189 | execute { |
| 190 | \ s:OPEN_TYPE_CURRENT : '' , |
| 191 | \ s:OPEN_TYPE_SPLIT : 'split' , |
| 192 | \ s:OPEN_TYPE_VSPLIT : 'vsplit' , |
| 193 | \ s:OPEN_TYPE_TAB : 'tab split', |
| 194 | \ }[a:mode] |
| 195 | endfunction |
| 196 | |
| 197 | " |
| 198 | function fuf#compareRanks(i1, i2) |
| 199 | if exists('a:i1.ranks') && exists('a:i2.ranks') |
| 200 | for i in range(min([len(a:i1.ranks), len(a:i2.ranks)])) |
| 201 | if a:i1.ranks[i] > a:i2.ranks[i] |
| 202 | return +1 |
| 203 | elseif a:i1.ranks[i] < a:i2.ranks[i] |
| 204 | return -1 |
| 205 | endif |
| 206 | endfor |
| 207 | endif |
| 208 | return 0 |
| 209 | endfunction |
| 210 | |
| 211 | " |
| 212 | function fuf#makePathItem(fname, menu, appendsDirSuffix) |
| 213 | let pathPair = fuf#splitPath(a:fname) |
| 214 | let dirSuffix = (a:appendsDirSuffix && isdirectory(expand(a:fname)) |
| 215 | \ ? l9#getPathSeparator() |
| 216 | \ : '') |
| 217 | return { |
| 218 | \ 'word' : a:fname . dirSuffix, |
| 219 | \ 'wordForPrimaryHead': s:toLowerForIgnoringCase(pathPair.head), |
| 220 | \ 'wordForPrimaryTail': s:toLowerForIgnoringCase(pathPair.tail), |
| 221 | \ 'wordForBoundary' : s:toLowerForIgnoringCase(s:getWordBoundaries(pathPair.tail)), |
| 222 | \ 'wordForRefining' : s:toLowerForIgnoringCase(a:fname . dirSuffix), |
| 223 | \ 'wordForRank' : s:toLowerForIgnoringCase(pathPair.tail), |
| 224 | \ 'menu' : a:menu, |
| 225 | \ } |
| 226 | endfunction |
| 227 | |
| 228 | " |
| 229 | function fuf#makeNonPathItem(word, menu) |
| 230 | let wordL = s:toLowerForIgnoringCase(a:word) |
| 231 | return { |
| 232 | \ 'word' : a:word, |
| 233 | \ 'wordForPrimary' : wordL, |
| 234 | \ 'wordForBoundary': s:toLowerForIgnoringCase(s:getWordBoundaries(a:word)), |
| 235 | \ 'wordForRefining': wordL, |
| 236 | \ 'wordForRank' : wordL, |
| 237 | \ 'menu' : a:menu, |
| 238 | \ } |
| 239 | endfunction |
| 240 | |
| 241 | " |
| 242 | function fuf#makePatternSet(patternBase, interpreter, partialMatching) |
| 243 | let MakeMatchingExpr = function(a:partialMatching |
| 244 | \ ? 's:makePartialMatchingExpr' |
| 245 | \ : 's:makeFuzzyMatchingExpr') |
| 246 | let [primary; refinings] = split(a:patternBase, g:fuf_patternSeparator, 1) |
| 247 | let elements = call(a:interpreter, [primary]) |
| 248 | let primaryExprs = map(elements.matchingPairs, 'MakeMatchingExpr(v:val[0], v:val[1])') |
| 249 | let refiningExprs = map(refinings, 's:makeRefiningExpr(v:val)') |
| 250 | return { |
| 251 | \ 'primary' : elements.primary, |
| 252 | \ 'primaryForRank': elements.primaryForRank, |
| 253 | \ 'filteringExpr' : join(primaryExprs + refiningExprs, ' && '), |
| 254 | \ } |
| 255 | endfunction |
| 256 | |
| 257 | " |
| 258 | function fuf#enumExpandedDirsEntries(dir, exclude) |
| 259 | let entries = fuf#glob(a:dir . '*') + fuf#glob(a:dir . '.*') |
| 260 | " removes "*/." and "*/.." |
| 261 | call filter(entries, 'v:val !~ ''\v(^|[/\\])\.\.?$''') |
| 262 | call map(entries, 'fuf#makePathItem(v:val, "", 1)') |
| 263 | if len(a:exclude) |
| 264 | call filter(entries, 'v:val.word !~ a:exclude') |
| 265 | endif |
| 266 | return entries |
| 267 | endfunction |
| 268 | |
| 269 | " |
| 270 | function fuf#mapToSetAbbrWithSnippedWordAsPath(items) |
| 271 | let maxLenStats = {} |
| 272 | call map(a:items, 's:makeFileAbbrInfo(v:val, maxLenStats)') |
| 273 | let snippedHeads = |
| 274 | \ map(maxLenStats, 's:getSnippedHead(v:key[: -2], v:val)') |
| 275 | return map(a:items, 's:setAbbrWithFileAbbrData(v:val, snippedHeads)') |
| 276 | endfunction |
| 277 | |
| 278 | " |
| 279 | function fuf#setAbbrWithFormattedWord(item, abbrIndex) |
| 280 | let lenMenu = (exists('a:item.menu') ? len(a:item.menu) + 2 : 0) |
| 281 | let abbrPrefix = (exists('a:item.abbrPrefix') ? a:item.abbrPrefix : '') |
| 282 | let a:item.abbr = abbrPrefix . a:item.word |
| 283 | if a:abbrIndex |
| 284 | let a:item.abbr = printf('%4d: ', a:item.index) . a:item.abbr |
| 285 | endif |
| 286 | let a:item.abbr = l9#snipTail(a:item.abbr, g:fuf_maxMenuWidth - lenMenu, s:ABBR_SNIP_MASK) |
| 287 | return a:item |
| 288 | endfunction |
| 289 | |
| 290 | " |
| 291 | function s:onCommandPre() |
| 292 | for m in filter(copy(fuf#getModeNames()), 'fuf#{v:val}#requiresOnCommandPre()') |
| 293 | call fuf#{m}#onCommandPre(getcmdtype() . getcmdline()) |
| 294 | endfor |
| 295 | " lets last entry become the newest in the history |
| 296 | call histadd(getcmdtype(), getcmdline()) |
| 297 | " this is not mapped again (:help recursive_mapping) |
| 298 | return "\<CR>" |
| 299 | endfunction |
| 300 | |
| 301 | " |
| 302 | let s:modeNames = [] |
| 303 | |
| 304 | " |
| 305 | function fuf#addMode(modeName) |
| 306 | if count(g:fuf_modesDisable, a:modeName) > 0 |
| 307 | return |
| 308 | endif |
| 309 | call add(s:modeNames, a:modeName) |
| 310 | call fuf#{a:modeName}#renewCache() |
| 311 | call fuf#{a:modeName}#onInit() |
| 312 | if fuf#{a:modeName}#requiresOnCommandPre() |
| 313 | " cnoremap has a problem, which doesn't expand cabbrev. |
| 314 | cmap <silent> <expr> <CR> <SID>onCommandPre() |
| 315 | endif |
| 316 | endfunction |
| 317 | |
| 318 | " |
| 319 | function fuf#getModeNames() |
| 320 | return s:modeNames |
| 321 | endfunction |
| 322 | |
| 323 | " |
| 324 | function fuf#defineLaunchCommand(CmdName, modeName, prefixInitialPattern, tempVars) |
| 325 | if empty(a:tempVars) |
| 326 | let preCmd = '' |
| 327 | else |
| 328 | let preCmd = printf('call l9#tempvariables#setList(%s, %s) | ', |
| 329 | \ string(s:TEMP_VARIABLES_GROUP), string(a:tempVars)) |
| 330 | endif |
| 331 | execute printf('command! -range -bang -narg=? %s %s call fuf#launch(%s, %s . <q-args>, len(<q-bang>))', |
| 332 | \ a:CmdName, preCmd, string(a:modeName), a:prefixInitialPattern) |
| 333 | endfunction |
| 334 | |
| 335 | " |
| 336 | function fuf#defineKeyMappingInHandler(key, func) |
| 337 | " hacks to be able to use feedkeys(). |
| 338 | execute printf( |
| 339 | \ 'inoremap <buffer> <silent> %s <C-r>=fuf#getRunningHandler().%s ? "" : ""<CR>', |
| 340 | \ a:key, a:func) |
| 341 | endfunction |
| 342 | |
| 343 | " |
| 344 | let s:oneTimeVariables = [] |
| 345 | |
| 346 | " |
| 347 | function fuf#setOneTimeVariables(...) |
| 348 | let s:oneTimeVariables += a:000 |
| 349 | endfunction |
| 350 | |
| 351 | " |
| 352 | function fuf#launch(modeName, initialPattern, partialMatching) |
| 353 | if exists('s:runningHandler') |
| 354 | call fuf#echoWarning('FuzzyFinder is running.') |
| 355 | endif |
| 356 | if count(fuf#getModeNames(), a:modeName) == 0 |
| 357 | echoerr 'This mode is not available: ' . a:modeName |
| 358 | return |
| 359 | endif |
| 360 | let s:runningHandler = fuf#{a:modeName}#createHandler(copy(s:handlerBase)) |
| 361 | let s:runningHandler.stats = fuf#loadDataFile(s:runningHandler.getModeName(), 'stats') |
| 362 | let s:runningHandler.partialMatching = a:partialMatching |
| 363 | let s:runningHandler.bufNrPrev = bufnr('%') |
| 364 | let s:runningHandler.lastCol = -1 |
| 365 | let s:runningHandler.windowRestoringCommand = winrestcmd() |
| 366 | call s:runningHandler.onModeEnterPre() |
| 367 | " NOTE: updatetime is set, because in Buffer-Tag mode on Vim 7.3 on Windows, |
| 368 | " Vim keeps from triggering CursorMovedI for updatetime after system() is |
| 369 | " called. I don't know why. |
| 370 | call fuf#setOneTimeVariables( |
| 371 | \ ['&completeopt', 'menuone'], |
| 372 | \ ['&ignorecase', 0], |
| 373 | \ ['&updatetime', 10], |
| 374 | \ ) |
| 375 | if s:runningHandler.getPreviewHeight() > 0 |
| 376 | call fuf#setOneTimeVariables( |
| 377 | \ ['&cmdheight', s:runningHandler.getPreviewHeight() + 1]) |
| 378 | endif |
| 379 | call l9#tempvariables#setList(s:TEMP_VARIABLES_GROUP, s:oneTimeVariables) |
| 380 | let s:oneTimeVariables = [] |
| 381 | call s:activateFufBuffer() |
| 382 | augroup FufLocal |
| 383 | autocmd! |
| 384 | autocmd CursorMovedI <buffer> call s:runningHandler.onCursorMovedI() |
| 385 | autocmd InsertLeave <buffer> nested call s:runningHandler.onInsertLeave() |
| 386 | augroup END |
| 387 | for [key, func] in [ |
| 388 | \ [ g:fuf_keyOpen , 'onCr(' . s:OPEN_TYPE_CURRENT . ')' ], |
| 389 | \ [ g:fuf_keyOpenSplit , 'onCr(' . s:OPEN_TYPE_SPLIT . ')' ], |
| 390 | \ [ g:fuf_keyOpenVsplit , 'onCr(' . s:OPEN_TYPE_VSPLIT . ')' ], |
| 391 | \ [ g:fuf_keyOpenTabpage , 'onCr(' . s:OPEN_TYPE_TAB . ')' ], |
| 392 | \ [ '<BS>' , 'onBs()' ], |
| 393 | \ [ '<C-h>' , 'onBs()' ], |
| 394 | \ [ '<C-w>' , 'onDeleteWord()' ], |
| 395 | \ [ g:fuf_keyPreview , 'onPreviewBase(1)' ], |
| 396 | \ [ g:fuf_keyNextMode , 'onSwitchMode(+1)' ], |
| 397 | \ [ g:fuf_keyPrevMode , 'onSwitchMode(-1)' ], |
| 398 | \ [ g:fuf_keySwitchMatching, 'onSwitchMatching()' ], |
| 399 | \ [ g:fuf_keyPrevPattern , 'onRecallPattern(+1)' ], |
| 400 | \ [ g:fuf_keyNextPattern , 'onRecallPattern(-1)' ], |
| 401 | \ ] |
| 402 | call fuf#defineKeyMappingInHandler(key, func) |
| 403 | endfor |
| 404 | " Starts Insert mode and makes CursorMovedI event now. Command prompt is |
| 405 | " needed to forces a completion menu to update every typing. |
| 406 | call setline(1, s:runningHandler.getPrompt() . a:initialPattern) |
| 407 | call s:runningHandler.onModeEnterPost() |
| 408 | call feedkeys("A", 'n') " startinsert! does not work in InsertLeave event handler |
| 409 | redraw |
| 410 | endfunction |
| 411 | |
| 412 | " |
| 413 | function fuf#loadDataFile(modeName, dataName) |
| 414 | if !s:dataFileAvailable |
| 415 | return [] |
| 416 | endif |
| 417 | let lines = l9#readFile(l9#concatPaths([g:fuf_dataDir, a:modeName, a:dataName])) |
| 418 | return map(lines, 'eval(v:val)') |
| 419 | endfunction |
| 420 | |
| 421 | " |
| 422 | function fuf#saveDataFile(modeName, dataName, items) |
| 423 | if !s:dataFileAvailable |
| 424 | return -1 |
| 425 | endif |
| 426 | let lines = map(copy(a:items), 'string(v:val)') |
| 427 | return l9#writeFile(lines, l9#concatPaths([g:fuf_dataDir, a:modeName, a:dataName])) |
| 428 | endfunction |
| 429 | |
| 430 | " |
| 431 | function fuf#getDataFileTime(modeName, dataName) |
| 432 | if !s:dataFileAvailable |
| 433 | return -1 |
| 434 | endif |
| 435 | return getftime(expand(l9#concatPaths([g:fuf_dataDir, a:modeName, a:dataName]))) |
| 436 | endfunction |
| 437 | |
| 438 | " |
| 439 | function s:createDataBufferListener(dataFile) |
| 440 | let listener = { 'dataFile': a:dataFile } |
| 441 | |
| 442 | function listener.onWrite(lines) |
| 443 | let [modeName, dataName] = split(self.dataFile, l9#getPathSeparator()) |
| 444 | let items = map(filter(a:lines, '!empty(v:val)'), 'eval(v:val)') |
| 445 | call fuf#saveDataFile(modeName, dataName, items) |
| 446 | echo "Data files updated" |
| 447 | return 1 |
| 448 | endfunction |
| 449 | |
| 450 | return listener |
| 451 | endfunction |
| 452 | |
| 453 | " |
| 454 | function s:createEditDataListener() |
| 455 | let listener = {} |
| 456 | |
| 457 | function listener.onComplete(dataFile, method) |
| 458 | let bufName = '[fuf-info]' |
| 459 | let lines = l9#readFile(l9#concatPaths([g:fuf_dataDir, a:dataFile])) |
| 460 | call l9#tempbuffer#openWritable(bufName, 'vim', lines, 0, 0, 0, |
| 461 | \ s:createDataBufferListener(a:dataFile)) |
| 462 | endfunction |
| 463 | |
| 464 | return listener |
| 465 | endfunction |
| 466 | |
| 467 | " |
| 468 | function s:getEditableDataFiles(modeName) |
| 469 | let dataFiles = fuf#{a:modeName}#getEditableDataNames() |
| 470 | call filter(dataFiles, 'fuf#getDataFileTime(a:modeName, v:val) != -1') |
| 471 | return map(dataFiles, 'l9#concatPaths([a:modeName, v:val])') |
| 472 | endfunction |
| 473 | |
| 474 | " |
| 475 | function fuf#editDataFile() |
| 476 | let dataFiles = map(copy(fuf#getModeNames()), 's:getEditableDataFiles(v:val)') |
| 477 | let dataFiles = l9#concat(dataFiles) |
| 478 | call fuf#callbackitem#launch('', 0, '>Mode>', s:createEditDataListener(), dataFiles, 0) |
| 479 | endfunction |
| 480 | |
| 481 | " |
| 482 | function fuf#getRunningHandler() |
| 483 | return s:runningHandler |
| 484 | endfunction |
| 485 | |
| 486 | " |
| 487 | function fuf#onComplete(findstart, base) |
| 488 | return s:runningHandler.onComplete(a:findstart, a:base) |
| 489 | endfunction |
| 490 | |
| 491 | " }}}1 |
| 492 | "============================================================================= |
| 493 | " LOCAL FUNCTIONS/VARIABLES {{{1 |
| 494 | |
| 495 | let s:TEMP_VARIABLES_GROUP = expand('<sfile>:p') |
| 496 | let s:ABBR_SNIP_MASK = '...' |
| 497 | let s:OPEN_TYPE_CURRENT = 1 |
| 498 | let s:OPEN_TYPE_SPLIT = 2 |
| 499 | let s:OPEN_TYPE_VSPLIT = 3 |
| 500 | let s:OPEN_TYPE_TAB = 4 |
| 501 | |
| 502 | " a:pattern: 'str' -> '\V\.\*s\.\*t\.\*r\.\*' |
| 503 | function s:makeFuzzyMatchingExpr(target, pattern) |
| 504 | let wi = '' |
| 505 | for c in split(a:pattern, '\zs') |
| 506 | if wi =~# '[^*?]$' && c !~ '[*?]' |
| 507 | let wi .= '*' |
| 508 | endif |
| 509 | let wi .= c |
| 510 | endfor |
| 511 | return s:makePartialMatchingExpr(a:target, wi) |
| 512 | endfunction |
| 513 | |
| 514 | " a:pattern: 'str' -> '\Vstr' |
| 515 | " 'st*r' -> '\Vst\.\*r' |
| 516 | function s:makePartialMatchingExpr(target, pattern) |
| 517 | let patternMigemo = s:makeAdditionalMigemoPattern(a:pattern) |
| 518 | if a:pattern !~ '[*?]' && empty(patternMigemo) |
| 519 | " NOTE: stridx is faster than regexp matching |
| 520 | return 'stridx(' . a:target . ', ' . string(a:pattern) . ') >= 0' |
| 521 | endif |
| 522 | return a:target . ' =~# ' . |
| 523 | \ string(l9#convertWildcardToRegexp(a:pattern)) . patternMigemo |
| 524 | endfunction |
| 525 | |
| 526 | " |
| 527 | function s:makeRefiningExpr(pattern) |
| 528 | if g:fuf_fuzzyRefining |
| 529 | let expr = s:makeFuzzyMatchingExpr('v:val.wordForRefining', a:pattern) |
| 530 | else |
| 531 | let expr = s:makePartialMatchingExpr('v:val.wordForRefining', a:pattern) |
| 532 | endif |
| 533 | if a:pattern =~# '\D' |
| 534 | return expr |
| 535 | else |
| 536 | return '(' . expr . ' || v:val.index == ' . string(a:pattern) . ')' |
| 537 | endif |
| 538 | endfunction |
| 539 | |
| 540 | " |
| 541 | function s:makeAdditionalMigemoPattern(pattern) |
| 542 | if !g:fuf_useMigemo || a:pattern =~# '[^\x01-\x7e]' |
| 543 | return '' |
| 544 | endif |
| 545 | return '\|\m' . substitute(migemo(a:pattern), '\\_s\*', '.*', 'g') |
| 546 | endfunction |
| 547 | |
| 548 | " |
| 549 | function s:interpretPrimaryPatternForPathTail(pattern) |
| 550 | let pattern = fuf#expandTailDotSequenceToParentDir(a:pattern) |
| 551 | let pairL = fuf#splitPath(s:toLowerForIgnoringCase(pattern)) |
| 552 | return { |
| 553 | \ 'primary' : pattern, |
| 554 | \ 'primaryForRank': pairL.tail, |
| 555 | \ 'matchingPairs' : [['v:val.wordForPrimaryTail', pairL.tail],], |
| 556 | \ } |
| 557 | endfunction |
| 558 | |
| 559 | " |
| 560 | function s:interpretPrimaryPatternForPath(pattern) |
| 561 | let pattern = fuf#expandTailDotSequenceToParentDir(a:pattern) |
| 562 | let patternL = s:toLowerForIgnoringCase(pattern) |
| 563 | let pairL = fuf#splitPath(patternL) |
| 564 | if g:fuf_splitPathMatching |
| 565 | let matches = [ |
| 566 | \ ['v:val.wordForPrimaryHead', pairL.head], |
| 567 | \ ['v:val.wordForPrimaryTail', pairL.tail], |
| 568 | \ ] |
| 569 | else |
| 570 | let matches = [ |
| 571 | \ ['v:val.wordForPrimaryHead . v:val.wordForPrimaryTail', patternL], |
| 572 | \ ] |
| 573 | endif |
| 574 | return { |
| 575 | \ 'primary' : pattern, |
| 576 | \ 'primaryForRank': pairL.tail, |
| 577 | \ 'matchingPairs' : matches, |
| 578 | \ } |
| 579 | endfunction |
| 580 | |
| 581 | " |
| 582 | function s:interpretPrimaryPatternForNonPath(pattern) |
| 583 | let patternL = s:toLowerForIgnoringCase(a:pattern) |
| 584 | return { |
| 585 | \ 'primary' : a:pattern, |
| 586 | \ 'primaryForRank': patternL, |
| 587 | \ 'matchingPairs' : [['v:val.wordForPrimary', patternL],], |
| 588 | \ } |
| 589 | endfunction |
| 590 | |
| 591 | " |
| 592 | function s:getWordBoundaries(word) |
| 593 | return substitute(a:word, '\a\zs\l\+\|\zs\A', '', 'g') |
| 594 | endfunction |
| 595 | |
| 596 | " |
| 597 | function s:toLowerForIgnoringCase(str) |
| 598 | return (g:fuf_ignoreCase ? tolower(a:str) : a:str) |
| 599 | endfunction |
| 600 | |
| 601 | " |
| 602 | function s:setRanks(item, pattern, exprBoundary, stats) |
| 603 | "let word2 = substitute(a:eval_word, '\a\zs\l\+\|\zs\A', '', 'g') |
| 604 | let a:item.ranks = [ |
| 605 | \ s:evaluateLearningRank(a:item.word, a:stats), |
| 606 | \ -s:scoreSequentialMatching(a:item.wordForRank, a:pattern), |
| 607 | \ -s:scoreBoundaryMatching(a:item.wordForBoundary, |
| 608 | \ a:pattern, a:exprBoundary), |
| 609 | \ a:item.index, |
| 610 | \ ] |
| 611 | return a:item |
| 612 | endfunction |
| 613 | |
| 614 | " |
| 615 | function s:evaluateLearningRank(word, stats) |
| 616 | for i in range(len(a:stats)) |
| 617 | if a:stats[i].word ==# a:word |
| 618 | return i |
| 619 | endif |
| 620 | endfor |
| 621 | return len(a:stats) |
| 622 | endfunction |
| 623 | |
| 624 | " range of return value is [0.0, 1.0] |
| 625 | function s:scoreSequentialMatching(word, pattern) |
| 626 | if empty(a:pattern) |
| 627 | return str2float('0.0') |
| 628 | endif |
| 629 | let pos = stridx(a:word, a:pattern) |
| 630 | if pos < 0 |
| 631 | return str2float('0.0') |
| 632 | endif |
| 633 | let lenRest = len(a:word) - len(a:pattern) - pos |
| 634 | return str2float(pos == 0 ? '0.5' : '0.0') + str2float('0.5') / (lenRest + 1) |
| 635 | endfunction |
| 636 | |
| 637 | " range of return value is [0.0, 1.0] |
| 638 | function s:scoreBoundaryMatching(wordForBoundary, pattern, exprBoundary) |
| 639 | if empty(a:pattern) |
| 640 | return str2float('0.0') |
| 641 | endif |
| 642 | if !eval(a:exprBoundary) |
| 643 | return 0 |
| 644 | endif |
| 645 | return (s:scoreSequentialMatching(a:wordForBoundary, a:pattern) + 1) / 2 |
| 646 | endfunction |
| 647 | |
| 648 | " |
| 649 | function s:highlightPrompt(prompt) |
| 650 | syntax clear |
| 651 | execute printf('syntax match %s /^\V%s/', g:fuf_promptHighlight, escape(a:prompt, '\/')) |
| 652 | endfunction |
| 653 | |
| 654 | " |
| 655 | function s:highlightError() |
| 656 | syntax clear |
| 657 | syntax match Error /^.*$/ |
| 658 | endfunction |
| 659 | |
| 660 | " |
| 661 | function s:expandAbbrevMap(pattern, abbrevMap) |
| 662 | let result = [a:pattern] |
| 663 | for [pattern, subs] in items(a:abbrevMap) |
| 664 | let exprs = result |
| 665 | let result = [] |
| 666 | for expr in exprs |
| 667 | let result += map(copy(subs), 'substitute(expr, pattern, escape(v:val, ''\''), "g")') |
| 668 | endfor |
| 669 | endfor |
| 670 | return l9#unique(result) |
| 671 | endfunction |
| 672 | |
| 673 | " |
| 674 | function s:makeFileAbbrInfo(item, maxLenStats) |
| 675 | let head = matchstr(a:item.word, '^.*[/\\]\ze.') |
| 676 | let a:item.abbr = { 'head' : head, |
| 677 | \ 'tail' : a:item.word[strlen(head):], |
| 678 | \ 'key' : head . '.', |
| 679 | \ 'prefix' : printf('%4d: ', a:item.index), } |
| 680 | if exists('a:item.abbrPrefix') |
| 681 | let a:item.abbr.prefix .= a:item.abbrPrefix |
| 682 | endif |
| 683 | let len = len(a:item.abbr.prefix) + len(a:item.word) + |
| 684 | \ (exists('a:item.menu') ? len(a:item.menu) + 2 : 0) |
| 685 | if !exists('a:maxLenStats[a:item.abbr.key]') || len > a:maxLenStats[a:item.abbr.key] |
| 686 | let a:maxLenStats[a:item.abbr.key] = len |
| 687 | endif |
| 688 | return a:item |
| 689 | endfunction |
| 690 | |
| 691 | " |
| 692 | function s:getSnippedHead(head, baseLen) |
| 693 | return l9#snipMid(a:head, len(a:head) + g:fuf_maxMenuWidth - a:baseLen, s:ABBR_SNIP_MASK) |
| 694 | endfunction |
| 695 | |
| 696 | " |
| 697 | function s:setAbbrWithFileAbbrData(item, snippedHeads) |
| 698 | let lenMenu = (exists('a:item.menu') ? len(a:item.menu) + 2 : 0) |
| 699 | let abbr = a:item.abbr.prefix . a:snippedHeads[a:item.abbr.key] . a:item.abbr.tail |
| 700 | let a:item.abbr = l9#snipTail(abbr, g:fuf_maxMenuWidth - lenMenu, s:ABBR_SNIP_MASK) |
| 701 | return a:item |
| 702 | endfunction |
| 703 | |
| 704 | " |
| 705 | let s:FUF_BUF_NAME = '[fuf]' |
| 706 | |
| 707 | " |
| 708 | function s:activateFufBuffer() |
| 709 | " lcd . : To avoid the strange behavior that unnamed buffer changes its cwd |
| 710 | " if 'autochdir' was set on. |
| 711 | lcd . |
| 712 | let cwd = getcwd() |
| 713 | call l9#tempbuffer#openScratch(s:FUF_BUF_NAME, 'fuf', [], 1, 0, 1, {}) |
| 714 | resize 1 " for issue #21 |
| 715 | " lcd ... : countermeasure against auto-cd script |
| 716 | lcd `=cwd` |
| 717 | setlocal nocursorline " for highlighting |
| 718 | setlocal nocursorcolumn " for highlighting |
| 719 | setlocal omnifunc=fuf#onComplete |
| 720 | redraw " for 'lazyredraw' |
| 721 | if exists(':AcpLock') |
| 722 | AcpLock |
| 723 | elseif exists(':AutoComplPopLock') |
| 724 | AutoComplPopLock |
| 725 | endif |
| 726 | endfunction |
| 727 | |
| 728 | " |
| 729 | function s:deactivateFufBuffer() |
| 730 | if exists(':AcpUnlock') |
| 731 | AcpUnlock |
| 732 | elseif exists(':AutoComplPopUnlock') |
| 733 | AutoComplPopUnlock |
| 734 | endif |
| 735 | call l9#tempbuffer#close(s:FUF_BUF_NAME) |
| 736 | endfunction |
| 737 | |
| 738 | " }}}1 |
| 739 | "============================================================================= |
| 740 | " s:handlerBase {{{1 |
| 741 | |
| 742 | let s:handlerBase = {} |
| 743 | |
| 744 | "----------------------------------------------------------------------------- |
| 745 | " PURE VIRTUAL FUNCTIONS {{{2 |
| 746 | " |
| 747 | " " |
| 748 | " s:handler.getModeName() |
| 749 | " |
| 750 | " " |
| 751 | " s:handler.getPrompt() |
| 752 | " |
| 753 | " " |
| 754 | " s:handler.getCompleteItems(patternSet) |
| 755 | " |
| 756 | " " |
| 757 | " s:handler.onOpen(word, mode) |
| 758 | " |
| 759 | " " Before entering FuzzyFinder buffer. This function should return in a short time. |
| 760 | " s:handler.onModeEnterPre() |
| 761 | " |
| 762 | " " After entering FuzzyFinder buffer. |
| 763 | " s:handler.onModeEnterPost() |
| 764 | " |
| 765 | " " After leaving FuzzyFinder buffer. |
| 766 | " s:handler.onModeLeavePost(opened) |
| 767 | " |
| 768 | " }}}2 |
| 769 | "----------------------------------------------------------------------------- |
| 770 | |
| 771 | " |
| 772 | function s:handlerBase.concretize(deriv) |
| 773 | call extend(self, a:deriv, 'error') |
| 774 | return self |
| 775 | endfunction |
| 776 | |
| 777 | " |
| 778 | function s:handlerBase.addStat(pattern, word) |
| 779 | let stat = { 'pattern' : a:pattern, 'word' : a:word } |
| 780 | call filter(self.stats, 'v:val !=# stat') |
| 781 | call insert(self.stats, stat) |
| 782 | let self.stats = self.stats[0 : g:fuf_learningLimit - 1] |
| 783 | endfunction |
| 784 | |
| 785 | " |
| 786 | function s:handlerBase.getMatchingCompleteItems(patternBase) |
| 787 | let MakeMatchingExpr = function(self.partialMatching |
| 788 | \ ? 's:makePartialMatchingExpr' |
| 789 | \ : 's:makeFuzzyMatchingExpr') |
| 790 | let patternSet = self.makePatternSet(a:patternBase) |
| 791 | let exprBoundary = s:makeFuzzyMatchingExpr('a:wordForBoundary', patternSet.primaryForRank) |
| 792 | let stats = filter( |
| 793 | \ copy(self.stats), 'v:val.pattern ==# patternSet.primaryForRank') |
| 794 | let items = self.getCompleteItems(patternSet.primary) |
| 795 | " NOTE: In order to know an excess, plus 1 to limit number |
| 796 | let items = l9#filterWithLimit( |
| 797 | \ items, patternSet.filteringExpr, g:fuf_enumeratingLimit + 1) |
| 798 | return map(items, |
| 799 | \ 's:setRanks(v:val, patternSet.primaryForRank, exprBoundary, stats)') |
| 800 | endfunction |
| 801 | |
| 802 | " |
| 803 | function s:handlerBase.onComplete(findstart, base) |
| 804 | if a:findstart |
| 805 | return 0 |
| 806 | elseif !self.existsPrompt(a:base) |
| 807 | return [] |
| 808 | endif |
| 809 | call s:highlightPrompt(self.getPrompt()) |
| 810 | let items = [] |
| 811 | for patternBase in s:expandAbbrevMap(self.removePrompt(a:base), g:fuf_abbrevMap) |
| 812 | let items += self.getMatchingCompleteItems(patternBase) |
| 813 | if len(items) > g:fuf_enumeratingLimit |
| 814 | let items = items[ : g:fuf_enumeratingLimit - 1] |
| 815 | call s:highlightError() |
| 816 | break |
| 817 | endif |
| 818 | endfor |
| 819 | if empty(items) |
| 820 | call s:highlightError() |
| 821 | else |
| 822 | call sort(items, 'fuf#compareRanks') |
| 823 | if g:fuf_autoPreview |
| 824 | call feedkeys("\<C-p>\<Down>\<C-r>=fuf#getRunningHandler().onPreviewBase(0) ? '' : ''\<CR>", 'n') |
| 825 | else |
| 826 | call feedkeys("\<C-p>\<Down>", 'n') |
| 827 | endif |
| 828 | let self.lastFirstWord = items[0].word |
| 829 | endif |
| 830 | return items |
| 831 | endfunction |
| 832 | |
| 833 | " |
| 834 | function s:handlerBase.existsPrompt(line) |
| 835 | return strlen(a:line) >= strlen(self.getPrompt()) && |
| 836 | \ a:line[:strlen(self.getPrompt()) -1] ==# self.getPrompt() |
| 837 | endfunction |
| 838 | |
| 839 | " |
| 840 | function s:handlerBase.removePrompt(line) |
| 841 | return a:line[(self.existsPrompt(a:line) ? strlen(self.getPrompt()) : 0):] |
| 842 | endfunction |
| 843 | |
| 844 | " |
| 845 | function s:handlerBase.restorePrompt(line) |
| 846 | let i = 0 |
| 847 | while i < len(self.getPrompt()) && i < len(a:line) && self.getPrompt()[i] ==# a:line[i] |
| 848 | let i += 1 |
| 849 | endwhile |
| 850 | return self.getPrompt() . a:line[i : ] |
| 851 | endfunction |
| 852 | |
| 853 | " |
| 854 | function s:handlerBase.onCursorMovedI() |
| 855 | if !self.existsPrompt(getline('.')) |
| 856 | call setline('.', self.restorePrompt(getline('.'))) |
| 857 | call feedkeys("\<End>", 'n') |
| 858 | elseif col('.') <= len(self.getPrompt()) |
| 859 | " if the cursor is moved before command prompt |
| 860 | call feedkeys(repeat("\<Right>", len(self.getPrompt()) - col('.') + 1), 'n') |
| 861 | elseif col('.') > strlen(getline('.')) && col('.') != self.lastCol |
| 862 | " if the cursor is placed on the end of the line and has been actually moved. |
| 863 | let self.lastCol = col('.') |
| 864 | let self.lastPattern = self.removePrompt(getline('.')) |
| 865 | call feedkeys("\<C-x>\<C-o>", 'n') |
| 866 | endif |
| 867 | endfunction |
| 868 | |
| 869 | " |
| 870 | function s:handlerBase.onInsertLeave() |
| 871 | unlet s:runningHandler |
| 872 | let tempVars = l9#tempvariables#getList(s:TEMP_VARIABLES_GROUP) |
| 873 | call l9#tempvariables#end(s:TEMP_VARIABLES_GROUP) |
| 874 | call s:deactivateFufBuffer() |
| 875 | call fuf#saveDataFile(self.getModeName(), 'stats', self.stats) |
| 876 | execute self.windowRestoringCommand |
| 877 | let fOpen = exists('s:reservedCommand') |
| 878 | if fOpen |
| 879 | call self.onOpen(s:reservedCommand[0], s:reservedCommand[1]) |
| 880 | unlet s:reservedCommand |
| 881 | endif |
| 882 | call self.onModeLeavePost(fOpen) |
| 883 | if exists('self.reservedMode') |
| 884 | call l9#tempvariables#setList(s:TEMP_VARIABLES_GROUP, tempVars) |
| 885 | call fuf#launch(self.reservedMode, self.lastPattern, self.partialMatching) |
| 886 | endif |
| 887 | endfunction |
| 888 | |
| 889 | " |
| 890 | function s:handlerBase.onCr(openType) |
| 891 | if pumvisible() |
| 892 | call feedkeys(printf("\<C-y>\<C-r>=fuf#getRunningHandler().onCr(%d) ? '' : ''\<CR>", |
| 893 | \ a:openType), 'n') |
| 894 | return |
| 895 | endif |
| 896 | if !empty(self.lastPattern) |
| 897 | call self.addStat(self.lastPattern, self.removePrompt(getline('.'))) |
| 898 | endif |
| 899 | if !self.isOpenable(getline('.')) |
| 900 | " To clear i_<C-r> expression (fuf#getRunningHandler().onCr...) |
| 901 | echo '' |
| 902 | return |
| 903 | endif |
| 904 | let s:reservedCommand = [self.removePrompt(getline('.')), a:openType] |
| 905 | call feedkeys("\<Esc>", 'n') " stopinsert behavior is strange... |
| 906 | endfunction |
| 907 | |
| 908 | " |
| 909 | function s:handlerBase.onBs() |
| 910 | call feedkeys((pumvisible() ? "\<C-e>\<BS>" : "\<BS>"), 'n') |
| 911 | endfunction |
| 912 | |
| 913 | " |
| 914 | function s:getLastBlockLength(pattern, patternIsPath) |
| 915 | let separatorPos = strridx(a:pattern, g:fuf_patternSeparator) |
| 916 | if separatorPos >= 0 |
| 917 | return len(a:pattern) - separatorPos |
| 918 | endif |
| 919 | if a:patternIsPath && a:pattern =~# '[/\\].' |
| 920 | return len(matchstr(a:pattern, '[^/\\]*.$')) |
| 921 | endif |
| 922 | return len(a:pattern) |
| 923 | endfunction |
| 924 | |
| 925 | " |
| 926 | function s:handlerBase.onDeleteWord() |
| 927 | let pattern = self.removePrompt(getline('.')[ : col('.') - 2]) |
| 928 | let numBs = s:getLastBlockLength(pattern, 1) |
| 929 | call feedkeys((pumvisible() ? "\<C-e>" : "") . repeat("\<BS>", numBs), 'n') |
| 930 | endfunction |
| 931 | |
| 932 | " |
| 933 | function s:handlerBase.onPreviewBase(repeatable) |
| 934 | if self.getPreviewHeight() <= 0 |
| 935 | return |
| 936 | elseif !pumvisible() |
| 937 | return |
| 938 | elseif !self.existsPrompt(getline('.')) |
| 939 | let word = self.removePrompt(getline('.')) |
| 940 | elseif !exists('self.lastFirstWord') |
| 941 | return |
| 942 | else |
| 943 | let word = self.lastFirstWord |
| 944 | endif |
| 945 | redraw |
| 946 | if a:repeatable && exists('self.lastPreviewInfo') && self.lastPreviewInfo.word ==# word |
| 947 | let self.lastPreviewInfo.count += 1 |
| 948 | else |
| 949 | let self.lastPreviewInfo = {'word': word, 'count': 0} |
| 950 | endif |
| 951 | let lines = self.makePreviewLines(word, self.lastPreviewInfo.count) |
| 952 | let lines = lines[: self.getPreviewHeight() - 1] |
| 953 | call map(lines, 'substitute(v:val, "\t", repeat(" ", &tabstop), "g")') |
| 954 | call map(lines, 'strtrans(v:val)') |
| 955 | call map(lines, 'l9#snipTail(v:val, &columns - 1, s:ABBR_SNIP_MASK)') |
| 956 | echo join(lines, "\n") |
| 957 | endfunction |
| 958 | |
| 959 | " |
| 960 | function s:handlerBase.onSwitchMode(shift) |
| 961 | let modes = copy(fuf#getModeNames()) |
| 962 | call map(modes, '{ "ranks": [ fuf#{v:val}#getSwitchOrder(), v:val ] }') |
| 963 | call filter(modes, 'v:val.ranks[0] >= 0') |
| 964 | call sort(modes, 'fuf#compareRanks') |
| 965 | let self.reservedMode = self.getModeName() |
| 966 | for i in range(len(modes)) |
| 967 | if modes[i].ranks[1] ==# self.getModeName() |
| 968 | let self.reservedMode = modes[(i + a:shift) % len(modes)].ranks[1] |
| 969 | break |
| 970 | endif |
| 971 | endfor |
| 972 | call feedkeys("\<Esc>", 'n') " stopinsert doesn't work. |
| 973 | endfunction |
| 974 | |
| 975 | " |
| 976 | function s:handlerBase.onSwitchMatching() |
| 977 | let self.partialMatching = !self.partialMatching |
| 978 | let self.lastCol = -1 |
| 979 | call setline('.', self.restorePrompt(self.lastPattern)) |
| 980 | call feedkeys("\<End>", 'n') |
| 981 | "call self.onCursorMovedI() |
| 982 | endfunction |
| 983 | |
| 984 | " |
| 985 | function s:handlerBase.onRecallPattern(shift) |
| 986 | let patterns = map(copy(self.stats), 'v:val.pattern') |
| 987 | if !exists('self.indexRecall') |
| 988 | let self.indexRecall = -1 |
| 989 | endif |
| 990 | let self.indexRecall += a:shift |
| 991 | if self.indexRecall < 0 |
| 992 | let self.indexRecall = -1 |
| 993 | elseif self.indexRecall >= len(patterns) |
| 994 | let self.indexRecall = len(patterns) - 1 |
| 995 | else |
| 996 | call setline('.', self.getPrompt() . patterns[self.indexRecall]) |
| 997 | call feedkeys("\<End>", 'n') |
| 998 | endif |
| 999 | endfunction |
| 1000 | |
| 1001 | " }}}1 |
| 1002 | "============================================================================= |
| 1003 | " INITIALIZATION {{{1 |
| 1004 | |
| 1005 | augroup FufGlobal |
| 1006 | autocmd! |
| 1007 | autocmd BufLeave * let s:bufferCursorPosMap[bufnr('')] = getpos('.') |
| 1008 | augroup END |
| 1009 | |
| 1010 | let s:bufferCursorPosMap = {} |
| 1011 | |
| 1012 | " |
| 1013 | let s:DATA_FILE_VERSION = 400 |
| 1014 | |
| 1015 | " |
| 1016 | function s:checkDataFileCompatibility() |
| 1017 | if empty(g:fuf_dataDir) |
| 1018 | let s:dataFileAvailable = 0 |
| 1019 | return |
| 1020 | endif |
| 1021 | let versionPath = l9#concatPaths([g:fuf_dataDir, 'VERSION']) |
| 1022 | let lines = l9#readFile(versionPath) |
| 1023 | if empty(lines) |
| 1024 | call l9#writeFile([s:DATA_FILE_VERSION], versionPath) |
| 1025 | let s:dataFileAvailable = 1 |
| 1026 | elseif str2nr(lines[0]) == s:DATA_FILE_VERSION |
| 1027 | let s:dataFileAvailable = 1 |
| 1028 | else |
| 1029 | call fuf#echoWarning(printf( |
| 1030 | \ "=======================================================\n" . |
| 1031 | \ " Existing data files for FuzzyFinder is no longer \n" . |
| 1032 | \ " compatible with this version of FuzzyFinder. Remove \n" . |
| 1033 | \ " %-53s\n" . |
| 1034 | \ "=======================================================\n" , |
| 1035 | \ string(g:fuf_dataDir))) |
| 1036 | call l9#inputHl('Question', 'Press Enter') |
| 1037 | let s:dataFileAvailable = 0 |
| 1038 | endif |
| 1039 | endfunction |
| 1040 | |
| 1041 | call s:checkDataFileCompatibility() |
| 1042 | |
| 1043 | " }}}1 |
| 1044 | "============================================================================= |
| 1045 | " vim: set fdm=marker: |
| 1046 | |