Guest User

Untitled

a guest
May 25th, 2018
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.19 KB | None | 0 0
  1. if &cp || exists('g:loaded_guten_tag')
  2. finish
  3. endif
  4. let g:loaded_guten_tag = 1
  5.  
  6. " Initialize the defaults - tag management
  7. if !exists('g:guten_tag_forbidden_paths')
  8. let g:guten_tag_forbidden_paths = ['v^/usr/.*', 'v^/etc/.*', 'v/var/.*']
  9. endif
  10. if !exists('g:guten_tag_markers')
  11. let g:guten_tag_markers = ['.git', 'src', 'source']
  12. endif
  13. if !exists('g:guten_tag_tags_files')
  14. let g:guten_tag_tags_files = ['tags', 'TAGS']
  15. endif
  16. if !exists('g:guten_tag_tags_when_forbidden')
  17. let g:guten_tag_tags_when_forbidden = &tags
  18. endif
  19.  
  20. " Initialize the defaults - tags exploration
  21. if !exists('g:guten_tag_indent')
  22. let g:guten_tag_indent = 2
  23. endif
  24. if !exists('g:guten_tag_split_style')
  25. let g:guten_tag_split_style = 'vertical'
  26. endif
  27. if !exists('g:guten_tag_window_height')
  28. let g:guten_tag_window_height = 30
  29. endif
  30. if !exists('g:gute_tag_window_width')
  31. let g:guten_tag_window_width = 35
  32. endif
  33.  
  34. augroup PluginGutenTag
  35. autocmd!
  36. autocmd BufRead,BufNewFile * call guten_tag#SetTagsPath()
  37. augroup END
  38.  
  39. " Commands
  40. command! GutenTagOpenTagWindow call guten_tag#CreateTagWindow()
  41.  
  42. " Search up the directory tree until one of the file markers is found, and
  43. " then uses this directory as a place to keep and look for tags file in.
  44. function! guten_tag#SetTagsPath()
  45. " First check if we're in an directory or a file which is forbidden from
  46. " having local tags files.
  47. let l:path = expand('%:p')
  48. if s:ForbiddenLocations()
  49. let &l:tags = g:guten_tag_tags_when_forbidden
  50. return
  51. endif
  52. " Find a marker
  53. while 1
  54. let l:path = fnamemodify(l:path, ':h')
  55. if s:HasMarkers(l:path)
  56. break
  57. endif
  58. if l:path ==# '/'
  59. return
  60. endif
  61. endwhile
  62. " Make a tags option
  63. let l:toplevel_tags = []
  64. for l:tagfile in g:guten_tag_tags_files
  65. call add(l:toplevel_tags, l:path . '/' . l:tagfile)
  66. endfor
  67. let &l:tags = join(l:toplevel_tags, ',')
  68. endfunction
  69.  
  70. " Test if a file matches any of the forbidden locations.
  71. function! s:ForbiddenLocations()
  72. let l:path = expand('%:p')
  73. for l:pattern in g:guten_tag_forbidden_paths
  74. if l:path =~# l:pattern
  75. return 1
  76. endif
  77. endfor
  78. return 0
  79. endfunction
  80.  
  81. " Test if there is a marker in a given directory.
  82. function! s:HasMarkers(dirpath)
  83. for l:marker in g:guten_tag_markers
  84. if !empty(globpath(a:dirpath, l:marker, 0, 1))
  85. return 1
  86. endif
  87. endfor
  88. return 0
  89. endfunction
  90.  
  91. " Create a new window and populate it with tag tree
  92. function! guten_tag#CreateTagWindow()
  93. let l:files = tagfiles()
  94. if len(l:files) == 0
  95. return
  96. endif
  97. let l:file = l:files[0]
  98. let l:tags = s:ParseFile(l:file)
  99. let l:content = s:TagWindowContent(l:tags)
  100. let l:window_name = s:MakeWindowName(l:file)
  101. if bufexists(l:window_name)
  102. return
  103. endif
  104. if g:guten_tag_split_style ==# 'vertical'
  105. let l:command = g:guten_tag_window_width . 'vnew "' . l:window_name . '"'
  106. else
  107. let l:command = g:guten_tag_window_height . 'new "' . l:window_name . '"'
  108. endif
  109. exec l:command
  110. call append(0, l:content)
  111. setlocal buftype=nofile
  112. setlocal nomodifiable
  113. exec "file! " . l:window_name
  114. endfunction
  115.  
  116. " Add a line describing a tag to a list of lines
  117. function! s:AddLine(add_to, tag, indent_level, print_parent)
  118. let l:kind = s:GetKind(a:tag)
  119. let l:new_line = l:kind ==# '' ? '' : l:kind . ' '
  120. let l:new_line = l:new_line . a:tag.name
  121. if a:print_parent
  122. let l:parent = s:GetParentName(a:tag)
  123. if l:parent isnot# v:null
  124. let l:new_line = l:new_line . ' (owned by ' . (l:parent) . ')'
  125. endif
  126. endif
  127. let l:new_line = repeat(' ', a:indent_level) . l:new_line
  128. call add(a:add_to, l:new_line)
  129. call add(a:add_to, "")
  130. endfunction
  131.  
  132. " Transform a flat list of tags into a dictionary where keys are file names
  133. " and values are lists of tags appearing in this file.
  134. function! s:BuildHierarchy(tags)
  135. let l:res = []
  136. " First, extract top-level tags
  137. for l:tag in a:tags
  138. let l:parent = s:GetParentName(l:tag)
  139. if l:parent is# v:null
  140. call add(l:res, l:tag)
  141. endif
  142. endfor
  143. " Now, attach non-top-level tags to them
  144. for l:tags_in_file in values(s:SeparateTagsByFilename(a:tags))
  145. for l:tag in l:tags_in_file
  146. let l:parent_name = s:GetParentName(l:tag)
  147. if l:parent_name is# v:null
  148. continue
  149. endif
  150. let l:parent = s:TagByName(l:parent_name, l:tags_in_file)
  151. if l:parent is# v:null
  152. " If it's not possible to determine tag's parent, treat it as a top
  153. " level tag.
  154. call add(l:res, l:tag)
  155. continue
  156. endif
  157. call add(l:parent.children, l:tag)
  158. endfor
  159. endfor
  160. return s:SeparateTagsByFilename(l:res)
  161. endfunction
  162.  
  163. " Get the name of a tag's parent, return v:null if there isn't one.
  164. function! s:GetParentName(tag)
  165. let l:fields = a:tag.fields
  166. let l:fields_to_test = ['struct', 'class']
  167. for l:fname in l:fields_to_test
  168. let l:res = get(l:fields, l:fname, v:null)
  169. if l:res isnot# v:null
  170. return l:res
  171. endif
  172. endfor
  173. return v:null
  174. endfunction
  175.  
  176. " Get the first letter of 'kind' attribute
  177. function! s:GetKind(tag)
  178. if has_key(a:tag.fields, 'kind')
  179. let l:kind = a:tag.fields.kind
  180. else
  181. return ""
  182. endif
  183. if l:kind ==# ""
  184. return ""
  185. else
  186. return strcharpart(l:kind, 0, 1)
  187. endif
  188. endfunction
  189.  
  190. " Test if a tag should not appear in the hierarchy of tags
  191. function! s:IgnoreTag(tag)
  192. let l:ignore_kinds = ['i', 'import', 'include', 'namespace']
  193. if !has_key(a:tag.fields, 'kind') || index(l:ignore_kinds, a:tag.fields.kind) ==# -1
  194. return 0
  195. else
  196. return 1
  197. endif
  198. endfunction
  199.  
  200. " Generate a name for tag exploration window
  201. function! s:MakeWindowName(tags_file)
  202. return 'Guten Tag ' . fnamemodify(a:tags_file, ':p')
  203. endfunction
  204.  
  205. " Parse a tag file into a sequence of top-level tags
  206. function! s:ParseFile(file)
  207. try
  208. let l:lines = readfile(a:file)
  209. catch /E484/
  210. return []
  211. endtry
  212. " Read all tags
  213. let l:all_tags = []
  214. for l:line in l:lines
  215. try
  216. let l:new_tag = s:ParseLine(l:line)
  217. catch /Comment line/
  218. continue
  219. endtry
  220. if !s:IgnoreTag(l:new_tag)
  221. call add(l:all_tags, l:new_tag)
  222. endif
  223. endfor
  224. return s:BuildHierarchy(uniq(sort(l:all_tags)))
  225. endfunction
  226.  
  227. " Parse a tag file line into a dict.
  228. function! s:ParseLine(line)
  229. if a:line =~# 'v^!.*'
  230. throw 'Comment line'
  231. endif
  232. let l:extract_name_and_file = split(a:line, "t")
  233. let l:res = {}
  234. let l:res.name = l:extract_name_and_file[0]
  235. let l:res.filename = l:extract_name_and_file[1]
  236. let l:res.children = []
  237. let l:rest = join(l:extract_name_and_file[2:], "t")
  238. " Extract EX search command
  239. let l:extract_fields = split(l:rest, '"')
  240. if len(l:extract_fields) <= 1
  241. res.fields = {}
  242. return res
  243. endif
  244. let l:res.search_cmd = l:extract_fields[0]
  245. " Just in case the fields contain a '"', join them back
  246. let l:extract_fields = join(l:extract_fields[1:], '"')
  247. let l:fields = {}
  248. for l:pair in split(l:extract_fields, "t")
  249. let l:split = split(l:pair, ':')
  250. if len(l:split) == 1
  251. let l:key = 'kind'
  252. let l:value = l:split[0]
  253. else
  254. let l:key = l:split[0]
  255. let l:value = join(l:split[1:], ':')
  256. endif
  257. let l:fields[l:key] = l:value
  258. endfor
  259. let l:res.fields = l:fields
  260. call s:TagPostprocessing(l:res)
  261. return res
  262. endfunction
  263.  
  264. " Return a dict where keys are filenames and values are lists of tags
  265. " appearing in the corresponding file
  266. function! s:SeparateTagsByFilename(tags)
  267. let l:res = {}
  268. for l:tag in a:tags
  269. let l:file = l:tag.filename
  270. if has_key(l:res, l:file)
  271. call add(l:res[l:file], l:tag)
  272. else
  273. let l:res[l:file] = [l:tag]
  274. endif
  275. endfor
  276. return l:res
  277. endfunction
  278.  
  279. " Return the first tag with a given name from a list, or null if there's no
  280. " such tag
  281. function! s:TagByName(name, list)
  282. for l:tag in a:list
  283. if l:tag.name ==# a:name
  284. return l:tag
  285. endif
  286. endfor
  287. return v:null
  288. endfunction
  289.  
  290. " Compare two tags by name
  291. function! s:TagComparator(tag1, tag2)
  292. let l:name1 = a:tag1.name
  293. let l:name2 = a:tag2.name
  294. if l:name1 ># l:name2
  295. return 1
  296. elseif l:name1 ==# l:name2
  297. return 0
  298. else
  299. return -1
  300. endif
  301. endfunction
  302.  
  303. " Return a list of lines in the tag overview window
  304. function! s:TagWindowContent(tags_hierarchy)
  305. let l:res = []
  306. for l:file in keys(a:tags_hierarchy)
  307. call add(l:res, fnamemodify(l:file, ':p'))
  308. call add(l:res, "")
  309. for l:toplevel in a:tags_hierarchy[l:file]
  310. let l:indent = g:guten_tag_indent
  311. call s:AddLine(l:res, l:toplevel, l:indent, 1)
  312. let l:traversal = [[l:toplevel, 0]] " tag, current child index
  313. let l:indent += g:guten_tag_indent
  314. while len(l:traversal) ># 0
  315. let [l:parent, l:child_index] = l:traversal[-1]
  316. if l:child_index ==# len(l:parent.children)
  317. unlet l:traversal[-1]
  318. let l:indent -= g:guten_tag_indent
  319. continue
  320. endif
  321. let l:cur = l:parent.children[l:child_index]
  322. call s:AddLine(l:res, l:cur, l:indent, 0)
  323. if len(l:cur.children) > 0
  324. call add(l:traversal, [l:cur, 0])
  325. let l:indent += g:guten_tag_indent
  326. echomsg l:indent
  327. continue
  328. endif
  329. let l:traversal[-1][1] += 1
  330. endwhile
  331. endfor
  332. endfor
  333. return l:res
  334. endfunction
  335.  
  336. " Do some actions after all fields and attributes of a tag were read
  337. function! s:TagPostprocessing(tag)
  338. " Presently, do nothing. TODO: language-specific processing, like function
  339. " signature extraction
  340. endfunction
Add Comment
Please, Sign In to add comment