blob: ac8065911370dfe614648736a013c1efc17760af [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
Austin Gatlinf3caeb62021-06-26 12:02:55 +0200175func dist#ft#ExCheck()
176 let lines = getline(1, min([line("$"), 100]))
177 if exists('g:filetype_euphoria')
178 exe 'setf ' . g:filetype_euphoria
179 elseif match(lines, '^--\|^ifdef\>\|^include\>') > -1
180 setf euphoria3
181 else
182 setf elixir
183 endif
184endfunc
185
Bram Moolenaard09a2062017-11-11 15:37:45 +0100186func dist#ft#EuphoriaCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100187 if exists('g:filetype_euphoria')
188 exe 'setf ' . g:filetype_euphoria
189 else
190 setf euphoria3
191 endif
192endfunc
193
Bram Moolenaard09a2062017-11-11 15:37:45 +0100194func dist#ft#DtraceCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100195 let lines = getline(1, min([line("$"), 100]))
196 if match(lines, '^module\>\|^import\>') > -1
197 " D files often start with a module and/or import statement.
198 setf d
199 elseif match(lines, '^#!\S\+dtrace\|#pragma\s\+D\s\+option\|:\S\{-}:\S\{-}:') > -1
200 setf dtrace
201 else
202 setf d
203 endif
204endfunc
205
Bram Moolenaard09a2062017-11-11 15:37:45 +0100206func dist#ft#FTe()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100207 if exists('g:filetype_euphoria')
208 exe 'setf ' . g:filetype_euphoria
209 else
210 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100211 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100212 if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$"
213 setf specman
214 return
215 endif
216 let n = n + 1
217 endwhile
218 setf eiffel
219 endif
220endfunc
221
222" Distinguish between HTML, XHTML and Django
Bram Moolenaard09a2062017-11-11 15:37:45 +0100223func dist#ft#FThtml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100224 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100225 while n < 10 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100226 if getline(n) =~ '\<DTD\s\+XHTML\s'
227 setf xhtml
228 return
229 endif
230 if getline(n) =~ '{%\s*\(extends\|block\|load\)\>\|{#\s\+'
231 setf htmldjango
232 return
233 endif
234 let n = n + 1
235 endwhile
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100236 setf FALLBACK html
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100237endfunc
238
239" Distinguish between standard IDL and MS-IDL
Bram Moolenaard09a2062017-11-11 15:37:45 +0100240func dist#ft#FTidl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100241 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100242 while n < 50 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100243 if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"'
244 setf msidl
245 return
246 endif
247 let n = n + 1
248 endwhile
249 setf idl
250endfunc
251
252" Distinguish between "default" and Cproto prototype file. */
Bram Moolenaard09a2062017-11-11 15:37:45 +0100253func dist#ft#ProtoCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100254 " Cproto files have a comment in the first line and a function prototype in
255 " the second line, it always ends in ";". Indent files may also have
256 " comments, thus we can't match comments to see the difference.
257 " IDL files can have a single ';' in the second line, require at least one
258 " chacter before the ';'.
259 if getline(2) =~ '.;$'
260 setf cpp
261 else
262 exe 'setf ' . a:default
263 endif
264endfunc
265
Bram Moolenaard09a2062017-11-11 15:37:45 +0100266func dist#ft#FTm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100267 let n = 1
268 let saw_comment = 0 " Whether we've seen a multiline comment leader.
269 while n < 100
270 let line = getline(n)
271 if line =~ '^\s*/\*'
272 " /* ... */ is a comment in Objective C and Murphi, so we can't conclude
273 " it's either of them yet, but track this as a hint in case we don't see
274 " anything more definitive.
275 let saw_comment = 1
276 endif
277 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|//\)'
278 setf objc
279 return
280 endif
281 if line =~ '^\s*%'
282 setf matlab
283 return
284 endif
285 if line =~ '^\s*(\*'
286 setf mma
287 return
288 endif
289 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
290 setf murphi
291 return
292 endif
293 let n = n + 1
294 endwhile
295
296 if saw_comment
297 " We didn't see anything definitive, but this looks like either Objective C
298 " or Murphi based on the comment leader. Assume the former as it is more
299 " common.
300 setf objc
301 elseif exists("g:filetype_m")
302 " Use user specified default filetype for .m
303 exe "setf " . g:filetype_m
304 else
305 " Default is matlab
306 setf matlab
307 endif
308endfunc
309
Bram Moolenaard09a2062017-11-11 15:37:45 +0100310func dist#ft#FTmms()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100311 let n = 1
Bram Moolenaard7df2792020-01-02 21:34:42 +0100312 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100313 let line = getline(n)
314 if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
315 setf mmix
316 return
317 endif
318 if line =~ '^\s*#'
319 setf make
320 return
321 endif
322 let n = n + 1
323 endwhile
324 setf mmix
325endfunc
326
327" This function checks if one of the first five lines start with a dot. In
328" that case it is probably an nroff file: 'filetype' is set and 1 is returned.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100329func dist#ft#FTnroff()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100330 if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.'
331 setf nroff
332 return 1
333 endif
334 return 0
335endfunc
336
Bram Moolenaard09a2062017-11-11 15:37:45 +0100337func dist#ft#FTmm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100338 let n = 1
Bram Moolenaard1caa942020-04-10 22:10:56 +0200339 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100340 let line = getline(n)
341 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
342 setf objcpp
343 return
344 endif
345 let n = n + 1
346 endwhile
347 setf nroff
348endfunc
349
Bram Moolenaard09a2062017-11-11 15:37:45 +0100350func dist#ft#FTpl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100351 if exists("g:filetype_pl")
352 exe "setf " . g:filetype_pl
353 else
354 " recognize Prolog by specific text in the first non-empty line
355 " require a blank after the '%' because Perl uses "%list" and "%translate"
356 let l = getline(nextnonblank(1))
357 if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
358 setf prolog
359 else
360 setf perl
361 endif
362 endif
363endfunc
364
Bram Moolenaard09a2062017-11-11 15:37:45 +0100365func dist#ft#FTinc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100366 if exists("g:filetype_inc")
367 exe "setf " . g:filetype_inc
368 else
369 let lines = getline(1).getline(2).getline(3)
370 if lines =~? "perlscript"
371 setf aspperl
372 elseif lines =~ "<%"
373 setf aspvbs
374 elseif lines =~ "<?"
375 setf php
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100376 " Pascal supports // comments but they're vary rarely used for file
377 " headers so assume POV-Ray
378 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
379 setf pascal
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100380 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100381 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100382 if exists("b:asmsyntax")
383 exe "setf " . fnameescape(b:asmsyntax)
384 else
385 setf pov
386 endif
387 endif
388 endif
389endfunc
390
Bram Moolenaard09a2062017-11-11 15:37:45 +0100391func dist#ft#FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100392 if exists("g:filetype_w")
393 exe "setf " . g:filetype_w
394 return
395 endif
396 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
397 setf progress
398 else
399 setf cweb
400 endif
401endfunc
402
Bram Moolenaard09a2062017-11-11 15:37:45 +0100403func dist#ft#FTprogress_asm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100404 if exists("g:filetype_i")
405 exe "setf " . g:filetype_i
406 return
407 endif
408 " This function checks for an assembly comment the first ten lines.
409 " If not found, assume Progress.
410 let lnum = 1
411 while lnum <= 10 && lnum < line('$')
412 let line = getline(lnum)
413 if line =~ '^\s*;' || line =~ '^\*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100414 call dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100415 return
416 elseif line !~ '^\s*$' || line =~ '^/\*'
417 " Not an empty line: Doesn't look like valid assembly code.
418 " Or it looks like a Progress /* comment
419 break
420 endif
421 let lnum = lnum + 1
422 endw
423 setf progress
424endfunc
425
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100426let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
427let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
428
Bram Moolenaard09a2062017-11-11 15:37:45 +0100429func dist#ft#FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100430 if exists("g:filetype_p")
431 exe "setf " . g:filetype_p
432 return
433 endif
434 " This function checks for valid Pascal syntax in the first ten lines.
435 " Look for either an opening comment or a program start.
436 " If not found, assume Progress.
437 let lnum = 1
438 while lnum <= 10 && lnum < line('$')
439 let line = getline(lnum)
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100440 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100441 setf pascal
442 return
443 elseif line !~ '^\s*$' || line =~ '^/\*'
444 " Not an empty line: Doesn't look like valid Pascal code.
445 " Or it looks like a Progress /* comment
446 break
447 endif
448 let lnum = lnum + 1
449 endw
450 setf progress
451endfunc
452
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100453func dist#ft#FTpp()
454 if exists("g:filetype_pp")
455 exe "setf " . g:filetype_pp
456 else
457 let line = getline(nextnonblank(1))
458 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
459 setf pascal
460 else
461 setf puppet
462 endif
463 endif
464endfunc
465
Bram Moolenaard09a2062017-11-11 15:37:45 +0100466func dist#ft#FTr()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100467 let max = line("$") > 50 ? 50 : line("$")
468
469 for n in range(1, max)
470 " Rebol is easy to recognize, check for that first
471 if getline(n) =~? '\<REBOL\>'
472 setf rebol
473 return
474 endif
475 endfor
476
477 for n in range(1, max)
478 " R has # comments
479 if getline(n) =~ '^\s*#'
480 setf r
481 return
482 endif
483 " Rexx has /* comments */
484 if getline(n) =~ '^\s*/\*'
485 setf rexx
486 return
487 endif
488 endfor
489
490 " Nothing recognized, use user default or assume Rexx
491 if exists("g:filetype_r")
492 exe "setf " . g:filetype_r
493 else
494 " Rexx used to be the default, but R appears to be much more popular.
495 setf r
496 endif
497endfunc
498
Bram Moolenaard09a2062017-11-11 15:37:45 +0100499func dist#ft#McSetf()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100500 " Rely on the file to start with a comment.
501 " MS message text files use ';', Sendmail files use '#' or 'dnl'
502 for lnum in range(1, min([line("$"), 20]))
503 let line = getline(lnum)
504 if line =~ '^\s*\(#\|dnl\)'
505 setf m4 " Sendmail .mc file
506 return
507 elseif line =~ '^\s*;'
508 setf msmessages " MS Message text file
509 return
510 endif
511 endfor
512 setf m4 " Default: Sendmail .mc file
513endfunc
514
515" Called from filetype.vim and scripts.vim.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100516func dist#ft#SetFileTypeSH(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100517 if did_filetype()
518 " Filetype was already detected
519 return
520 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100521 if expand("<amatch>") =~ g:ft_ignore_pat
522 return
523 endif
524 if a:name =~ '\<csh\>'
525 " Some .sh scripts contain #!/bin/csh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100526 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100527 return
528 elseif a:name =~ '\<tcsh\>'
529 " Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100530 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100531 return
532 elseif a:name =~ '\<zsh\>'
533 " Some .sh scripts contain #!/bin/zsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100534 call dist#ft#SetFileTypeShell("zsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100535 return
536 elseif a:name =~ '\<ksh\>'
537 let b:is_kornshell = 1
538 if exists("b:is_bash")
539 unlet b:is_bash
540 endif
541 if exists("b:is_sh")
542 unlet b:is_sh
543 endif
544 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
545 let b:is_bash = 1
546 if exists("b:is_kornshell")
547 unlet b:is_kornshell
548 endif
549 if exists("b:is_sh")
550 unlet b:is_sh
551 endif
552 elseif a:name =~ '\<sh\>'
553 let b:is_sh = 1
554 if exists("b:is_kornshell")
555 unlet b:is_kornshell
556 endif
557 if exists("b:is_bash")
558 unlet b:is_bash
559 endif
560 endif
Bram Moolenaard09a2062017-11-11 15:37:45 +0100561 call dist#ft#SetFileTypeShell("sh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100562endfunc
563
564" For shell-like file types, check for an "exec" command hidden in a comment,
565" as used for Tcl.
566" Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100567func dist#ft#SetFileTypeShell(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100568 if did_filetype()
569 " Filetype was already detected
570 return
571 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100572 if expand("<amatch>") =~ g:ft_ignore_pat
573 return
574 endif
575 let l = 2
576 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
577 " Skip empty and comment lines.
578 let l = l + 1
579 endwhile
580 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
581 " Found an "exec" line after a comment with continuation
582 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
583 if n =~ '\<tclsh\|\<wish'
584 setf tcl
585 return
586 endif
587 endif
588 exe "setf " . a:name
589endfunc
590
Bram Moolenaard09a2062017-11-11 15:37:45 +0100591func dist#ft#CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100592 if did_filetype()
593 " Filetype was already detected
594 return
595 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100596 if exists("g:filetype_csh")
Bram Moolenaard09a2062017-11-11 15:37:45 +0100597 call dist#ft#SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100598 elseif &shell =~ "tcsh"
Bram Moolenaard09a2062017-11-11 15:37:45 +0100599 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100600 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100601 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100602 endif
603endfunc
604
Bram Moolenaarcef73222017-11-09 21:05:31 +0100605let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100606func dist#ft#FTRules()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100607 let path = expand('<amatch>:p')
Bram Moolenaaraa9675a2020-08-17 21:57:09 +0200608 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100609 setf udevrules
610 return
611 endif
612 if path =~ '^/etc/ufw/'
613 setf conf " Better than hog
614 return
615 endif
616 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
617 setf javascript
618 return
619 endif
620 try
621 let config_lines = readfile('/etc/udev/udev.conf')
622 catch /^Vim\%((\a\+)\)\=:E484/
623 setf hog
624 return
625 endtry
626 let dir = expand('<amatch>:p:h')
627 for line in config_lines
628 if line =~ s:ft_rules_udev_rules_pattern
629 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
630 if dir == udev_rules
631 setf udevrules
632 endif
633 break
634 endif
635 endfor
636 setf hog
637endfunc
638
Bram Moolenaard09a2062017-11-11 15:37:45 +0100639func dist#ft#SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100640 if exists("g:filetype_sql")
641 exe "setf " . g:filetype_sql
642 else
643 setf sql
644 endif
645endfunc
646
647" If the file has an extension of 't' and is in a directory 't' or 'xt' then
648" it is almost certainly a Perl test file.
649" If the first line starts with '#' and contains 'perl' it's probably a Perl
650" file.
651" (Slow test) If a file contains a 'use' statement then it is almost certainly
652" a Perl file.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100653func dist#ft#FTperl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100654 let dirname = expand("%:p:h:t")
655 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
656 setf perl
657 return 1
658 endif
659 if getline(1)[0] == '#' && getline(1) =~ 'perl'
660 setf perl
661 return 1
662 endif
Bram Moolenaarf0b03c42017-12-17 17:17:07 +0100663 let save_cursor = getpos('.')
664 call cursor(1,1)
665 let has_use = search('^use\s\s*\k', 'c', 30)
666 call setpos('.', save_cursor)
667 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100668 setf perl
669 return 1
670 endif
671 return 0
672endfunc
673
674" Choose context, plaintex, or tex (LaTeX) based on these rules:
675" 1. Check the first line of the file for "%&<format>".
676" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200677" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100678func dist#ft#FTtex()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100679 let firstline = getline(1)
680 if firstline =~ '^%&\s*\a\+'
681 let format = tolower(matchstr(firstline, '\a\+'))
682 let format = substitute(format, 'pdf', '', '')
683 if format == 'tex'
684 let format = 'latex'
685 elseif format == 'plaintex'
686 let format = 'plain'
687 endif
688 elseif expand('%') =~ 'tex/context/.*/.*.tex'
689 let format = 'context'
690 else
691 " Default value, may be changed later:
692 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
693 " Save position, go to the top of the file, find first non-comment line.
694 let save_cursor = getpos('.')
695 call cursor(1,1)
696 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
697 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
698 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
699 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\>'
700 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
701 \ 'cnp', firstNC + 1000)
702 if kwline == 1 " lpat matched
703 let format = 'latex'
704 elseif kwline == 2 " cpat matched
705 let format = 'context'
706 endif " If neither matched, keep default set above.
707 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
708 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
709 " if cline > 0
710 " let format = 'context'
711 " endif
712 " if lline > 0 && (cline == 0 || cline > lline)
713 " let format = 'tex'
714 " endif
715 endif " firstNC
716 call setpos('.', save_cursor)
717 endif " firstline =~ '^%&\s*\a\+'
718
719 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
720 if format == 'plain'
721 setf plaintex
722 elseif format == 'context'
723 setf context
724 else " probably LaTeX
725 setf tex
726 endif
727 return
728endfunc
729
Bram Moolenaard09a2062017-11-11 15:37:45 +0100730func dist#ft#FTxml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100731 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100732 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100733 let line = getline(n)
734 " DocBook 4 or DocBook 5.
735 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
736 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
737 if is_docbook4 || is_docbook5
738 let b:docbk_type = "xml"
739 if is_docbook5
740 let b:docbk_ver = 5
741 else
742 let b:docbk_ver = 4
743 endif
744 setf docbk
745 return
746 endif
747 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
748 setf xbl
749 return
750 endif
751 let n += 1
752 endwhile
753 setf xml
754endfunc
755
Bram Moolenaard09a2062017-11-11 15:37:45 +0100756func dist#ft#FTy()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100757 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100758 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100759 let line = getline(n)
760 if line =~ '^\s*%'
761 setf yacc
762 return
763 endif
764 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
765 setf racc
766 return
767 endif
768 let n = n + 1
769 endwhile
770 setf yacc
771endfunc
772
Bram Moolenaard09a2062017-11-11 15:37:45 +0100773func dist#ft#Redif()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100774 let lnum = 1
775 while lnum <= 5 && lnum < line('$')
776 if getline(lnum) =~ "^\ctemplate-type:"
777 setf redif
778 return
779 endif
780 let lnum = lnum + 1
781 endwhile
782endfunc
783
784
785" Restore 'cpoptions'
786let &cpo = s:cpo_save
787unlet s:cpo_save