blob: 84691b1e16082ba58025ea8544b6993864ed060e [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 Moolenaard1caa942020-04-10 22:10:56 +02004" Last Change: 2020 Mar 30
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
365 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100366 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100367 if exists("b:asmsyntax")
368 exe "setf " . fnameescape(b:asmsyntax)
369 else
370 setf pov
371 endif
372 endif
373 endif
374endfunc
375
Bram Moolenaard09a2062017-11-11 15:37:45 +0100376func dist#ft#FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100377 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
386endfunc
387
Bram Moolenaard09a2062017-11-11 15:37:45 +0100388func dist#ft#FTprogress_asm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100389 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 =~ '^\*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100399 call dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100400 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
409endfunc
410
Bram Moolenaard09a2062017-11-11 15:37:45 +0100411func dist#ft#FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100412 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
434endfunc
435
Bram Moolenaard09a2062017-11-11 15:37:45 +0100436func dist#ft#FTr()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100437 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
467endfunc
468
Bram Moolenaard09a2062017-11-11 15:37:45 +0100469func dist#ft#McSetf()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100470 " 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
483endfunc
484
485" Called from filetype.vim and scripts.vim.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100486func dist#ft#SetFileTypeSH(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100487 if did_filetype()
488 " Filetype was already detected
489 return
490 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100491 if expand("<amatch>") =~ g:ft_ignore_pat
492 return
493 endif
494 if a:name =~ '\<csh\>'
495 " Some .sh scripts contain #!/bin/csh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100496 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100497 return
498 elseif a:name =~ '\<tcsh\>'
499 " Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100500 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100501 return
502 elseif a:name =~ '\<zsh\>'
503 " Some .sh scripts contain #!/bin/zsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100504 call dist#ft#SetFileTypeShell("zsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100505 return
506 elseif a:name =~ '\<ksh\>'
507 let b:is_kornshell = 1
508 if exists("b:is_bash")
509 unlet b:is_bash
510 endif
511 if exists("b:is_sh")
512 unlet b:is_sh
513 endif
514 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
515 let b:is_bash = 1
516 if exists("b:is_kornshell")
517 unlet b:is_kornshell
518 endif
519 if exists("b:is_sh")
520 unlet b:is_sh
521 endif
522 elseif a:name =~ '\<sh\>'
523 let b:is_sh = 1
524 if exists("b:is_kornshell")
525 unlet b:is_kornshell
526 endif
527 if exists("b:is_bash")
528 unlet b:is_bash
529 endif
530 endif
Bram Moolenaard09a2062017-11-11 15:37:45 +0100531 call dist#ft#SetFileTypeShell("sh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100532endfunc
533
534" For shell-like file types, check for an "exec" command hidden in a comment,
535" as used for Tcl.
536" Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100537func dist#ft#SetFileTypeShell(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100538 if did_filetype()
539 " Filetype was already detected
540 return
541 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100542 if expand("<amatch>") =~ g:ft_ignore_pat
543 return
544 endif
545 let l = 2
546 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
547 " Skip empty and comment lines.
548 let l = l + 1
549 endwhile
550 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
551 " Found an "exec" line after a comment with continuation
552 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
553 if n =~ '\<tclsh\|\<wish'
554 setf tcl
555 return
556 endif
557 endif
558 exe "setf " . a:name
559endfunc
560
Bram Moolenaard09a2062017-11-11 15:37:45 +0100561func dist#ft#CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100562 if did_filetype()
563 " Filetype was already detected
564 return
565 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100566 if exists("g:filetype_csh")
Bram Moolenaard09a2062017-11-11 15:37:45 +0100567 call dist#ft#SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100568 elseif &shell =~ "tcsh"
Bram Moolenaard09a2062017-11-11 15:37:45 +0100569 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100570 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100571 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100572 endif
573endfunc
574
Bram Moolenaarcef73222017-11-09 21:05:31 +0100575let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100576func dist#ft#FTRules()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100577 let path = expand('<amatch>:p')
Bram Moolenaar624b6ea2020-08-17 21:17:25 +0200578 if path =~ '^/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100579 setf udevrules
580 return
581 endif
582 if path =~ '^/etc/ufw/'
583 setf conf " Better than hog
584 return
585 endif
586 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
587 setf javascript
588 return
589 endif
590 try
591 let config_lines = readfile('/etc/udev/udev.conf')
592 catch /^Vim\%((\a\+)\)\=:E484/
593 setf hog
594 return
595 endtry
596 let dir = expand('<amatch>:p:h')
597 for line in config_lines
598 if line =~ s:ft_rules_udev_rules_pattern
599 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
600 if dir == udev_rules
601 setf udevrules
602 endif
603 break
604 endif
605 endfor
606 setf hog
607endfunc
608
Bram Moolenaard09a2062017-11-11 15:37:45 +0100609func dist#ft#SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100610 if exists("g:filetype_sql")
611 exe "setf " . g:filetype_sql
612 else
613 setf sql
614 endif
615endfunc
616
617" If the file has an extension of 't' and is in a directory 't' or 'xt' then
618" it is almost certainly a Perl test file.
619" If the first line starts with '#' and contains 'perl' it's probably a Perl
620" file.
621" (Slow test) If a file contains a 'use' statement then it is almost certainly
622" a Perl file.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100623func dist#ft#FTperl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100624 let dirname = expand("%:p:h:t")
625 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
626 setf perl
627 return 1
628 endif
629 if getline(1)[0] == '#' && getline(1) =~ 'perl'
630 setf perl
631 return 1
632 endif
Bram Moolenaarf0b03c42017-12-17 17:17:07 +0100633 let save_cursor = getpos('.')
634 call cursor(1,1)
635 let has_use = search('^use\s\s*\k', 'c', 30)
636 call setpos('.', save_cursor)
637 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100638 setf perl
639 return 1
640 endif
641 return 0
642endfunc
643
644" Choose context, plaintex, or tex (LaTeX) based on these rules:
645" 1. Check the first line of the file for "%&<format>".
646" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200647" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100648func dist#ft#FTtex()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100649 let firstline = getline(1)
650 if firstline =~ '^%&\s*\a\+'
651 let format = tolower(matchstr(firstline, '\a\+'))
652 let format = substitute(format, 'pdf', '', '')
653 if format == 'tex'
654 let format = 'latex'
655 elseif format == 'plaintex'
656 let format = 'plain'
657 endif
658 elseif expand('%') =~ 'tex/context/.*/.*.tex'
659 let format = 'context'
660 else
661 " Default value, may be changed later:
662 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
663 " Save position, go to the top of the file, find first non-comment line.
664 let save_cursor = getpos('.')
665 call cursor(1,1)
666 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
667 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
668 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
669 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\>'
670 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
671 \ 'cnp', firstNC + 1000)
672 if kwline == 1 " lpat matched
673 let format = 'latex'
674 elseif kwline == 2 " cpat matched
675 let format = 'context'
676 endif " If neither matched, keep default set above.
677 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
678 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
679 " if cline > 0
680 " let format = 'context'
681 " endif
682 " if lline > 0 && (cline == 0 || cline > lline)
683 " let format = 'tex'
684 " endif
685 endif " firstNC
686 call setpos('.', save_cursor)
687 endif " firstline =~ '^%&\s*\a\+'
688
689 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
690 if format == 'plain'
691 setf plaintex
692 elseif format == 'context'
693 setf context
694 else " probably LaTeX
695 setf tex
696 endif
697 return
698endfunc
699
Bram Moolenaard09a2062017-11-11 15:37:45 +0100700func dist#ft#FTxml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100701 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100702 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100703 let line = getline(n)
704 " DocBook 4 or DocBook 5.
705 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
706 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
707 if is_docbook4 || is_docbook5
708 let b:docbk_type = "xml"
709 if is_docbook5
710 let b:docbk_ver = 5
711 else
712 let b:docbk_ver = 4
713 endif
714 setf docbk
715 return
716 endif
717 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
718 setf xbl
719 return
720 endif
721 let n += 1
722 endwhile
723 setf xml
724endfunc
725
Bram Moolenaard09a2062017-11-11 15:37:45 +0100726func dist#ft#FTy()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100727 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100728 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100729 let line = getline(n)
730 if line =~ '^\s*%'
731 setf yacc
732 return
733 endif
734 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
735 setf racc
736 return
737 endif
738 let n = n + 1
739 endwhile
740 setf yacc
741endfunc
742
Bram Moolenaard09a2062017-11-11 15:37:45 +0100743func dist#ft#Redif()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100744 let lnum = 1
745 while lnum <= 5 && lnum < line('$')
746 if getline(lnum) =~ "^\ctemplate-type:"
747 setf redif
748 return
749 endif
750 let lnum = lnum + 1
751 endwhile
752endfunc
753
754
755" Restore 'cpoptions'
756let &cpo = s:cpo_save
757unlet s:cpo_save