blob: 344889249bdb11e7c82fff766728ba876e6af268 [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 Moolenaardeba5eb2021-09-03 19:21:36 +0200267 if exists("g:filetype_m")
268 exe "setf " . g:filetype_m
269 return
270 endif
271
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200272 " excluding end(for|function|if|switch|while) common to Murphi
273 let octave_block_terminators = '\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>'
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200274
Doug Kearns7329cfa2021-11-26 13:01:41 +0000275 let objc_preprocessor = '^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>'
276
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100277 let n = 1
278 let saw_comment = 0 " Whether we've seen a multiline comment leader.
279 while n < 100
280 let line = getline(n)
281 if line =~ '^\s*/\*'
282 " /* ... */ is a comment in Objective C and Murphi, so we can't conclude
283 " it's either of them yet, but track this as a hint in case we don't see
284 " anything more definitive.
285 let saw_comment = 1
286 endif
Doug Kearns7329cfa2021-11-26 13:01:41 +0000287 if line =~ '^\s*//' || line =~ '^\s*@import\>' || line =~ objc_preprocessor
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100288 setf objc
289 return
290 endif
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200291 if line =~ '^\s*\%(#\|%!\)' || line =~ '^\s*unwind_protect\>' ||
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200292 \ line =~ '\%(^\|;\)\s*' .. octave_block_terminators
293 setf octave
294 return
295 endif
296 " TODO: could be Matlab or Octave
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100297 if line =~ '^\s*%'
298 setf matlab
299 return
300 endif
301 if line =~ '^\s*(\*'
302 setf mma
303 return
304 endif
305 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
306 setf murphi
307 return
308 endif
309 let n = n + 1
310 endwhile
311
312 if saw_comment
313 " We didn't see anything definitive, but this looks like either Objective C
314 " or Murphi based on the comment leader. Assume the former as it is more
315 " common.
316 setf objc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100317 else
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200318 " Default is Matlab
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100319 setf matlab
320 endif
321endfunc
322
Bram Moolenaard09a2062017-11-11 15:37:45 +0100323func dist#ft#FTmms()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100324 let n = 1
Bram Moolenaard7df2792020-01-02 21:34:42 +0100325 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100326 let line = getline(n)
327 if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
328 setf mmix
329 return
330 endif
331 if line =~ '^\s*#'
332 setf make
333 return
334 endif
335 let n = n + 1
336 endwhile
337 setf mmix
338endfunc
339
340" This function checks if one of the first five lines start with a dot. In
341" that case it is probably an nroff file: 'filetype' is set and 1 is returned.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100342func dist#ft#FTnroff()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100343 if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.'
344 setf nroff
345 return 1
346 endif
347 return 0
348endfunc
349
Bram Moolenaard09a2062017-11-11 15:37:45 +0100350func dist#ft#FTmm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100351 let n = 1
Bram Moolenaard1caa942020-04-10 22:10:56 +0200352 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100353 let line = getline(n)
354 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
355 setf objcpp
356 return
357 endif
358 let n = n + 1
359 endwhile
360 setf nroff
361endfunc
362
Bram Moolenaard09a2062017-11-11 15:37:45 +0100363func dist#ft#FTpl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100364 if exists("g:filetype_pl")
365 exe "setf " . g:filetype_pl
366 else
367 " recognize Prolog by specific text in the first non-empty line
368 " require a blank after the '%' because Perl uses "%list" and "%translate"
369 let l = getline(nextnonblank(1))
370 if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
371 setf prolog
372 else
373 setf perl
374 endif
375 endif
376endfunc
377
Bram Moolenaard09a2062017-11-11 15:37:45 +0100378func dist#ft#FTinc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100379 if exists("g:filetype_inc")
380 exe "setf " . g:filetype_inc
381 else
382 let lines = getline(1).getline(2).getline(3)
383 if lines =~? "perlscript"
384 setf aspperl
385 elseif lines =~ "<%"
386 setf aspvbs
387 elseif lines =~ "<?"
388 setf php
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100389 " Pascal supports // comments but they're vary rarely used for file
390 " headers so assume POV-Ray
391 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
392 setf pascal
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100393 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100394 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100395 if exists("b:asmsyntax")
396 exe "setf " . fnameescape(b:asmsyntax)
397 else
398 setf pov
399 endif
400 endif
401 endif
402endfunc
403
Bram Moolenaard09a2062017-11-11 15:37:45 +0100404func dist#ft#FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100405 if exists("g:filetype_w")
406 exe "setf " . g:filetype_w
407 return
408 endif
409 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
410 setf progress
411 else
412 setf cweb
413 endif
414endfunc
415
Bram Moolenaard09a2062017-11-11 15:37:45 +0100416func dist#ft#FTprogress_asm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100417 if exists("g:filetype_i")
418 exe "setf " . g:filetype_i
419 return
420 endif
421 " This function checks for an assembly comment the first ten lines.
422 " If not found, assume Progress.
423 let lnum = 1
424 while lnum <= 10 && lnum < line('$')
425 let line = getline(lnum)
426 if line =~ '^\s*;' || line =~ '^\*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100427 call dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100428 return
429 elseif line !~ '^\s*$' || line =~ '^/\*'
430 " Not an empty line: Doesn't look like valid assembly code.
431 " Or it looks like a Progress /* comment
432 break
433 endif
434 let lnum = lnum + 1
435 endw
436 setf progress
437endfunc
438
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100439let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
440let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
441
Bram Moolenaard09a2062017-11-11 15:37:45 +0100442func dist#ft#FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100443 if exists("g:filetype_p")
444 exe "setf " . g:filetype_p
445 return
446 endif
447 " This function checks for valid Pascal syntax in the first ten lines.
448 " Look for either an opening comment or a program start.
449 " If not found, assume Progress.
450 let lnum = 1
451 while lnum <= 10 && lnum < line('$')
452 let line = getline(lnum)
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100453 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100454 setf pascal
455 return
456 elseif line !~ '^\s*$' || line =~ '^/\*'
457 " Not an empty line: Doesn't look like valid Pascal code.
458 " Or it looks like a Progress /* comment
459 break
460 endif
461 let lnum = lnum + 1
462 endw
463 setf progress
464endfunc
465
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100466func dist#ft#FTpp()
467 if exists("g:filetype_pp")
468 exe "setf " . g:filetype_pp
469 else
470 let line = getline(nextnonblank(1))
471 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
472 setf pascal
473 else
474 setf puppet
475 endif
476 endif
477endfunc
478
Bram Moolenaard09a2062017-11-11 15:37:45 +0100479func dist#ft#FTr()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100480 let max = line("$") > 50 ? 50 : line("$")
481
482 for n in range(1, max)
483 " Rebol is easy to recognize, check for that first
484 if getline(n) =~? '\<REBOL\>'
485 setf rebol
486 return
487 endif
488 endfor
489
490 for n in range(1, max)
491 " R has # comments
492 if getline(n) =~ '^\s*#'
493 setf r
494 return
495 endif
496 " Rexx has /* comments */
497 if getline(n) =~ '^\s*/\*'
498 setf rexx
499 return
500 endif
501 endfor
502
503 " Nothing recognized, use user default or assume Rexx
504 if exists("g:filetype_r")
505 exe "setf " . g:filetype_r
506 else
507 " Rexx used to be the default, but R appears to be much more popular.
508 setf r
509 endif
510endfunc
511
Bram Moolenaard09a2062017-11-11 15:37:45 +0100512func dist#ft#McSetf()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100513 " Rely on the file to start with a comment.
514 " MS message text files use ';', Sendmail files use '#' or 'dnl'
515 for lnum in range(1, min([line("$"), 20]))
516 let line = getline(lnum)
517 if line =~ '^\s*\(#\|dnl\)'
518 setf m4 " Sendmail .mc file
519 return
520 elseif line =~ '^\s*;'
521 setf msmessages " MS Message text file
522 return
523 endif
524 endfor
525 setf m4 " Default: Sendmail .mc file
526endfunc
527
528" Called from filetype.vim and scripts.vim.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100529func dist#ft#SetFileTypeSH(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100530 if did_filetype()
531 " Filetype was already detected
532 return
533 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100534 if expand("<amatch>") =~ g:ft_ignore_pat
535 return
536 endif
537 if a:name =~ '\<csh\>'
538 " Some .sh scripts contain #!/bin/csh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100539 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100540 return
541 elseif a:name =~ '\<tcsh\>'
542 " Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100543 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100544 return
545 elseif a:name =~ '\<zsh\>'
546 " Some .sh scripts contain #!/bin/zsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100547 call dist#ft#SetFileTypeShell("zsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100548 return
549 elseif a:name =~ '\<ksh\>'
550 let b:is_kornshell = 1
551 if exists("b:is_bash")
552 unlet b:is_bash
553 endif
554 if exists("b:is_sh")
555 unlet b:is_sh
556 endif
557 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
558 let b:is_bash = 1
559 if exists("b:is_kornshell")
560 unlet b:is_kornshell
561 endif
562 if exists("b:is_sh")
563 unlet b:is_sh
564 endif
565 elseif a:name =~ '\<sh\>'
566 let b:is_sh = 1
567 if exists("b:is_kornshell")
568 unlet b:is_kornshell
569 endif
570 if exists("b:is_bash")
571 unlet b:is_bash
572 endif
573 endif
Bram Moolenaard09a2062017-11-11 15:37:45 +0100574 call dist#ft#SetFileTypeShell("sh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100575endfunc
576
577" For shell-like file types, check for an "exec" command hidden in a comment,
578" as used for Tcl.
579" Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100580func dist#ft#SetFileTypeShell(name)
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 expand("<amatch>") =~ g:ft_ignore_pat
586 return
587 endif
588 let l = 2
589 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
590 " Skip empty and comment lines.
591 let l = l + 1
592 endwhile
593 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
594 " Found an "exec" line after a comment with continuation
595 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
596 if n =~ '\<tclsh\|\<wish'
597 setf tcl
598 return
599 endif
600 endif
601 exe "setf " . a:name
602endfunc
603
Bram Moolenaard09a2062017-11-11 15:37:45 +0100604func dist#ft#CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100605 if did_filetype()
606 " Filetype was already detected
607 return
608 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100609 if exists("g:filetype_csh")
Bram Moolenaard09a2062017-11-11 15:37:45 +0100610 call dist#ft#SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100611 elseif &shell =~ "tcsh"
Bram Moolenaard09a2062017-11-11 15:37:45 +0100612 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100613 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100614 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100615 endif
616endfunc
617
Bram Moolenaarcef73222017-11-09 21:05:31 +0100618let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100619func dist#ft#FTRules()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100620 let path = expand('<amatch>:p')
Bram Moolenaaraa9675a2020-08-17 21:57:09 +0200621 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100622 setf udevrules
623 return
624 endif
625 if path =~ '^/etc/ufw/'
626 setf conf " Better than hog
627 return
628 endif
629 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
630 setf javascript
631 return
632 endif
633 try
634 let config_lines = readfile('/etc/udev/udev.conf')
635 catch /^Vim\%((\a\+)\)\=:E484/
636 setf hog
637 return
638 endtry
639 let dir = expand('<amatch>:p:h')
640 for line in config_lines
641 if line =~ s:ft_rules_udev_rules_pattern
642 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
643 if dir == udev_rules
644 setf udevrules
645 endif
646 break
647 endif
648 endfor
649 setf hog
650endfunc
651
Bram Moolenaard09a2062017-11-11 15:37:45 +0100652func dist#ft#SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100653 if exists("g:filetype_sql")
654 exe "setf " . g:filetype_sql
655 else
656 setf sql
657 endif
658endfunc
659
660" If the file has an extension of 't' and is in a directory 't' or 'xt' then
661" it is almost certainly a Perl test file.
662" If the first line starts with '#' and contains 'perl' it's probably a Perl
663" file.
664" (Slow test) If a file contains a 'use' statement then it is almost certainly
665" a Perl file.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100666func dist#ft#FTperl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100667 let dirname = expand("%:p:h:t")
668 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
669 setf perl
670 return 1
671 endif
672 if getline(1)[0] == '#' && getline(1) =~ 'perl'
673 setf perl
674 return 1
675 endif
Bram Moolenaarf0b03c42017-12-17 17:17:07 +0100676 let save_cursor = getpos('.')
677 call cursor(1,1)
678 let has_use = search('^use\s\s*\k', 'c', 30)
679 call setpos('.', save_cursor)
680 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100681 setf perl
682 return 1
683 endif
684 return 0
685endfunc
686
687" Choose context, plaintex, or tex (LaTeX) based on these rules:
688" 1. Check the first line of the file for "%&<format>".
689" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200690" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100691func dist#ft#FTtex()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100692 let firstline = getline(1)
693 if firstline =~ '^%&\s*\a\+'
694 let format = tolower(matchstr(firstline, '\a\+'))
695 let format = substitute(format, 'pdf', '', '')
696 if format == 'tex'
697 let format = 'latex'
698 elseif format == 'plaintex'
699 let format = 'plain'
700 endif
701 elseif expand('%') =~ 'tex/context/.*/.*.tex'
702 let format = 'context'
703 else
704 " Default value, may be changed later:
705 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
706 " Save position, go to the top of the file, find first non-comment line.
707 let save_cursor = getpos('.')
708 call cursor(1,1)
709 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
710 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
711 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
712 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\>'
713 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
714 \ 'cnp', firstNC + 1000)
715 if kwline == 1 " lpat matched
716 let format = 'latex'
717 elseif kwline == 2 " cpat matched
718 let format = 'context'
719 endif " If neither matched, keep default set above.
720 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
721 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
722 " if cline > 0
723 " let format = 'context'
724 " endif
725 " if lline > 0 && (cline == 0 || cline > lline)
726 " let format = 'tex'
727 " endif
728 endif " firstNC
729 call setpos('.', save_cursor)
730 endif " firstline =~ '^%&\s*\a\+'
731
732 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
733 if format == 'plain'
734 setf plaintex
735 elseif format == 'context'
736 setf context
737 else " probably LaTeX
738 setf tex
739 endif
740 return
741endfunc
742
Bram Moolenaard09a2062017-11-11 15:37:45 +0100743func dist#ft#FTxml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100744 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100745 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100746 let line = getline(n)
747 " DocBook 4 or DocBook 5.
748 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
749 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
750 if is_docbook4 || is_docbook5
751 let b:docbk_type = "xml"
752 if is_docbook5
753 let b:docbk_ver = 5
754 else
755 let b:docbk_ver = 4
756 endif
757 setf docbk
758 return
759 endif
760 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
761 setf xbl
762 return
763 endif
764 let n += 1
765 endwhile
766 setf xml
767endfunc
768
Bram Moolenaard09a2062017-11-11 15:37:45 +0100769func dist#ft#FTy()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100770 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100771 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100772 let line = getline(n)
773 if line =~ '^\s*%'
774 setf yacc
775 return
776 endif
777 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
778 setf racc
779 return
780 endif
781 let n = n + 1
782 endwhile
783 setf yacc
784endfunc
785
Bram Moolenaard09a2062017-11-11 15:37:45 +0100786func dist#ft#Redif()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100787 let lnum = 1
788 while lnum <= 5 && lnum < line('$')
789 if getline(lnum) =~ "^\ctemplate-type:"
790 setf redif
791 return
792 endif
793 let lnum = lnum + 1
794 endwhile
795endfunc
796
797
798" Restore 'cpoptions'
799let &cpo = s:cpo_save
800unlet s:cpo_save