blob: 1ac74b57854ec4dd1af169cd401ad48ad70b0aee [file] [log] [blame]
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001" Vim functions for file type detection
2"
3" Maintainer: Bram Moolenaar <Bram@vim.org>
Bram Moolenaar207f0092020-08-30 17:20:20 +02004" Last Change: 2020 Aug 17
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01005
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'
10let s:cpo_save = &cpo
11set cpo&vim
12
Bram Moolenaard09a2062017-11-11 15:37:45 +010013func dist#ft#Check_inp()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010014 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
31endfunc
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.
Bram Moolenaard09a2062017-11-11 15:37:45 +010035func dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010036 " make sure b:asmsyntax exists
37 if !exists("b:asmsyntax")
38 let b:asmsyntax = ""
39 endif
40
41 if b:asmsyntax == ""
Bram Moolenaard09a2062017-11-11 15:37:45 +010042 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010043 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)
55endfunc
56
Bram Moolenaard09a2062017-11-11 15:37:45 +010057func dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010058 " 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
68endfunc
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.
Bram Moolenaard09a2062017-11-11 15:37:45 +010072func dist#ft#FTVB(alt)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010073 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
78endfunc
79
Bram Moolenaard09a2062017-11-11 15:37:45 +010080func dist#ft#FTbtm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010081 if exists("g:dosbatch_syntax_for_btm") && g:dosbatch_syntax_for_btm
82 setf dosbatch
83 else
84 setf btm
85 endif
86endfunc
87
Bram Moolenaard09a2062017-11-11 15:37:45 +010088func dist#ft#BindzoneCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010089 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
94endfunc
95
Bram Moolenaard09a2062017-11-11 15:37:45 +010096func dist#ft#FTlpc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010097 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
108endfunc
109
Bram Moolenaard09a2062017-11-11 15:37:45 +0100110func dist#ft#FTheader()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100111 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
124endfunc
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.
Bram Moolenaarba3ff532018-11-04 14:45:49 +0100129" If a line has "main", "include", "//" or "/*" it's probably ch.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100130" Otherwise CHILL is assumed.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100131func dist#ft#FTchange()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100132 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
153endfunc
154
Bram Moolenaard09a2062017-11-11 15:37:45 +0100155func dist#ft#FTent()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100156 " 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
173endfunc
174
Bram Moolenaard09a2062017-11-11 15:37:45 +0100175func dist#ft#EuphoriaCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100176 if exists('g:filetype_euphoria')
177 exe 'setf ' . g:filetype_euphoria
178 else
179 setf euphoria3
180 endif
181endfunc
182
Bram Moolenaard09a2062017-11-11 15:37:45 +0100183func dist#ft#DtraceCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100184 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
193endfunc
194
Bram Moolenaard09a2062017-11-11 15:37:45 +0100195func dist#ft#FTe()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100196 if exists('g:filetype_euphoria')
197 exe 'setf ' . g:filetype_euphoria
198 else
199 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100200 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100201 if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$"
202 setf specman
203 return
204 endif
205 let n = n + 1
206 endwhile
207 setf eiffel
208 endif
209endfunc
210
211" Distinguish between HTML, XHTML and Django
Bram Moolenaard09a2062017-11-11 15:37:45 +0100212func dist#ft#FThtml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100213 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100214 while n < 10 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100215 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
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100225 setf FALLBACK html
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100226endfunc
227
228" Distinguish between standard IDL and MS-IDL
Bram Moolenaard09a2062017-11-11 15:37:45 +0100229func dist#ft#FTidl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100230 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100231 while n < 50 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100232 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
239endfunc
240
241" Distinguish between "default" and Cproto prototype file. */
Bram Moolenaard09a2062017-11-11 15:37:45 +0100242func dist#ft#ProtoCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100243 " 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
253endfunc
254
Bram Moolenaard09a2062017-11-11 15:37:45 +0100255func dist#ft#FTm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100256 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
297endfunc
298
Bram Moolenaard09a2062017-11-11 15:37:45 +0100299func dist#ft#FTmms()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100300 let n = 1
Bram Moolenaard7df2792020-01-02 21:34:42 +0100301 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100302 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
314endfunc
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.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100318func dist#ft#FTnroff()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100319 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
324endfunc
325
Bram Moolenaard09a2062017-11-11 15:37:45 +0100326func dist#ft#FTmm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100327 let n = 1
Bram Moolenaard1caa942020-04-10 22:10:56 +0200328 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100329 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
337endfunc
338
Bram Moolenaard09a2062017-11-11 15:37:45 +0100339func dist#ft#FTpl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100340 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
352endfunc
353
Bram Moolenaard09a2062017-11-11 15:37:45 +0100354func dist#ft#FTinc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100355 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
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100365 " Pascal supports // comments but they're vary rarely used for file
366 " headers so assume POV-Ray
367 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
368 setf pascal
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100369 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100370 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100371 if exists("b:asmsyntax")
372 exe "setf " . fnameescape(b:asmsyntax)
373 else
374 setf pov
375 endif
376 endif
377 endif
378endfunc
379
Bram Moolenaard09a2062017-11-11 15:37:45 +0100380func dist#ft#FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100381 if exists("g:filetype_w")
382 exe "setf " . g:filetype_w
383 return
384 endif
385 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
386 setf progress
387 else
388 setf cweb
389 endif
390endfunc
391
Bram Moolenaard09a2062017-11-11 15:37:45 +0100392func dist#ft#FTprogress_asm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100393 if exists("g:filetype_i")
394 exe "setf " . g:filetype_i
395 return
396 endif
397 " This function checks for an assembly comment the first ten lines.
398 " If not found, assume Progress.
399 let lnum = 1
400 while lnum <= 10 && lnum < line('$')
401 let line = getline(lnum)
402 if line =~ '^\s*;' || line =~ '^\*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100403 call dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100404 return
405 elseif line !~ '^\s*$' || line =~ '^/\*'
406 " Not an empty line: Doesn't look like valid assembly code.
407 " Or it looks like a Progress /* comment
408 break
409 endif
410 let lnum = lnum + 1
411 endw
412 setf progress
413endfunc
414
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100415let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
416let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
417
Bram Moolenaard09a2062017-11-11 15:37:45 +0100418func dist#ft#FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100419 if exists("g:filetype_p")
420 exe "setf " . g:filetype_p
421 return
422 endif
423 " This function checks for valid Pascal syntax in the first ten lines.
424 " Look for either an opening comment or a program start.
425 " If not found, assume Progress.
426 let lnum = 1
427 while lnum <= 10 && lnum < line('$')
428 let line = getline(lnum)
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100429 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100430 setf pascal
431 return
432 elseif line !~ '^\s*$' || line =~ '^/\*'
433 " Not an empty line: Doesn't look like valid Pascal code.
434 " Or it looks like a Progress /* comment
435 break
436 endif
437 let lnum = lnum + 1
438 endw
439 setf progress
440endfunc
441
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100442func dist#ft#FTpp()
443 if exists("g:filetype_pp")
444 exe "setf " . g:filetype_pp
445 else
446 let line = getline(nextnonblank(1))
447 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
448 setf pascal
449 else
450 setf puppet
451 endif
452 endif
453endfunc
454
Bram Moolenaard09a2062017-11-11 15:37:45 +0100455func dist#ft#FTr()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100456 let max = line("$") > 50 ? 50 : line("$")
457
458 for n in range(1, max)
459 " Rebol is easy to recognize, check for that first
460 if getline(n) =~? '\<REBOL\>'
461 setf rebol
462 return
463 endif
464 endfor
465
466 for n in range(1, max)
467 " R has # comments
468 if getline(n) =~ '^\s*#'
469 setf r
470 return
471 endif
472 " Rexx has /* comments */
473 if getline(n) =~ '^\s*/\*'
474 setf rexx
475 return
476 endif
477 endfor
478
479 " Nothing recognized, use user default or assume Rexx
480 if exists("g:filetype_r")
481 exe "setf " . g:filetype_r
482 else
483 " Rexx used to be the default, but R appears to be much more popular.
484 setf r
485 endif
486endfunc
487
Bram Moolenaard09a2062017-11-11 15:37:45 +0100488func dist#ft#McSetf()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100489 " Rely on the file to start with a comment.
490 " MS message text files use ';', Sendmail files use '#' or 'dnl'
491 for lnum in range(1, min([line("$"), 20]))
492 let line = getline(lnum)
493 if line =~ '^\s*\(#\|dnl\)'
494 setf m4 " Sendmail .mc file
495 return
496 elseif line =~ '^\s*;'
497 setf msmessages " MS Message text file
498 return
499 endif
500 endfor
501 setf m4 " Default: Sendmail .mc file
502endfunc
503
504" Called from filetype.vim and scripts.vim.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100505func dist#ft#SetFileTypeSH(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100506 if did_filetype()
507 " Filetype was already detected
508 return
509 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100510 if expand("<amatch>") =~ g:ft_ignore_pat
511 return
512 endif
513 if a:name =~ '\<csh\>'
514 " Some .sh scripts contain #!/bin/csh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100515 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100516 return
517 elseif a:name =~ '\<tcsh\>'
518 " Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100519 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100520 return
521 elseif a:name =~ '\<zsh\>'
522 " Some .sh scripts contain #!/bin/zsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100523 call dist#ft#SetFileTypeShell("zsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100524 return
525 elseif a:name =~ '\<ksh\>'
526 let b:is_kornshell = 1
527 if exists("b:is_bash")
528 unlet b:is_bash
529 endif
530 if exists("b:is_sh")
531 unlet b:is_sh
532 endif
533 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
534 let b:is_bash = 1
535 if exists("b:is_kornshell")
536 unlet b:is_kornshell
537 endif
538 if exists("b:is_sh")
539 unlet b:is_sh
540 endif
541 elseif a:name =~ '\<sh\>'
542 let b:is_sh = 1
543 if exists("b:is_kornshell")
544 unlet b:is_kornshell
545 endif
546 if exists("b:is_bash")
547 unlet b:is_bash
548 endif
549 endif
Bram Moolenaard09a2062017-11-11 15:37:45 +0100550 call dist#ft#SetFileTypeShell("sh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100551endfunc
552
553" For shell-like file types, check for an "exec" command hidden in a comment,
554" as used for Tcl.
555" Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100556func dist#ft#SetFileTypeShell(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100557 if did_filetype()
558 " Filetype was already detected
559 return
560 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100561 if expand("<amatch>") =~ g:ft_ignore_pat
562 return
563 endif
564 let l = 2
565 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
566 " Skip empty and comment lines.
567 let l = l + 1
568 endwhile
569 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
570 " Found an "exec" line after a comment with continuation
571 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
572 if n =~ '\<tclsh\|\<wish'
573 setf tcl
574 return
575 endif
576 endif
577 exe "setf " . a:name
578endfunc
579
Bram Moolenaard09a2062017-11-11 15:37:45 +0100580func dist#ft#CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100581 if did_filetype()
582 " Filetype was already detected
583 return
584 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100585 if exists("g:filetype_csh")
Bram Moolenaard09a2062017-11-11 15:37:45 +0100586 call dist#ft#SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100587 elseif &shell =~ "tcsh"
Bram Moolenaard09a2062017-11-11 15:37:45 +0100588 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100589 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100590 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100591 endif
592endfunc
593
Bram Moolenaarcef73222017-11-09 21:05:31 +0100594let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100595func dist#ft#FTRules()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100596 let path = expand('<amatch>:p')
Bram Moolenaaraa9675a2020-08-17 21:57:09 +0200597 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100598 setf udevrules
599 return
600 endif
601 if path =~ '^/etc/ufw/'
602 setf conf " Better than hog
603 return
604 endif
605 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
606 setf javascript
607 return
608 endif
609 try
610 let config_lines = readfile('/etc/udev/udev.conf')
611 catch /^Vim\%((\a\+)\)\=:E484/
612 setf hog
613 return
614 endtry
615 let dir = expand('<amatch>:p:h')
616 for line in config_lines
617 if line =~ s:ft_rules_udev_rules_pattern
618 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
619 if dir == udev_rules
620 setf udevrules
621 endif
622 break
623 endif
624 endfor
625 setf hog
626endfunc
627
Bram Moolenaard09a2062017-11-11 15:37:45 +0100628func dist#ft#SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100629 if exists("g:filetype_sql")
630 exe "setf " . g:filetype_sql
631 else
632 setf sql
633 endif
634endfunc
635
636" If the file has an extension of 't' and is in a directory 't' or 'xt' then
637" it is almost certainly a Perl test file.
638" If the first line starts with '#' and contains 'perl' it's probably a Perl
639" file.
640" (Slow test) If a file contains a 'use' statement then it is almost certainly
641" a Perl file.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100642func dist#ft#FTperl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100643 let dirname = expand("%:p:h:t")
644 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
645 setf perl
646 return 1
647 endif
648 if getline(1)[0] == '#' && getline(1) =~ 'perl'
649 setf perl
650 return 1
651 endif
Bram Moolenaarf0b03c42017-12-17 17:17:07 +0100652 let save_cursor = getpos('.')
653 call cursor(1,1)
654 let has_use = search('^use\s\s*\k', 'c', 30)
655 call setpos('.', save_cursor)
656 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100657 setf perl
658 return 1
659 endif
660 return 0
661endfunc
662
663" Choose context, plaintex, or tex (LaTeX) based on these rules:
664" 1. Check the first line of the file for "%&<format>".
665" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200666" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100667func dist#ft#FTtex()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100668 let firstline = getline(1)
669 if firstline =~ '^%&\s*\a\+'
670 let format = tolower(matchstr(firstline, '\a\+'))
671 let format = substitute(format, 'pdf', '', '')
672 if format == 'tex'
673 let format = 'latex'
674 elseif format == 'plaintex'
675 let format = 'plain'
676 endif
677 elseif expand('%') =~ 'tex/context/.*/.*.tex'
678 let format = 'context'
679 else
680 " Default value, may be changed later:
681 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
682 " Save position, go to the top of the file, find first non-comment line.
683 let save_cursor = getpos('.')
684 call cursor(1,1)
685 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
686 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
687 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
688 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\>'
689 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
690 \ 'cnp', firstNC + 1000)
691 if kwline == 1 " lpat matched
692 let format = 'latex'
693 elseif kwline == 2 " cpat matched
694 let format = 'context'
695 endif " If neither matched, keep default set above.
696 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
697 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
698 " if cline > 0
699 " let format = 'context'
700 " endif
701 " if lline > 0 && (cline == 0 || cline > lline)
702 " let format = 'tex'
703 " endif
704 endif " firstNC
705 call setpos('.', save_cursor)
706 endif " firstline =~ '^%&\s*\a\+'
707
708 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
709 if format == 'plain'
710 setf plaintex
711 elseif format == 'context'
712 setf context
713 else " probably LaTeX
714 setf tex
715 endif
716 return
717endfunc
718
Bram Moolenaard09a2062017-11-11 15:37:45 +0100719func dist#ft#FTxml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100720 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100721 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100722 let line = getline(n)
723 " DocBook 4 or DocBook 5.
724 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
725 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
726 if is_docbook4 || is_docbook5
727 let b:docbk_type = "xml"
728 if is_docbook5
729 let b:docbk_ver = 5
730 else
731 let b:docbk_ver = 4
732 endif
733 setf docbk
734 return
735 endif
736 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
737 setf xbl
738 return
739 endif
740 let n += 1
741 endwhile
742 setf xml
743endfunc
744
Bram Moolenaard09a2062017-11-11 15:37:45 +0100745func dist#ft#FTy()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100746 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100747 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100748 let line = getline(n)
749 if line =~ '^\s*%'
750 setf yacc
751 return
752 endif
753 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
754 setf racc
755 return
756 endif
757 let n = n + 1
758 endwhile
759 setf yacc
760endfunc
761
Bram Moolenaard09a2062017-11-11 15:37:45 +0100762func dist#ft#Redif()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100763 let lnum = 1
764 while lnum <= 5 && lnum < line('$')
765 if getline(lnum) =~ "^\ctemplate-type:"
766 setf redif
767 return
768 endif
769 let lnum = lnum + 1
770 endwhile
771endfunc
772
773
774" Restore 'cpoptions'
775let &cpo = s:cpo_save
776unlet s:cpo_save