1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
|
" Vim script
" Author: Peter Odding <peter@peterodding.com>
" Last Change: July 1, 2015
" URL: http://peterodding.com/code/vim/easytags/
let g:xolox#easytags#version = '3.11'
let g:xolox#easytags#default_pattern_prefix = '\C\<'
let g:xolox#easytags#default_pattern_suffix = '\>'
if !exists('s:timers_initialized')
let g:xolox#easytags#update_timer = xolox#misc#timer#resumable()
let g:xolox#easytags#highlight_timer = xolox#misc#timer#resumable()
let g:xolox#easytags#syntax_match_timer = xolox#misc#timer#resumable()
let g:xolox#easytags#syntax_keyword_timer = xolox#misc#timer#resumable()
let g:xolox#easytags#syntax_filter_stage_1_timer = xolox#misc#timer#resumable()
let g:xolox#easytags#syntax_filter_stage_2_timer = xolox#misc#timer#resumable()
let s:timers_initialized = 1
endif
" Plug-in initialization. {{{1
function! xolox#easytags#initialize(min_version) " {{{2
" Check that the location of Exuberant Ctags has been configured or that the
" correct version of the program exists in one of its default locations.
if exists('g:easytags_cmd') && xolox#easytags#check_ctags_compatible(g:easytags_cmd, a:min_version)
return 1
endif
if xolox#misc#os#is_win()
" FIXME The code below that searches the $PATH is not used on Windows at
" the moment because xolox#misc#path#which() generally produces absolute
" paths and on Windows these absolute paths tend to contain spaces which
" makes xolox#shell#execute_with_dll() fail. I've tried quoting the
" program name with double quotes but it fails just the same (it works
" with system() though). Anyway the problem of having multiple conflicting
" versions of Exuberant Ctags installed is not that relevant to Windows
" since it doesn't have a package management system. I still want to fix
" xolox#shell#execute_with_dll() though.
if xolox#easytags#check_ctags_compatible('ctags', a:min_version)
let g:easytags_cmd = 'ctags'
return 1
endif
else
" Exuberant Ctags can be installed under several names:
" - On Ubuntu Linux, Exuberant Ctags is installed as `ctags-exuberant'
" (and possibly `ctags' but that one can't be trusted :-)
" - On Debian Linux, Exuberant Ctags is installed as `exuberant-ctags'.
" - On Free-BSD, Exuberant Ctags is installed as `exctags'.
" IIUC on Mac OS X the program /usr/bin/ctags is installed by default but
" unusable and when the user installs Exuberant Ctags in an alternative
" location, it doesn't come before /usr/bin/ctags in the search path. To
" solve this problem in a general way and to save every Mac user out there
" some frustration the plug-in will search the path and consider every
" possible location, meaning that as long as Exuberant Ctags is installed
" in the $PATH the plug-in should find it automatically.
for program in xolox#misc#path#which('exuberant-ctags', 'ctags-exuberant', 'ctags', 'exctags')
if xolox#easytags#check_ctags_compatible(program, a:min_version)
let g:easytags_cmd = program
return 1
endif
endfor
endif
endfunction
function! xolox#easytags#check_ctags_compatible(name, min_version) " {{{2
" Not every executable out there named `ctags' is in fact Exuberant Ctags.
" This function makes sure it is because the easytags plug-in requires the
" --list-languages option (and more).
call xolox#misc#msg#debug("easytags.vim %s: Checking if Exuberant Ctags is installed as '%s'.", g:xolox#easytags#version, a:name)
" Make sure the given program is executable.
if !executable(a:name)
call xolox#misc#msg#debug("easytags.vim %s: Program '%s' is not executable!", g:xolox#easytags#version, a:name)
return 0
endif
" Make sure the command exits without reporting an error.
let command = a:name . ' --version'
let result = xolox#misc#os#exec({'command': command, 'check': 0})
if result['exit_code'] != 0
call xolox#misc#msg#debug("easytags.vim %s: Command '%s' returned nonzero exit code %i!", g:xolox#easytags#version, a:name, result['exit_code'])
return 0
else
let g:easytags_ctags_version = '5.5'
return 1
endif
endfunction
function! xolox#easytags#register(global) " {{{2
" Parse the &tags option and get a list of all tags files *including
" non-existing files* (this is why we can't just call tagfiles()).
let tagfiles = xolox#misc#option#split_tags(&tags)
let expanded = map(copy(tagfiles), 'resolve(expand(v:val))')
" Add the filename to the &tags option when the user hasn't done so already.
let tagsfile = a:global ? g:easytags_file : xolox#easytags#get_file_type_specific_tagsfile()
if index(expanded, xolox#misc#path#absolute(tagsfile)) == -1
" This is a real mess because of bugs in Vim?! :let &tags = '...' doesn't
" work on UNIX and Windows, :set tags=... doesn't work on Windows. What I
" mean with "doesn't work" is that tagfiles() == [] after the :let/:set
" command even though the tags file exists! One easy way to confirm that
" this is a bug in Vim is to type :set tags= then press <Tab> followed by
" <CR>. Now you entered the exact same value that the code below also did
" but suddenly Vim sees the tags file and tagfiles() != [] :-S
call add(tagfiles, tagsfile)
let value = xolox#misc#option#join_tags(tagfiles)
let cmd = (a:global ? 'set' : 'setl') . ' tags=' . escape(value, '\ ')
if xolox#misc#os#is_win() && v:version < 703
" TODO How to clear the expression from Vim's status line?
call feedkeys(":" . cmd . "|let &ro=&ro\<CR>", 'n')
else
execute cmd
endif
endif
endfunction
" Public interface through (automatic) commands. {{{1
function! xolox#easytags#autoload(event) " {{{2
try
let session_loading = xolox#easytags#session_is_loading() && a:event == 'BufReadPost'
let do_update = xolox#misc#option#get('easytags_auto_update', 1) && !session_loading
let do_highlight = xolox#misc#option#get('easytags_auto_highlight', 1) && &eventignore !~? '\<syntax\>'
" Don't execute this function for unsupported file types (doesn't load
" the list of file types if updates and highlighting are both disabled).
if (do_update || do_highlight) && !empty(xolox#easytags#filetypes#canonicalize(&filetype))
" Update entries for current file in tags file?
if do_update
let buffer_read = (a:event =~? 'BufReadPost')
let buffer_written = (a:event =~? 'BufWritePost')
if buffer_written || (buffer_read && xolox#misc#option#get('easytags_always_enabled', 0))
call xolox#easytags#update(1, 0, [])
endif
endif
" Apply highlighting of tags to current buffer?
if do_highlight
if !exists('b:easytags_last_highlighted')
call xolox#easytags#highlight()
else
for tagfile in tagfiles()
if getftime(tagfile) > b:easytags_last_highlighted
call xolox#easytags#highlight()
break
endif
endfor
endif
let b:easytags_last_highlighted = localtime()
endif
endif
catch
call xolox#misc#msg#warn("easytags.vim %s: %s (at %s)", g:xolox#easytags#version, v:exception, v:throwpoint)
endtry
endfunction
function! xolox#easytags#update(silent, filter_tags, filenames) " {{{2
let async = xolox#misc#option#get('easytags_async', 0)
call g:xolox#easytags#update_timer.start()
try
let have_args = !empty(a:filenames)
let starttime = xolox#misc#timer#start()
let cfile = s:check_cfile(a:silent, a:filter_tags, have_args)
let command_line = s:prep_cmdline(cfile, a:filenames)
if empty(command_line)
return 0
endif
" Pack all of the information required to update the tags in
" a Vim dictionary which is easy to serialize to a string.
let params = {}
let params['command'] = command_line
let params['ctags_version'] = g:easytags_ctags_version
let params['default_filetype'] = xolox#easytags#filetypes#canonicalize(&filetype)
let params['filter_tags'] = a:filter_tags || async
let params['have_args'] = have_args
let dynamic_tagsfile = xolox#easytags#get_dynamic_tagsfile()
if !empty(dynamic_tagsfile)
let params['tagsfile'] = dynamic_tagsfile
elseif !empty(g:easytags_by_filetype)
let params['directory'] = xolox#misc#path#absolute(g:easytags_by_filetype)
let params['filetypes'] = g:xolox#easytags#filetypes#ctags_to_vim
else
let params['tagsfile'] = xolox#easytags#get_global_tagsfile()
endif
if async
call xolox#misc#async#call({'function': 'xolox#easytags#update#with_vim', 'arguments': [params], 'callback': 'xolox#easytags#async_callback'})
else
call s:report_results(xolox#easytags#update#with_vim(params), 0)
" When :UpdateTags was executed manually we'll refresh the dynamic
" syntax highlighting so that new tags are immediately visible.
if !a:silent && xolox#misc#option#get('easytags_auto_highlight', 1)
HighlightTags
endif
endif
return 1
catch
call xolox#misc#msg#warn("easytags.vim %s: %s (at %s)", g:xolox#easytags#version, v:exception, v:throwpoint)
finally
call g:xolox#easytags#update_timer.stop()
endtry
endfunction
function! s:check_cfile(silent, filter_tags, have_args) " {{{3
if a:have_args
return ''
endif
let silent = a:silent || a:filter_tags
if xolox#misc#option#get('easytags_autorecurse', 0)
let cdir = xolox#easytags#utils#resolve(expand('%:p:h'))
if !isdirectory(cdir)
if silent | return '' | endif
throw "The directory of the current file doesn't exist yet!"
endif
return cdir
endif
let cfile = xolox#easytags#utils#resolve(expand('%:p'))
if cfile == '' || !filereadable(cfile)
if silent | return '' | endif
throw "You'll need to save your file before using :UpdateTags!"
elseif g:easytags_ignored_filetypes != '' && &ft =~ g:easytags_ignored_filetypes
if silent | return '' | endif
throw "The " . string(&ft) . " file type is explicitly ignored."
elseif empty(xolox#easytags#filetypes#canonicalize(&ft))
if silent | return '' | endif
throw "Exuberant Ctags doesn't support the " . string(&ft) . " file type!"
endif
return cfile
endfunction
function! s:prep_cmdline(cfile, arguments) " {{{3
let vim_file_type = xolox#easytags#filetypes#canonicalize(&filetype)
let custom_languages = xolox#misc#option#get('easytags_languages', {})
let language = get(custom_languages, vim_file_type, {})
if empty(language)
let cmdline = [xolox#easytags#ctags_command()]
call add(cmdline, '--fields=+l')
call add(cmdline, '--c-kinds=+p')
call add(cmdline, '--c++-kinds=+p')
call add(cmdline, '--sort=no')
call add(cmdline, '-f-')
if xolox#misc#option#get('easytags_include_members', 0)
call add(cmdline, '--extra=+q')
endif
else
let program = get(language, 'cmd', xolox#easytags#ctags_command())
if empty(program)
call xolox#misc#msg#warn("easytags.vim %s: No 'cmd' defined for language '%s', and also no global default!", g:xolox#easytags#version, vim_file_type)
return ''
endif
let cmdline = [program] + get(language, 'args', [])
call add(cmdline, xolox#misc#escape#shell(get(language, 'stdout_opt', '-f-')))
endif
let have_args = 0
if a:cfile != ''
if xolox#misc#option#get('easytags_autorecurse', 0)
call add(cmdline, empty(language) ? '-R' : xolox#misc#escape#shell(get(language, 'recurse_flag', '-R')))
call add(cmdline, xolox#misc#escape#shell(a:cfile))
else
if empty(language)
" TODO Should --language-force distinguish between C and C++?
" TODO --language-force doesn't make sense for JavaScript tags in HTML files?
let filetype = xolox#easytags#filetypes#to_ctags(vim_file_type)
call add(cmdline, xolox#misc#escape#shell('--language-force=' . filetype))
endif
call add(cmdline, xolox#misc#escape#shell(a:cfile))
endif
let have_args = 1
else
for arg in a:arguments
if arg =~ '^-'
call add(cmdline, arg)
let have_args = 1
else
let matches = split(expand(arg), "\n")
if !empty(matches)
call map(matches, 'xolox#misc#escape#shell(xolox#easytags#utils#canonicalize(v:val))')
call extend(cmdline, matches)
let have_args = 1
endif
endif
endfor
endif
" No need to run Exuberant Ctags without any filename arguments!
return have_args ? join(cmdline) : ''
endfunction
function! xolox#easytags#highlight() " {{{2
" TODO This is a mess; Re-implement Python version in Vim script, benchmark, remove Python version.
try
call g:xolox#easytags#highlight_timer.start()
let filetype = xolox#easytags#filetypes#canonicalize(&filetype)
let tagkinds = get(s:tagkinds, filetype, [])
if exists('g:syntax_on') && !empty(tagkinds) && !exists('b:easytags_nohl')
let starttime = xolox#misc#timer#start()
let used_python = 0
for tagkind in tagkinds
let hlgroup_tagged = tagkind.hlgroup . 'Tag'
" Define style on first run, clear highlighting on later runs.
if !hlexists(hlgroup_tagged)
execute 'highlight def link' hlgroup_tagged tagkind.hlgroup
else
execute 'syntax clear' hlgroup_tagged
endif
" Try to perform the highlighting using the fast Python script.
" TODO The tags files are read multiple times by the Python script
" within one run of xolox#easytags#highlight()
if s:highlight_with_python(hlgroup_tagged, tagkind)
let used_python = 1
else
" Fall back to the slow and naive Vim script implementation.
if !exists('taglist')
" Get the list of tags when we need it and remember the results.
let ctags_filetypes = xolox#easytags#filetypes#find_ctags_aliases(filetype)
let filetypes_pattern = printf('^\(%s\)$', join(map(ctags_filetypes, 'xolox#misc#escape#pattern(v:val)'), '\|'))
call g:xolox#easytags#syntax_filter_stage_1_timer.start()
let taglist = filter(taglist('.'), "get(v:val, 'language', '') =~? filetypes_pattern")
call g:xolox#easytags#syntax_filter_stage_1_timer.stop()
endif
" Filter a copy of the list of tags to the relevant kinds.
if has_key(tagkind, 'tagkinds')
let filter = 'v:val.kind =~ tagkind.tagkinds'
else
let filter = tagkind.vim_filter
endif
call g:xolox#easytags#syntax_filter_stage_2_timer.start()
let matches = filter(copy(taglist), filter)
call g:xolox#easytags#syntax_filter_stage_2_timer.stop()
if matches != []
" Convert matched tags to :syntax commands and execute them.
let use_keywords_when = xolox#misc#option#get('easytags_syntax_keyword', 'auto')
let has_default_pattern_prefix = (tagkind.pattern_prefix == g:xolox#easytags#default_pattern_prefix)
let has_default_pattern_suffix = (tagkind.pattern_suffix == g:xolox#easytags#default_pattern_suffix)
let has_non_default_patterns = !(has_default_pattern_prefix && has_default_pattern_suffix)
if use_keywords_when == 'always' || (use_keywords_when == 'auto' && !has_non_default_patterns)
" Vim's ":syntax keyword" command doesn't use the regular
" expression engine and the resulting syntax highlighting is
" therefor much faster. Because of this we use the syntax
" keyword command when 1) we can do so without sacrificing
" accuracy or 2) the user explicitly chose to sacrifice
" accuracy in order to make the highlighting faster.
call g:xolox#easytags#syntax_keyword_timer.start()
let keywords = {}
for tag in matches
if s:is_keyword_compatible(tag)
let keywords[tag.name] = 1
endif
endfor
if !empty(keywords)
let template = 'syntax keyword %s %s containedin=ALLBUT,%s'
let command = printf(template, hlgroup_tagged, join(keys(keywords)), xolox#easytags#syntax_groups_to_ignore())
call xolox#misc#msg#debug("easytags.vim %s: Executing command '%s'.", g:xolox#easytags#version, command)
execute command
" Remove the tags that we just highlighted from the list of
" tags that still need to be highlighted.
call filter(matches, "!s:is_keyword_compatible(v:val)")
endif
call g:xolox#easytags#syntax_keyword_timer.stop()
endif
if !empty(matches)
call g:xolox#easytags#syntax_match_timer.start()
let matches = xolox#misc#list#unique(map(matches, 'xolox#misc#escape#pattern(get(v:val, "name"))'))
let pattern = tagkind.pattern_prefix . '\%(' . join(matches, '\|') . '\)' . tagkind.pattern_suffix
let template = 'syntax match %s /%s/ containedin=ALLBUT,%s'
let command = printf(template, hlgroup_tagged, escape(pattern, '/'), xolox#easytags#syntax_groups_to_ignore())
call xolox#misc#msg#debug("easytags.vim %s: Executing command '%s'.", g:xolox#easytags#version, command)
try
execute command
catch /^Vim\%((\a\+)\)\=:E339/
let msg = "easytags.vim %s: Failed to highlight %i %s tags because pattern is too big! (%i KB)"
call xolox#misc#msg#warn(msg, g:xolox#easytags#version, len(matches), tagkind.hlgroup, len(pattern) / 1024)
endtry
call g:xolox#easytags#syntax_match_timer.stop()
endif
endif
endif
endfor
" Avoid flashing each highlighted buffer in front of the user when
" loading a session.
if !xolox#easytags#session_is_loading()
redraw
endif
let bufname = expand('%:p:~')
if bufname == ''
let bufname = 'unnamed buffer #' . bufnr('%')
endif
let msg = "easytags.vim %s: Highlighted tags in %s in %s%s."
call xolox#misc#timer#stop(msg, g:xolox#easytags#version, bufname, starttime, used_python ? " (using Python)" : "")
return 1
endif
catch
call xolox#misc#msg#warn("easytags.vim %s: %s (at %s)", g:xolox#easytags#version, v:exception, v:throwpoint)
finally
call g:xolox#easytags#highlight_timer.stop()
endtry
endfunction
function! s:is_keyword_compatible(tag)
let name = get(a:tag, 'name', '')
if !empty(name)
" Make sure the tag contains only `keyword characters' (included in the
" &iskeyword option) and is not longer than 80 characters (these
" limitations are documented under :help :syn-keyword).
if name =~ '^\k\+$' && len(name) <= 80
" Make sure the tag doesn't conflict with one of the named options
" accepted by the `:syntax keyword' command (using these named options
" improperly, e.g. without a mandatory argument, will raise an error).
return !has_key(s:invalid_keywords, name)
endif
return 0
endfunction
" These are documented under :help E395, except for "contains" which is not
" documented as being forbidden but when used definitely triggers an error.
let s:invalid_keywords = {
\ 'cchar': 1,
\ 'conceal': 1,
\ 'contained': 1,
\ 'containedin': 1,
\ 'contains': 1,
\ 'nextgroup': 1,
\ 'skipempty': 1,
\ 'skipnl': 1,
\ 'skipwhite': 1,
\ 'transparent': 1,
\ }
" Public supporting functions (might be useful to others). {{{1
function! xolox#easytags#ctags_command() " {{{2
let program = xolox#misc#option#get('easytags_cmd')
if !empty(program)
let options = xolox#misc#option#get('easytags_opts')
if !empty(options)
let command_line = [program]
call extend(command_line, map(copy(options), 'xolox#misc#escape#shell(expand(v:val))'))
let program = join(command_line)
endif
return program
endif
return ''
endfunction
function! xolox#easytags#get_tagsfile() " {{{2
" Get the absolute pathname of the tags file to use. This function
" automatically selects the best choice from the following options (in
" descending order of preference):
"
" 1. Dynamic tags files (see `xolox#easytags#get_dynamic_tagsfile()`).
" 2. File type specific tags files (see `xolox#easytags#get_file_type_specific_tagsfile()`).
" 3. The global tags file (see `xolox#easytags#get_global_tagsfile()`).
"
" Returns the absolute pathname of the selected tags file.
"
" This function is no longer used by the vim-easytags plug-in itself because
" the vim-easytags plug-in needs to differentiate between the different
" types of tags files in every place where it deals with tags files. Because
" this is an externally callable function it is unclear to me if other code
" depends on it, this is the reason why I haven't removed it yet.
let tagsfile = xolox#easytags#get_dynamic_tagsfile()
if empty(tagsfile)
let tagsfile = xolox#easytags#get_file_type_specific_tagsfile()
endif
if empty(tagsfile)
let tagsfile = xolox#easytags#get_global_tagsfile()
endif
return tagsfile
endfunction
function! xolox#easytags#get_dynamic_tagsfile() " {{{2
" Get the pathname of the dynamic tags file to use. If the user configured
" dynamic tags files this function returns the pathname of the applicable
" dynamic tags file (which may not exist yet), otherwise it returns an empty
" string.
let tagsfile = ''
" Look for a suitable project specific tags file?
let dynamic_files = xolox#misc#option#get('easytags_dynamic_files', 0)
if dynamic_files == 1
let tagsfile = get(tagfiles(), 0, '')
elseif dynamic_files == 2
let tagsfile = xolox#misc#option#eval_tags(&tags, 1)
let directory = fnamemodify(tagsfile, ':h')
if filewritable(directory) != 2
" If the directory of the dynamic tags file is not writable, we fall
" back to another type of tags file.
call xolox#misc#msg#warn("easytags.vim %s: Dynamic tags files enabled but %s not writable so falling back.", g:xolox#easytags#version, directory)
let tagsfile = ''
endif
endif
if !empty(tagsfile)
return s:select_tags_file(tagsfile, 'dynamic')
endif
return ''
endfunction
function! xolox#easytags#get_file_type_specific_tagsfile() " {{{2
" Get the pathname of the file type specific tags file to use. If the user
" configured file type specific tags files this function returns the
" pathname of the applicable file type specific tags file (which may not
" exist yet), otherwise it returns an empty string.
let vim_file_type = xolox#easytags#filetypes#canonicalize(&filetype)
if !empty(g:easytags_by_filetype) && !empty(vim_file_type)
let directory = xolox#misc#path#absolute(g:easytags_by_filetype)
let tagsfile = xolox#misc#path#merge(directory, vim_file_type)
if !empty(tagsfile)
return s:select_tags_file(tagsfile, 'file type specific')
endif
endif
return ''
endfunction
function! xolox#easytags#get_global_tagsfile() " {{{2
" Get the pathname of the global tags file. Returns the absolute pathname of
" the global tags file.
let tagsfile = xolox#misc#option#get('easytags_file')
return s:select_tags_file(expand(tagsfile), 'global')
endfunction
function! s:select_tags_file(tagsfile, kind) " {{{2
" If the selected tags file exists, make sure its writable. Also provide the
" user with feedback about the tags file selection process.
if filereadable(a:tagsfile) && filewritable(a:tagsfile) != 1
let message = "The %s tags file %s isn't writable!"
throw printf(message, a:kind, fnamemodify(a:tagsfile, ':~'))
endif
" Provide the user with feedback about the tags file selection process.
call xolox#misc#msg#debug("easytags.vim %s: Selected %s tags file %s.", g:xolox#easytags#version, a:kind, a:tagsfile)
" Canonicalize the tags file's pathname.
return xolox#misc#path#absolute(a:tagsfile)
endfunction
function! xolox#easytags#syntax_groups_to_ignore() " {{{2
" Get a string matching the syntax groups where dynamic highlighting should
" *not* apply. This is complicated by the fact that Vim has a tendency to do
" this:
"
" Vim(syntax):E409: Unknown group name: doxygen.*
"
" This happens when a group wildcard doesn't match *anything*. Why does Vim
" always have to make everything so complicated? :-(
let groups = ['.*String.*', '.*Comment.*']
for group_name in ['cIncluded', 'cCppOut2', 'cCppInElse2', 'cCppOutIf2', 'pythonDocTest', 'pythonDocTest2']
if hlexists(group_name)
call add(groups, group_name)
endif
endfor
" Doxygen is an "add-on syntax script", it's usually used in combination:
" :set syntax=c.doxygen
" It gets special treatment because it defines a dozen or so groups :-)
if hlexists('doxygenComment')
call add(groups, 'doxygen.*')
endif
return join(groups, ',')
endfunction
function! xolox#easytags#async_callback(response) " {{{2
if has_key(a:response, 'result')
call s:report_results(a:response['result'], 1)
else
call xolox#misc#msg#warn("easytags.vim %s: Asynchronous tags file update failed! (%s at %s)", g:xolox#easytags#version, a:response['exception'], a:response['throwpoint'])
endif
endfunction
function! xolox#easytags#session_is_loading() " {{{2
return exists('g:SessionLoad')
endfunction
function! xolox#easytags#disable_automatic_updates() " {{{2
let s:easytags_auto_update_save = xolox#misc#option#get('easytags_auto_update', 1)
let g:easytags_auto_update = 0
endfunction
function! xolox#easytags#restore_automatic_updates() " {{{2
if exists('s:easytags_auto_update_save')
let g:easytags_auto_update = s:easytags_auto_update_save
unlet s:easytags_auto_update_save
endif
endfunction
function! xolox#easytags#why_so_slow() " {{{2
let message = [printf("easytags.vim %s: Timings since you started Vim:", g:xolox#easytags#version)]
call add(message, printf(" - %s seconds updating tags", g:xolox#easytags#update_timer.format()))
call add(message, printf(" - %s seconds highlighting tags", g:xolox#easytags#highlight_timer.format()))
call add(message, printf(" - %s seconds highlighting tags using ':syntax match')", g:xolox#easytags#syntax_match_timer.format()))
call add(message, printf(" - %s seconds highlighting tags using ':syntax keyword')", g:xolox#easytags#syntax_keyword_timer.format()))
call add(message, printf(" - %s seconds filtering tags for highlighting (stage 1)", g:xolox#easytags#syntax_filter_stage_1_timer.format()))
call add(message, printf(" - %s seconds filtering tags for highlighting (stage 2)", g:xolox#easytags#syntax_filter_stage_2_timer.format()))
echo join(message, "\n")
endfunction
" Public API for definition of file type specific dynamic syntax highlighting. {{{1
function! xolox#easytags#define_tagkind(object) " {{{2
if !has_key(a:object, 'pattern_prefix')
let a:object.pattern_prefix = g:xolox#easytags#default_pattern_prefix
endif
if !has_key(a:object, 'pattern_suffix')
let a:object.pattern_suffix = g:xolox#easytags#default_pattern_suffix
endif
if !has_key(s:tagkinds, a:object.filetype)
let s:tagkinds[a:object.filetype] = []
endif
call add(s:tagkinds[a:object.filetype], a:object)
endfunction
" Miscellaneous script-local functions. {{{1
function! s:report_results(response, async) " {{{2
if !xolox#misc#option#get('easytags_suppress_report', 0)
let actions = []
if a:response['num_updated'] > 0
call add(actions, printf('updated %i tags', a:response['num_updated']))
endif
if a:response['num_filtered'] > 0
call add(actions, printf('filtered %i invalid tags', a:response['num_filtered']))
endif
if !empty(actions)
let function = a:async ? 'xolox#misc#msg#debug' : 'xolox#misc#msg#info'
let actions_string = xolox#misc#str#ucfirst(join(actions, ' and '))
let command_type = a:async ? 'asynchronously' : 'synchronously'
call call(function, ["easytags.vim %s: %s in %s (%s).", g:xolox#easytags#version, actions_string, a:response['elapsed_time'], command_type])
endif
endif
endfunction
function! s:python_available() " {{{2
if !exists('s:is_python_available')
try
execute 'pyfile' fnameescape(g:easytags_python_script)
redir => output
silent python easytags_ping()
redir END
let s:is_python_available = (output =~ 'it works!')
catch
let s:is_python_available = 0
endtry
endif
return s:is_python_available
endfunction
function! s:highlight_with_python(syntax_group, tagkind) " {{{2
if xolox#misc#option#get('easytags_python_enabled', 1) && s:python_available()
" Gather arguments for Python function.
let context = {}
let context['tagsfiles'] = tagfiles()
let context['syntaxgroup'] = a:syntax_group
" TODO This doesn't support file type groups!
let context['filetype'] = xolox#easytags#filetypes#to_ctags(xolox#easytags#filetypes#canonicalize(&filetype))
let context['tagkinds'] = get(a:tagkind, 'tagkinds', '')
let context['prefix'] = get(a:tagkind, 'pattern_prefix', '')
let context['suffix'] = get(a:tagkind, 'pattern_suffix', '')
let context['filters'] = get(a:tagkind, 'python_filter', {})
let context['ignoresyntax'] = xolox#easytags#syntax_groups_to_ignore()
" Call the Python function and intercept the output.
try
redir => commands
python import vim
silent python print easytags_gensyncmd(**vim.eval('context'))
redir END
execute commands
return 1
catch
redir END
" If the Python script raised an error, don't run it again.
let g:easytags_python_enabled = 0
endtry
endif
return 0
endfunction
" Built-in file type & tag kind definitions. {{{1
" Don't bother redefining everything below when this script is sourced again.
if exists('s:tagkinds')
finish
endif
let s:tagkinds = {}
" Enable line continuation.
let s:cpo_save = &cpo
set cpo&vim
" Lua. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'lua',
\ 'hlgroup': 'luaFunc',
\ 'tagkinds': 'f'})
" C and C++. {{{2
"
" C and C++ are both treated as C++, for details refer
" to https://github.com/xolox/vim-easytags/issues/91.
call xolox#easytags#define_tagkind({
\ 'filetype': 'cpp',
\ 'hlgroup': 'cType',
\ 'tagkinds': '[cgstu]'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'cpp',
\ 'hlgroup': 'cEnum',
\ 'tagkinds': 'e'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'cpp',
\ 'hlgroup': 'cPreProc',
\ 'tagkinds': 'd'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'cpp',
\ 'hlgroup': 'cFunction',
\ 'tagkinds': '[fp]'})
highlight def link cEnum Identifier
highlight def link cFunction Function
if xolox#misc#option#get('easytags_include_members', 0)
call xolox#easytags#define_tagkind({
\ 'filetype': 'cpp',
\ 'hlgroup': 'cMember',
\ 'tagkinds': 'm'})
highlight def link cMember Identifier
endif
" PHP. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'php',
\ 'hlgroup': 'phpFunctions',
\ 'tagkinds': 'f',
\ 'pattern_suffix': '(\@='})
call xolox#easytags#define_tagkind({
\ 'filetype': 'php',
\ 'hlgroup': 'phpClasses',
\ 'tagkinds': 'c'})
" Vim script. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'vim',
\ 'hlgroup': 'vimAutoGroup',
\ 'tagkinds': 'a'})
highlight def link vimAutoGroup vimAutoEvent
call xolox#easytags#define_tagkind({
\ 'filetype': 'vim',
\ 'hlgroup': 'vimCommand',
\ 'tagkinds': 'c',
\ 'pattern_prefix': '\(\(^\|\s\):\?\)\@<=',
\ 'pattern_suffix': '\(!\?\(\s\|$\)\)\@='})
" Exuberant Ctags doesn't mark script local functions in Vim scripts as
" "static". When your tags file contains search patterns this plug-in can use
" those search patterns to check which Vim script functions are defined
" globally and which script local.
call xolox#easytags#define_tagkind({
\ 'filetype': 'vim',
\ 'hlgroup': 'vimFuncName',
\ 'vim_filter': 'v:val.kind ==# "f" && get(v:val, "cmd", "") !~? ''<sid>\w\|\<s:\w''',
\ 'python_filter': { 'kind': 'f', 'nomatch': '(?i)(<sid>\w|\bs:\w)' },
\ 'pattern_prefix': '\C\%(\<s:\|<[sS][iI][dD]>\)\@<!\<'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'vim',
\ 'hlgroup': 'vimScriptFuncName',
\ 'vim_filter': 'v:val.kind ==# "f" && get(v:val, "cmd", "") =~? ''<sid>\w\|\<s:\w''',
\ 'python_filter': { 'kind': 'f', 'match': '(?i)(<sid>\w|\bs:\w)' },
\ 'pattern_prefix': '\C\%(\<s:\|<[sS][iI][dD]>\)'})
highlight def link vimScriptFuncName vimFuncName
" Python. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'python',
\ 'hlgroup': 'pythonFunction',
\ 'tagkinds': 'f',
\ 'pattern_prefix': '\%(\<def\s\+\)\@<!\<'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'python',
\ 'hlgroup': 'pythonMethod',
\ 'tagkinds': 'm',
\ 'pattern_prefix': '\.\@<='})
call xolox#easytags#define_tagkind({
\ 'filetype': 'python',
\ 'hlgroup': 'pythonClass',
\ 'tagkinds': 'c'})
highlight def link pythonMethodTag pythonFunction
highlight def link pythonClassTag pythonFunction
" Java. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'java',
\ 'hlgroup': 'javaClass',
\ 'tagkinds': 'c'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'java',
\ 'hlgroup': 'javaInterface',
\ 'tagkinds': 'i'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'java',
\ 'hlgroup': 'javaMethod',
\ 'tagkinds': 'm'})
highlight def link javaClass Identifier
highlight def link javaMethod Function
highlight def link javaInterface Identifier
" C#. {{{2
" TODO C# name spaces, interface names, enumeration member names, structure names?
call xolox#easytags#define_tagkind({
\ 'filetype': 'cs',
\ 'hlgroup': 'csClassOrStruct',
\ 'tagkinds': 'c'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'cs',
\ 'hlgroup': 'csMethod',
\ 'tagkinds': '[ms]'})
highlight def link csClassOrStruct Identifier
highlight def link csMethod Function
" Ruby. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'ruby',
\ 'hlgroup': 'rubyModuleName',
\ 'tagkinds': 'm'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'ruby',
\ 'hlgroup': 'rubyClassName',
\ 'tagkinds': 'c'})
call xolox#easytags#define_tagkind({
\ 'filetype': 'ruby',
\ 'hlgroup': 'rubyMethodName',
\ 'tagkinds': '[fF]'})
highlight def link rubyModuleName Type
highlight def link rubyClassName Type
highlight def link rubyMethodName Function
" Awk. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'awk',
\ 'hlgroup': 'awkFunctionTag',
\ 'tagkinds': 'f'})
highlight def link awkFunctionTag Function
" Shell. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'sh',
\ 'hlgroup': 'shFunctionTag',
\ 'tagkinds': 'f',
\ 'pattern_suffix': '\(\w\|\s*()\)\@!'})
highlight def link shFunctionTag Operator
" Tcl. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'tcl',
\ 'hlgroup': 'tclCommandTag',
\ 'tagkinds': 'p'})
highlight def link tclCommandTag Operator
" Perl. {{{2
call xolox#easytags#define_tagkind({
\ 'filetype': 'perl',
\ 'hlgroup': 'perlFunctionTag',
\ 'tagkinds': '[s]',
\ 'pattern_prefix': '\%(\<sub\s\+\)\@<!\%(>\|\s\|&\|^\)\@<=\<'})
highlight def link perlFunctionTag Operator
" }}}
" Restore "cpoptions".
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: ts=2 sw=2 et
|