autoignore.vim 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. " =============================================================================
  2. " File: autoload/ctrlp/autoignore.vim
  3. " Description: Auto-ignore Extension
  4. " Author: Ludovic Chabant <github.com/ludovicchabant>
  5. " =============================================================================
  6. " Global Settings {{{
  7. if exists('g:ctrlp_autoignore_loaded') && g:ctrlp_autoignore_loaded
  8. \ && !g:ctrlp_autoignore_debug
  9. finish
  10. endif
  11. let g:ctrlp_autoignore_loaded = 1
  12. if !exists('g:ctrlp_autoignore_debug')
  13. let g:ctrlp_autoignore_debug = 0
  14. endif
  15. if !exists('g:ctrlp_autoignore_trace')
  16. let g:ctrlp_autoignore_trace = 0
  17. endif
  18. " }}}
  19. " Initialization {{{
  20. if !exists('g:ctrlp_custom_ignore')
  21. let g:ctrlp_custom_ignore = {}
  22. endif
  23. let g:ctrlp_custom_ignore['func'] = 'ctrlp#autoignore#ignore'
  24. let g:ctrlp_custom_ignore['func-init'] = 'ctrlp#autoignore#ignore_init'
  25. let g:ctrlp_custom_ignore['func-close'] = 'ctrlp#autoignore#ignore_close'
  26. if !exists('g:ctrlp_root_markers')
  27. let g:ctrlp_root_markers = []
  28. endif
  29. call add(g:ctrlp_root_markers, '.ctrlpignore')
  30. " }}}
  31. " Internals {{{
  32. function! s:trace(message) abort
  33. if g:ctrlp_autoignore_trace
  34. echom "ctrlp_autoignore: " . a:message
  35. endif
  36. endfunction
  37. let s:proj_cache = {}
  38. let s:active_cwd = ''
  39. let s:active_cwd_len = 0
  40. let s:active_patterns = []
  41. let s:changed_wildignore = 0
  42. let s:prev_wildignore = ''
  43. function! s:load_project_patterns(root_dir) abort
  44. let l:ign_path = a:root_dir . '/.ctrlpignore'
  45. if !filereadable(l:ign_path)
  46. call s:trace("No pattern file at: " . l:ign_path)
  47. return []
  48. endif
  49. let l:cursyntax = 'regexp'
  50. let l:knownsyntaxes = ['regexp', 'wildignore']
  51. let l:patterns = []
  52. let l:lines = readfile(l:ign_path)
  53. for line in l:lines
  54. " Comment line?
  55. if match(line, '\v^\s*$') >= 0 || match(line, '\v^\s*#') >= 0
  56. continue
  57. endif
  58. " Syntax change?
  59. let l:matches = matchlist(line, '\v^syntax:\s?(\w+)\s*$')
  60. if len(l:matches) > 0
  61. let l:cursyntax = l:matches[1]
  62. if index(l:knownsyntaxes, l:cursyntax) < 0
  63. echoerr "ctrlp_autoignore: Unknown syntax '".l:cursyntax."' in: ".l:ign_path
  64. endif
  65. continue
  66. endif
  67. " Patterns!
  68. let l:matches = matchlist(line, '\v^((dir|file|link)\:)?(.*)')
  69. let l:mtype = l:matches[2]
  70. let l:mpat = l:matches[3]
  71. call add(l:patterns, {'syn': l:cursyntax, 'type': l:mtype, 'pat': l:mpat})
  72. endfor
  73. call s:trace("Loaded " . len(l:patterns) . " patterns from: " . l:ign_path)
  74. return l:patterns
  75. endfunction
  76. function! s:get_project_patterns(root_dir) abort
  77. let l:ign_path = a:root_dir . '/.ctrlpignore'
  78. let l:ign_mtime = getftime(l:ign_path)
  79. let l:patterns = get(s:proj_cache, a:root_dir)
  80. if type(l:patterns) == type({})
  81. " Check that these patterns are still valid.
  82. if l:ign_mtime < 0
  83. " File got deleted! :(
  84. let l:patterns['pats'] = []
  85. return l:patterns['pats']
  86. elseif l:ign_mtime <= l:patterns['mtime']
  87. " File hasn't changed! :)
  88. return l:patterns['pats']
  89. endif
  90. endif
  91. call s:trace("Loading patterns for project: " . a:root_dir)
  92. let l:loaded = s:load_project_patterns(a:root_dir)
  93. let s:proj_cache[a:root_dir] = {
  94. \'mtime': localtime(),
  95. \'pats': l:loaded}
  96. return l:loaded
  97. endfunction
  98. " The custom ignore function that CtrlP will be using in addition to
  99. " normal pattern-based matching.
  100. function! ctrlp#autoignore#ignore(item, type) abort
  101. let l:cnv_item = tr(strpart(a:item, s:active_cwd_len), "\\", "/")
  102. for pat in s:active_patterns
  103. if pat['syn'] != 'regexp'
  104. continue
  105. endif
  106. if pat['type'] == '' || pat['type'] == a:type
  107. if match(l:cnv_item, pat['pat']) >= 0
  108. call s:trace("Ignoring ".l:cnv_item." because of ".pat['pat'])
  109. return 1
  110. endif
  111. endif
  112. endfor
  113. return 0
  114. endfunction
  115. function! ctrlp#autoignore#ignore_init() abort
  116. let l:root = getcwd()
  117. let s:active_cwd = l:root
  118. " len+1 is for including the next separator after the root.
  119. let s:active_cwd_len = len(l:root) + 1
  120. let s:active_patterns = s:get_project_patterns(l:root)
  121. call s:trace("Got ".len(s:active_patterns)." patterns for ".l:root)
  122. let s:changed_wildignore = 0
  123. let s:prev_wildignore = &wildignore
  124. for pat in s:active_patterns
  125. if pat['syn'] == 'wildignore'
  126. execute 'set wildignore+='.pat['pat']
  127. let s:changed_wildignore = 1
  128. endif
  129. endfor
  130. if s:changed_wildignore
  131. call s:trace("Set wildignore to ".&wildignore)
  132. endif
  133. endfunction
  134. function! ctrlp#autoignore#ignore_close() abort
  135. if s:changed_wildignore
  136. execute 'set wildignore='.s:prev_wildignore
  137. let s:prev_wildignore = ''
  138. call s:trace("Set wildignore back to ".&wildignore)
  139. endif
  140. endfunction
  141. " List patterns for a given project's root.
  142. function! ctrlp#autoignore#get_patterns(root_dir) abort
  143. let l:patterns = s:get_project_patterns(a:root_dir)
  144. for pat in l:patterns
  145. let l:prefix = pat['type'] == '' ? '(all)' : pat['type']
  146. echom l:prefix . ':' . pat['pat']
  147. endfor
  148. endfunction
  149. " }}}
  150. " vim:fen:fdm=marker:fmr={{{,}}}:fdl=0:fdc=1:ts=2:sw=2:sts=2