comparison runtime/autoload/filetype.vim @ 12808:2ff38c2addd4 v8.0.1281

patch 8.0.1281: loading file type detection slows down startup commit https://github.com/vim/vim/commit/851ee6c3da5fd726d92e1e3300d7e5e2e8b907c5 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Nov 9 20:46:17 2017 +0100 patch 8.0.1281: loading file type detection slows down startup Problem: Loading file type detection slows down startup. Solution: Move functions to an autoload script.
author Christian Brabandt <cb@256bit.org>
date Thu, 09 Nov 2017 21:00:05 +0100
parents
children e0dea10b30b6
comparison
equal deleted inserted replaced
12807:504ec77bef23 12808:2ff38c2addd4
1 " Vim functions for file type detection
2 "
3 " Maintainer: Bram Moolenaar <Bram@vim.org>
4 " Last Change: 2017 Nov 09
5
6 " These functions are moved here from runtime/filetype.vim to make startup
7 " faster.
8
9 " Line continuation is used here, remove 'C' from 'cpoptions'
10 let s:cpo_save = &cpo
11 set cpo&vim
12
13 func filetype#Check_inp()
14 if getline(1) =~ '^\*'
15 setf abaqus
16 else
17 let n = 1
18 if line("$") > 500
19 let nmax = 500
20 else
21 let nmax = line("$")
22 endif
23 while n <= nmax
24 if getline(n) =~? "^header surface data"
25 setf trasys
26 break
27 endif
28 let n = n + 1
29 endwhile
30 endif
31 endfunc
32
33 " This function checks for the kind of assembly that is wanted by the user, or
34 " can be detected from the first five lines of the file.
35 func filetype#FTasm()
36 " make sure b:asmsyntax exists
37 if !exists("b:asmsyntax")
38 let b:asmsyntax = ""
39 endif
40
41 if b:asmsyntax == ""
42 call filetype#FTasmsyntax()
43 endif
44
45 " if b:asmsyntax still isn't set, default to asmsyntax or GNU
46 if b:asmsyntax == ""
47 if exists("g:asmsyntax")
48 let b:asmsyntax = g:asmsyntax
49 else
50 let b:asmsyntax = "asm"
51 endif
52 endif
53
54 exe "setf " . fnameescape(b:asmsyntax)
55 endfunc
56
57 func filetype#FTasmsyntax()
58 " see if file contains any asmsyntax=foo overrides. If so, change
59 " b:asmsyntax appropriately
60 let head = " ".getline(1)." ".getline(2)." ".getline(3)." ".getline(4).
61 \" ".getline(5)." "
62 let match = matchstr(head, '\sasmsyntax=\zs[a-zA-Z0-9]\+\ze\s')
63 if match != ''
64 let b:asmsyntax = match
65 elseif ((head =~? '\.title') || (head =~? '\.ident') || (head =~? '\.macro') || (head =~? '\.subtitle') || (head =~? '\.library'))
66 let b:asmsyntax = "vmasm"
67 endif
68 endfunc
69
70 " Check if one of the first five lines contains "VB_Name". In that case it is
71 " probably a Visual Basic file. Otherwise it's assumed to be "alt" filetype.
72 func filetype#FTVB(alt)
73 if getline(1).getline(2).getline(3).getline(4).getline(5) =~? 'VB_Name\|Begin VB\.\(Form\|MDIForm\|UserControl\)'
74 setf vb
75 else
76 exe "setf " . a:alt
77 endif
78 endfunc
79
80 func filetype#FTbtm()
81 if exists("g:dosbatch_syntax_for_btm") && g:dosbatch_syntax_for_btm
82 setf dosbatch
83 else
84 setf btm
85 endif
86 endfunc
87
88 func filetype#BindzoneCheck(default)
89 if getline(1).getline(2).getline(3).getline(4) =~ '^; <<>> DiG [0-9.]\+.* <<>>\|$ORIGIN\|$TTL\|IN\s\+SOA'
90 setf bindzone
91 elseif a:default != ''
92 exe 'setf ' . a:default
93 endif
94 endfunc
95
96 func filetype#FTlpc()
97 if exists("g:lpc_syntax_for_c")
98 let lnum = 1
99 while lnum <= 12
100 if getline(lnum) =~# '^\(//\|inherit\|private\|protected\|nosave\|string\|object\|mapping\|mixed\)'
101 setf lpc
102 return
103 endif
104 let lnum = lnum + 1
105 endwhile
106 endif
107 setf c
108 endfunc
109
110 func filetype#FTheader()
111 if match(getline(1, min([line("$"), 200])), '^@\(interface\|end\|class\)') > -1
112 if exists("g:c_syntax_for_h")
113 setf objc
114 else
115 setf objcpp
116 endif
117 elseif exists("g:c_syntax_for_h")
118 setf c
119 elseif exists("g:ch_syntax_for_h")
120 setf ch
121 else
122 setf cpp
123 endif
124 endfunc
125
126 " This function checks if one of the first ten lines start with a '@'. In
127 " that case it is probably a change file.
128 " If the first line starts with # or ! it's probably a ch file.
129 " If a line has "main", "include", "//" ir "/*" it's probably ch.
130 " Otherwise CHILL is assumed.
131 func filetype#FTchange()
132 let lnum = 1
133 while lnum <= 10
134 if getline(lnum)[0] == '@'
135 setf change
136 return
137 endif
138 if lnum == 1 && (getline(1)[0] == '#' || getline(1)[0] == '!')
139 setf ch
140 return
141 endif
142 if getline(lnum) =~ "MODULE"
143 setf chill
144 return
145 endif
146 if getline(lnum) =~ 'main\s*(\|#\s*include\|//'
147 setf ch
148 return
149 endif
150 let lnum = lnum + 1
151 endwhile
152 setf chill
153 endfunc
154
155 func filetype#FTent()
156 " This function checks for valid cl syntax in the first five lines.
157 " Look for either an opening comment, '#', or a block start, '{".
158 " If not found, assume SGML.
159 let lnum = 1
160 while lnum < 6
161 let line = getline(lnum)
162 if line =~ '^\s*[#{]'
163 setf cl
164 return
165 elseif line !~ '^\s*$'
166 " Not a blank line, not a comment, and not a block start,
167 " so doesn't look like valid cl code.
168 break
169 endif
170 let lnum = lnum + 1
171 endw
172 setf dtd
173 endfunc
174
175 func filetype#EuphoriaCheck()
176 if exists('g:filetype_euphoria')
177 exe 'setf ' . g:filetype_euphoria
178 else
179 setf euphoria3
180 endif
181 endfunc
182
183 func filetype#DtraceCheck()
184 let lines = getline(1, min([line("$"), 100]))
185 if match(lines, '^module\>\|^import\>') > -1
186 " D files often start with a module and/or import statement.
187 setf d
188 elseif match(lines, '^#!\S\+dtrace\|#pragma\s\+D\s\+option\|:\S\{-}:\S\{-}:') > -1
189 setf dtrace
190 else
191 setf d
192 endif
193 endfunc
194
195 func filetype#FTe()
196 if exists('g:filetype_euphoria')
197 exe 'setf ' . g:filetype_euphoria
198 else
199 let n = 1
200 while n < 100 && n < line("$")
201 if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$"
202 setf specman
203 return
204 endif
205 let n = n + 1
206 endwhile
207 setf eiffel
208 endif
209 endfunc
210
211 " Distinguish between HTML, XHTML and Django
212 func filetype#FThtml()
213 let n = 1
214 while n < 10 && n < line("$")
215 if getline(n) =~ '\<DTD\s\+XHTML\s'
216 setf xhtml
217 return
218 endif
219 if getline(n) =~ '{%\s*\(extends\|block\|load\)\>\|{#\s\+'
220 setf htmldjango
221 return
222 endif
223 let n = n + 1
224 endwhile
225 setf html
226 endfunc
227
228 " Distinguish between standard IDL and MS-IDL
229 func filetype#FTidl()
230 let n = 1
231 while n < 50 && n < line("$")
232 if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"'
233 setf msidl
234 return
235 endif
236 let n = n + 1
237 endwhile
238 setf idl
239 endfunc
240
241 " Distinguish between "default" and Cproto prototype file. */
242 func filetype#ProtoCheck(default)
243 " Cproto files have a comment in the first line and a function prototype in
244 " the second line, it always ends in ";". Indent files may also have
245 " comments, thus we can't match comments to see the difference.
246 " IDL files can have a single ';' in the second line, require at least one
247 " chacter before the ';'.
248 if getline(2) =~ '.;$'
249 setf cpp
250 else
251 exe 'setf ' . a:default
252 endif
253 endfunc
254
255 func filetype#FTm()
256 let n = 1
257 let saw_comment = 0 " Whether we've seen a multiline comment leader.
258 while n < 100
259 let line = getline(n)
260 if line =~ '^\s*/\*'
261 " /* ... */ is a comment in Objective C and Murphi, so we can't conclude
262 " it's either of them yet, but track this as a hint in case we don't see
263 " anything more definitive.
264 let saw_comment = 1
265 endif
266 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|//\)'
267 setf objc
268 return
269 endif
270 if line =~ '^\s*%'
271 setf matlab
272 return
273 endif
274 if line =~ '^\s*(\*'
275 setf mma
276 return
277 endif
278 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
279 setf murphi
280 return
281 endif
282 let n = n + 1
283 endwhile
284
285 if saw_comment
286 " We didn't see anything definitive, but this looks like either Objective C
287 " or Murphi based on the comment leader. Assume the former as it is more
288 " common.
289 setf objc
290 elseif exists("g:filetype_m")
291 " Use user specified default filetype for .m
292 exe "setf " . g:filetype_m
293 else
294 " Default is matlab
295 setf matlab
296 endif
297 endfunc
298
299 func filetype#FTmms()
300 let n = 1
301 while n < 10
302 let line = getline(n)
303 if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
304 setf mmix
305 return
306 endif
307 if line =~ '^\s*#'
308 setf make
309 return
310 endif
311 let n = n + 1
312 endwhile
313 setf mmix
314 endfunc
315
316 " This function checks if one of the first five lines start with a dot. In
317 " that case it is probably an nroff file: 'filetype' is set and 1 is returned.
318 func filetype#FTnroff()
319 if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.'
320 setf nroff
321 return 1
322 endif
323 return 0
324 endfunc
325
326 func filetype#FTmm()
327 let n = 1
328 while n < 10
329 let line = getline(n)
330 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
331 setf objcpp
332 return
333 endif
334 let n = n + 1
335 endwhile
336 setf nroff
337 endfunc
338
339 func filetype#FTpl()
340 if exists("g:filetype_pl")
341 exe "setf " . g:filetype_pl
342 else
343 " recognize Prolog by specific text in the first non-empty line
344 " require a blank after the '%' because Perl uses "%list" and "%translate"
345 let l = getline(nextnonblank(1))
346 if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
347 setf prolog
348 else
349 setf perl
350 endif
351 endif
352 endfunc
353
354 func filetype#FTinc()
355 if exists("g:filetype_inc")
356 exe "setf " . g:filetype_inc
357 else
358 let lines = getline(1).getline(2).getline(3)
359 if lines =~? "perlscript"
360 setf aspperl
361 elseif lines =~ "<%"
362 setf aspvbs
363 elseif lines =~ "<?"
364 setf php
365 else
366 call filetype#FTasmsyntax()
367 if exists("b:asmsyntax")
368 exe "setf " . fnameescape(b:asmsyntax)
369 else
370 setf pov
371 endif
372 endif
373 endif
374 endfunc
375
376 func filetype#FTprogress_cweb()
377 if exists("g:filetype_w")
378 exe "setf " . g:filetype_w
379 return
380 endif
381 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
382 setf progress
383 else
384 setf cweb
385 endif
386 endfunc
387
388 func filetype#FTprogress_asm()
389 if exists("g:filetype_i")
390 exe "setf " . g:filetype_i
391 return
392 endif
393 " This function checks for an assembly comment the first ten lines.
394 " If not found, assume Progress.
395 let lnum = 1
396 while lnum <= 10 && lnum < line('$')
397 let line = getline(lnum)
398 if line =~ '^\s*;' || line =~ '^\*'
399 call filetype#FTasm()
400 return
401 elseif line !~ '^\s*$' || line =~ '^/\*'
402 " Not an empty line: Doesn't look like valid assembly code.
403 " Or it looks like a Progress /* comment
404 break
405 endif
406 let lnum = lnum + 1
407 endw
408 setf progress
409 endfunc
410
411 func filetype#FTprogress_pascal()
412 if exists("g:filetype_p")
413 exe "setf " . g:filetype_p
414 return
415 endif
416 " This function checks for valid Pascal syntax in the first ten lines.
417 " Look for either an opening comment or a program start.
418 " If not found, assume Progress.
419 let lnum = 1
420 while lnum <= 10 && lnum < line('$')
421 let line = getline(lnum)
422 if line =~ '^\s*\(program\|unit\|procedure\|function\|const\|type\|var\)\>'
423 \ || line =~ '^\s*{' || line =~ '^\s*(\*'
424 setf pascal
425 return
426 elseif line !~ '^\s*$' || line =~ '^/\*'
427 " Not an empty line: Doesn't look like valid Pascal code.
428 " Or it looks like a Progress /* comment
429 break
430 endif
431 let lnum = lnum + 1
432 endw
433 setf progress
434 endfunc
435
436 func filetype#FTr()
437 let max = line("$") > 50 ? 50 : line("$")
438
439 for n in range(1, max)
440 " Rebol is easy to recognize, check for that first
441 if getline(n) =~? '\<REBOL\>'
442 setf rebol
443 return
444 endif
445 endfor
446
447 for n in range(1, max)
448 " R has # comments
449 if getline(n) =~ '^\s*#'
450 setf r
451 return
452 endif
453 " Rexx has /* comments */
454 if getline(n) =~ '^\s*/\*'
455 setf rexx
456 return
457 endif
458 endfor
459
460 " Nothing recognized, use user default or assume Rexx
461 if exists("g:filetype_r")
462 exe "setf " . g:filetype_r
463 else
464 " Rexx used to be the default, but R appears to be much more popular.
465 setf r
466 endif
467 endfunc
468
469 func filetype#McSetf()
470 " Rely on the file to start with a comment.
471 " MS message text files use ';', Sendmail files use '#' or 'dnl'
472 for lnum in range(1, min([line("$"), 20]))
473 let line = getline(lnum)
474 if line =~ '^\s*\(#\|dnl\)'
475 setf m4 " Sendmail .mc file
476 return
477 elseif line =~ '^\s*;'
478 setf msmessages " MS Message text file
479 return
480 endif
481 endfor
482 setf m4 " Default: Sendmail .mc file
483 endfunc
484
485 " Called from filetype.vim and scripts.vim.
486 func filetype#SetFileTypeSH(name)
487 if expand("<amatch>") =~ g:ft_ignore_pat
488 return
489 endif
490 if a:name =~ '\<csh\>'
491 " Some .sh scripts contain #!/bin/csh.
492 call filetype#SetFileTypeShell("csh")
493 return
494 elseif a:name =~ '\<tcsh\>'
495 " Some .sh scripts contain #!/bin/tcsh.
496 call filetype#SetFileTypeShell("tcsh")
497 return
498 elseif a:name =~ '\<zsh\>'
499 " Some .sh scripts contain #!/bin/zsh.
500 call filetype#SetFileTypeShell("zsh")
501 return
502 elseif a:name =~ '\<ksh\>'
503 let b:is_kornshell = 1
504 if exists("b:is_bash")
505 unlet b:is_bash
506 endif
507 if exists("b:is_sh")
508 unlet b:is_sh
509 endif
510 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
511 let b:is_bash = 1
512 if exists("b:is_kornshell")
513 unlet b:is_kornshell
514 endif
515 if exists("b:is_sh")
516 unlet b:is_sh
517 endif
518 elseif a:name =~ '\<sh\>'
519 let b:is_sh = 1
520 if exists("b:is_kornshell")
521 unlet b:is_kornshell
522 endif
523 if exists("b:is_bash")
524 unlet b:is_bash
525 endif
526 endif
527 call filetype#SetFileTypeShell("sh")
528 endfunc
529
530 " For shell-like file types, check for an "exec" command hidden in a comment,
531 " as used for Tcl.
532 " Also called from scripts.vim, thus can't be local to this script.
533 func filetype#SetFileTypeShell(name)
534 if expand("<amatch>") =~ g:ft_ignore_pat
535 return
536 endif
537 let l = 2
538 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
539 " Skip empty and comment lines.
540 let l = l + 1
541 endwhile
542 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
543 " Found an "exec" line after a comment with continuation
544 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
545 if n =~ '\<tclsh\|\<wish'
546 setf tcl
547 return
548 endif
549 endif
550 exe "setf " . a:name
551 endfunc
552
553 func filetype#CSH()
554 if exists("g:filetype_csh")
555 call filetype#SetFileTypeShell(g:filetype_csh)
556 elseif &shell =~ "tcsh"
557 call filetype#SetFileTypeShell("tcsh")
558 else
559 call filetype#SetFileTypeShell("csh")
560 endif
561 endfunc
562
563 func filetype#FTRules()
564 let path = expand('<amatch>:p')
565 if path =~ '^/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
566 setf udevrules
567 return
568 endif
569 if path =~ '^/etc/ufw/'
570 setf conf " Better than hog
571 return
572 endif
573 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
574 setf javascript
575 return
576 endif
577 try
578 let config_lines = readfile('/etc/udev/udev.conf')
579 catch /^Vim\%((\a\+)\)\=:E484/
580 setf hog
581 return
582 endtry
583 let dir = expand('<amatch>:p:h')
584 for line in config_lines
585 if line =~ s:ft_rules_udev_rules_pattern
586 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
587 if dir == udev_rules
588 setf udevrules
589 endif
590 break
591 endif
592 endfor
593 setf hog
594 endfunc
595
596 func filetype#SQL()
597 if exists("g:filetype_sql")
598 exe "setf " . g:filetype_sql
599 else
600 setf sql
601 endif
602 endfunc
603
604 " If the file has an extension of 't' and is in a directory 't' or 'xt' then
605 " it is almost certainly a Perl test file.
606 " If the first line starts with '#' and contains 'perl' it's probably a Perl
607 " file.
608 " (Slow test) If a file contains a 'use' statement then it is almost certainly
609 " a Perl file.
610 func filetype#FTperl()
611 let dirname = expand("%:p:h:t")
612 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
613 setf perl
614 return 1
615 endif
616 if getline(1)[0] == '#' && getline(1) =~ 'perl'
617 setf perl
618 return 1
619 endif
620 if search('^use\s\s*\k', 'nc', 30)
621 setf perl
622 return 1
623 endif
624 return 0
625 endfunc
626
627 " Choose context, plaintex, or tex (LaTeX) based on these rules:
628 " 1. Check the first line of the file for "%&<format>".
629 " 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
630 " 3. Default to "latex" or to g:tex_flavor, can be set in user's vimrc.
631 func filetype#FTtex()
632 let firstline = getline(1)
633 if firstline =~ '^%&\s*\a\+'
634 let format = tolower(matchstr(firstline, '\a\+'))
635 let format = substitute(format, 'pdf', '', '')
636 if format == 'tex'
637 let format = 'latex'
638 elseif format == 'plaintex'
639 let format = 'plain'
640 endif
641 elseif expand('%') =~ 'tex/context/.*/.*.tex'
642 let format = 'context'
643 else
644 " Default value, may be changed later:
645 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
646 " Save position, go to the top of the file, find first non-comment line.
647 let save_cursor = getpos('.')
648 call cursor(1,1)
649 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
650 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
651 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
652 let cpat = 'start\a\+\|setup\a\+\|usemodule\|enablemode\|enableregime\|setvariables\|useencoding\|usesymbols\|stelle\a\+\|verwende\a\+\|stel\a\+\|gebruik\a\+\|usa\a\+\|imposta\a\+\|regle\a\+\|utilisemodule\>'
653 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
654 \ 'cnp', firstNC + 1000)
655 if kwline == 1 " lpat matched
656 let format = 'latex'
657 elseif kwline == 2 " cpat matched
658 let format = 'context'
659 endif " If neither matched, keep default set above.
660 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
661 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
662 " if cline > 0
663 " let format = 'context'
664 " endif
665 " if lline > 0 && (cline == 0 || cline > lline)
666 " let format = 'tex'
667 " endif
668 endif " firstNC
669 call setpos('.', save_cursor)
670 endif " firstline =~ '^%&\s*\a\+'
671
672 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
673 if format == 'plain'
674 setf plaintex
675 elseif format == 'context'
676 setf context
677 else " probably LaTeX
678 setf tex
679 endif
680 return
681 endfunc
682
683 func filetype#FTxml()
684 let n = 1
685 while n < 100 && n < line("$")
686 let line = getline(n)
687 " DocBook 4 or DocBook 5.
688 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
689 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
690 if is_docbook4 || is_docbook5
691 let b:docbk_type = "xml"
692 if is_docbook5
693 let b:docbk_ver = 5
694 else
695 let b:docbk_ver = 4
696 endif
697 setf docbk
698 return
699 endif
700 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
701 setf xbl
702 return
703 endif
704 let n += 1
705 endwhile
706 setf xml
707 endfunc
708
709 func filetype#FTy()
710 let n = 1
711 while n < 100 && n < line("$")
712 let line = getline(n)
713 if line =~ '^\s*%'
714 setf yacc
715 return
716 endif
717 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
718 setf racc
719 return
720 endif
721 let n = n + 1
722 endwhile
723 setf yacc
724 endfunc
725
726 func filetype#Redif()
727 let lnum = 1
728 while lnum <= 5 && lnum < line('$')
729 if getline(lnum) =~ "^\ctemplate-type:"
730 setf redif
731 return
732 endif
733 let lnum = lnum + 1
734 endwhile
735 endfunc
736
737
738 " Restore 'cpoptions'
739 let &cpo = s:cpo_save
740 unlet s:cpo_save