blob: 342731b272e1e5cad77fe5a0975591a855e50daf [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 Moolenaarfa3b7232021-12-24 13:18:38 +00004" Last Change: 2021 Dec 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
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000222" Distinguish between Forth and F#.
223" Provided by Doug Kearns.
224func dist#ft#FTfs()
225 if exists("g:filetype_fs")
226 exe "setf " . g:filetype_fs
227 else
228 let line = getline(nextnonblank(1))
229 " comments and colon definitions
230 if line =~ '^\s*\.\=( ' || line =~ '^\s*\\G\= ' || line =~ '^\\$'
231 \ || line =~ '^\s*: \S'
232 setf forth
233 else
Bram Moolenaar53ba95e2021-11-30 13:02:58 +0000234 setf fsharp
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000235 endif
236 endif
237endfunc
238
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100239" Distinguish between HTML, XHTML and Django
Bram Moolenaard09a2062017-11-11 15:37:45 +0100240func dist#ft#FThtml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100241 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100242 while n < 10 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100243 if getline(n) =~ '\<DTD\s\+XHTML\s'
244 setf xhtml
245 return
246 endif
247 if getline(n) =~ '{%\s*\(extends\|block\|load\)\>\|{#\s\+'
248 setf htmldjango
249 return
250 endif
251 let n = n + 1
252 endwhile
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100253 setf FALLBACK html
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100254endfunc
255
256" Distinguish between standard IDL and MS-IDL
Bram Moolenaard09a2062017-11-11 15:37:45 +0100257func dist#ft#FTidl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100258 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100259 while n < 50 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100260 if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"'
261 setf msidl
262 return
263 endif
264 let n = n + 1
265 endwhile
266 setf idl
267endfunc
268
269" Distinguish between "default" and Cproto prototype file. */
Bram Moolenaard09a2062017-11-11 15:37:45 +0100270func dist#ft#ProtoCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100271 " Cproto files have a comment in the first line and a function prototype in
272 " the second line, it always ends in ";". Indent files may also have
273 " comments, thus we can't match comments to see the difference.
274 " IDL files can have a single ';' in the second line, require at least one
275 " chacter before the ';'.
276 if getline(2) =~ '.;$'
277 setf cpp
278 else
279 exe 'setf ' . a:default
280 endif
281endfunc
282
Bram Moolenaard09a2062017-11-11 15:37:45 +0100283func dist#ft#FTm()
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200284 if exists("g:filetype_m")
285 exe "setf " . g:filetype_m
286 return
287 endif
288
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200289 " excluding end(for|function|if|switch|while) common to Murphi
290 let octave_block_terminators = '\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>'
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200291
Doug Kearns7329cfa2021-11-26 13:01:41 +0000292 let objc_preprocessor = '^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>'
293
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100294 let n = 1
295 let saw_comment = 0 " Whether we've seen a multiline comment leader.
296 while n < 100
297 let line = getline(n)
298 if line =~ '^\s*/\*'
299 " /* ... */ is a comment in Objective C and Murphi, so we can't conclude
300 " it's either of them yet, but track this as a hint in case we don't see
301 " anything more definitive.
302 let saw_comment = 1
303 endif
Doug Kearns7329cfa2021-11-26 13:01:41 +0000304 if line =~ '^\s*//' || line =~ '^\s*@import\>' || line =~ objc_preprocessor
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100305 setf objc
306 return
307 endif
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200308 if line =~ '^\s*\%(#\|%!\)' || line =~ '^\s*unwind_protect\>' ||
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200309 \ line =~ '\%(^\|;\)\s*' .. octave_block_terminators
310 setf octave
311 return
312 endif
313 " TODO: could be Matlab or Octave
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100314 if line =~ '^\s*%'
315 setf matlab
316 return
317 endif
318 if line =~ '^\s*(\*'
319 setf mma
320 return
321 endif
322 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
323 setf murphi
324 return
325 endif
326 let n = n + 1
327 endwhile
328
329 if saw_comment
330 " We didn't see anything definitive, but this looks like either Objective C
331 " or Murphi based on the comment leader. Assume the former as it is more
332 " common.
333 setf objc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100334 else
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200335 " Default is Matlab
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100336 setf matlab
337 endif
338endfunc
339
Bram Moolenaard09a2062017-11-11 15:37:45 +0100340func dist#ft#FTmms()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100341 let n = 1
Bram Moolenaard7df2792020-01-02 21:34:42 +0100342 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100343 let line = getline(n)
344 if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
345 setf mmix
346 return
347 endif
348 if line =~ '^\s*#'
349 setf make
350 return
351 endif
352 let n = n + 1
353 endwhile
354 setf mmix
355endfunc
356
357" This function checks if one of the first five lines start with a dot. In
358" that case it is probably an nroff file: 'filetype' is set and 1 is returned.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100359func dist#ft#FTnroff()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100360 if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.'
361 setf nroff
362 return 1
363 endif
364 return 0
365endfunc
366
Bram Moolenaard09a2062017-11-11 15:37:45 +0100367func dist#ft#FTmm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100368 let n = 1
Bram Moolenaard1caa942020-04-10 22:10:56 +0200369 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100370 let line = getline(n)
371 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
372 setf objcpp
373 return
374 endif
375 let n = n + 1
376 endwhile
377 setf nroff
378endfunc
379
Bram Moolenaard09a2062017-11-11 15:37:45 +0100380func dist#ft#FTpl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100381 if exists("g:filetype_pl")
382 exe "setf " . g:filetype_pl
383 else
384 " recognize Prolog by specific text in the first non-empty line
385 " require a blank after the '%' because Perl uses "%list" and "%translate"
386 let l = getline(nextnonblank(1))
387 if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
388 setf prolog
389 else
390 setf perl
391 endif
392 endif
393endfunc
394
Bram Moolenaard09a2062017-11-11 15:37:45 +0100395func dist#ft#FTinc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100396 if exists("g:filetype_inc")
397 exe "setf " . g:filetype_inc
398 else
399 let lines = getline(1).getline(2).getline(3)
400 if lines =~? "perlscript"
401 setf aspperl
402 elseif lines =~ "<%"
403 setf aspvbs
404 elseif lines =~ "<?"
405 setf php
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100406 " Pascal supports // comments but they're vary rarely used for file
407 " headers so assume POV-Ray
408 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
409 setf pascal
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100410 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100411 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100412 if exists("b:asmsyntax")
413 exe "setf " . fnameescape(b:asmsyntax)
414 else
415 setf pov
416 endif
417 endif
418 endif
419endfunc
420
Bram Moolenaard09a2062017-11-11 15:37:45 +0100421func dist#ft#FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100422 if exists("g:filetype_w")
423 exe "setf " . g:filetype_w
424 return
425 endif
426 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
427 setf progress
428 else
429 setf cweb
430 endif
431endfunc
432
Bram Moolenaard09a2062017-11-11 15:37:45 +0100433func dist#ft#FTprogress_asm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100434 if exists("g:filetype_i")
435 exe "setf " . g:filetype_i
436 return
437 endif
438 " This function checks for an assembly comment the first ten lines.
439 " If not found, assume Progress.
440 let lnum = 1
441 while lnum <= 10 && lnum < line('$')
442 let line = getline(lnum)
443 if line =~ '^\s*;' || line =~ '^\*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100444 call dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100445 return
446 elseif line !~ '^\s*$' || line =~ '^/\*'
447 " Not an empty line: Doesn't look like valid assembly code.
448 " Or it looks like a Progress /* comment
449 break
450 endif
451 let lnum = lnum + 1
452 endw
453 setf progress
454endfunc
455
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100456let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
457let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
458
Bram Moolenaard09a2062017-11-11 15:37:45 +0100459func dist#ft#FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100460 if exists("g:filetype_p")
461 exe "setf " . g:filetype_p
462 return
463 endif
464 " This function checks for valid Pascal syntax in the first ten lines.
465 " Look for either an opening comment or a program start.
466 " If not found, assume Progress.
467 let lnum = 1
468 while lnum <= 10 && lnum < line('$')
469 let line = getline(lnum)
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100470 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100471 setf pascal
472 return
473 elseif line !~ '^\s*$' || line =~ '^/\*'
474 " Not an empty line: Doesn't look like valid Pascal code.
475 " Or it looks like a Progress /* comment
476 break
477 endif
478 let lnum = lnum + 1
479 endw
480 setf progress
481endfunc
482
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100483func dist#ft#FTpp()
484 if exists("g:filetype_pp")
485 exe "setf " . g:filetype_pp
486 else
487 let line = getline(nextnonblank(1))
488 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
489 setf pascal
490 else
491 setf puppet
492 endif
493 endif
494endfunc
495
Bram Moolenaard09a2062017-11-11 15:37:45 +0100496func dist#ft#FTr()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100497 let max = line("$") > 50 ? 50 : line("$")
498
499 for n in range(1, max)
500 " Rebol is easy to recognize, check for that first
501 if getline(n) =~? '\<REBOL\>'
502 setf rebol
503 return
504 endif
505 endfor
506
507 for n in range(1, max)
508 " R has # comments
509 if getline(n) =~ '^\s*#'
510 setf r
511 return
512 endif
513 " Rexx has /* comments */
514 if getline(n) =~ '^\s*/\*'
515 setf rexx
516 return
517 endif
518 endfor
519
520 " Nothing recognized, use user default or assume Rexx
521 if exists("g:filetype_r")
522 exe "setf " . g:filetype_r
523 else
524 " Rexx used to be the default, but R appears to be much more popular.
525 setf r
526 endif
527endfunc
528
Bram Moolenaard09a2062017-11-11 15:37:45 +0100529func dist#ft#McSetf()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100530 " Rely on the file to start with a comment.
531 " MS message text files use ';', Sendmail files use '#' or 'dnl'
532 for lnum in range(1, min([line("$"), 20]))
533 let line = getline(lnum)
534 if line =~ '^\s*\(#\|dnl\)'
535 setf m4 " Sendmail .mc file
536 return
537 elseif line =~ '^\s*;'
538 setf msmessages " MS Message text file
539 return
540 endif
541 endfor
542 setf m4 " Default: Sendmail .mc file
543endfunc
544
545" Called from filetype.vim and scripts.vim.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100546func dist#ft#SetFileTypeSH(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100547 if did_filetype()
548 " Filetype was already detected
549 return
550 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100551 if expand("<amatch>") =~ g:ft_ignore_pat
552 return
553 endif
554 if a:name =~ '\<csh\>'
555 " Some .sh scripts contain #!/bin/csh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100556 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100557 return
558 elseif a:name =~ '\<tcsh\>'
559 " Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100560 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100561 return
562 elseif a:name =~ '\<zsh\>'
563 " Some .sh scripts contain #!/bin/zsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100564 call dist#ft#SetFileTypeShell("zsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100565 return
566 elseif a:name =~ '\<ksh\>'
567 let b:is_kornshell = 1
568 if exists("b:is_bash")
569 unlet b:is_bash
570 endif
571 if exists("b:is_sh")
572 unlet b:is_sh
573 endif
574 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
575 let b:is_bash = 1
576 if exists("b:is_kornshell")
577 unlet b:is_kornshell
578 endif
579 if exists("b:is_sh")
580 unlet b:is_sh
581 endif
582 elseif a:name =~ '\<sh\>'
583 let b:is_sh = 1
584 if exists("b:is_kornshell")
585 unlet b:is_kornshell
586 endif
587 if exists("b:is_bash")
588 unlet b:is_bash
589 endif
590 endif
Bram Moolenaard09a2062017-11-11 15:37:45 +0100591 call dist#ft#SetFileTypeShell("sh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100592endfunc
593
594" For shell-like file types, check for an "exec" command hidden in a comment,
595" as used for Tcl.
596" Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100597func dist#ft#SetFileTypeShell(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100598 if did_filetype()
599 " Filetype was already detected
600 return
601 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100602 if expand("<amatch>") =~ g:ft_ignore_pat
603 return
604 endif
605 let l = 2
606 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
607 " Skip empty and comment lines.
608 let l = l + 1
609 endwhile
610 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
611 " Found an "exec" line after a comment with continuation
612 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
613 if n =~ '\<tclsh\|\<wish'
614 setf tcl
615 return
616 endif
617 endif
618 exe "setf " . a:name
619endfunc
620
Bram Moolenaard09a2062017-11-11 15:37:45 +0100621func dist#ft#CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100622 if did_filetype()
623 " Filetype was already detected
624 return
625 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100626 if exists("g:filetype_csh")
Bram Moolenaard09a2062017-11-11 15:37:45 +0100627 call dist#ft#SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100628 elseif &shell =~ "tcsh"
Bram Moolenaard09a2062017-11-11 15:37:45 +0100629 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100630 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100631 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100632 endif
633endfunc
634
Bram Moolenaarcef73222017-11-09 21:05:31 +0100635let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100636func dist#ft#FTRules()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100637 let path = expand('<amatch>:p')
Bram Moolenaaraa9675a2020-08-17 21:57:09 +0200638 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100639 setf udevrules
640 return
641 endif
642 if path =~ '^/etc/ufw/'
643 setf conf " Better than hog
644 return
645 endif
646 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
647 setf javascript
648 return
649 endif
650 try
651 let config_lines = readfile('/etc/udev/udev.conf')
652 catch /^Vim\%((\a\+)\)\=:E484/
653 setf hog
654 return
655 endtry
656 let dir = expand('<amatch>:p:h')
657 for line in config_lines
658 if line =~ s:ft_rules_udev_rules_pattern
659 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
660 if dir == udev_rules
661 setf udevrules
662 endif
663 break
664 endif
665 endfor
666 setf hog
667endfunc
668
Bram Moolenaard09a2062017-11-11 15:37:45 +0100669func dist#ft#SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100670 if exists("g:filetype_sql")
671 exe "setf " . g:filetype_sql
672 else
673 setf sql
674 endif
675endfunc
676
677" If the file has an extension of 't' and is in a directory 't' or 'xt' then
678" it is almost certainly a Perl test file.
679" If the first line starts with '#' and contains 'perl' it's probably a Perl
680" file.
681" (Slow test) If a file contains a 'use' statement then it is almost certainly
682" a Perl file.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100683func dist#ft#FTperl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100684 let dirname = expand("%:p:h:t")
685 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
686 setf perl
687 return 1
688 endif
689 if getline(1)[0] == '#' && getline(1) =~ 'perl'
690 setf perl
691 return 1
692 endif
Bram Moolenaarf0b03c42017-12-17 17:17:07 +0100693 let save_cursor = getpos('.')
694 call cursor(1,1)
695 let has_use = search('^use\s\s*\k', 'c', 30)
696 call setpos('.', save_cursor)
697 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100698 setf perl
699 return 1
700 endif
701 return 0
702endfunc
703
704" Choose context, plaintex, or tex (LaTeX) based on these rules:
705" 1. Check the first line of the file for "%&<format>".
706" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200707" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100708func dist#ft#FTtex()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100709 let firstline = getline(1)
710 if firstline =~ '^%&\s*\a\+'
711 let format = tolower(matchstr(firstline, '\a\+'))
712 let format = substitute(format, 'pdf', '', '')
713 if format == 'tex'
714 let format = 'latex'
715 elseif format == 'plaintex'
716 let format = 'plain'
717 endif
718 elseif expand('%') =~ 'tex/context/.*/.*.tex'
719 let format = 'context'
720 else
721 " Default value, may be changed later:
722 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
723 " Save position, go to the top of the file, find first non-comment line.
724 let save_cursor = getpos('.')
725 call cursor(1,1)
726 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
727 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
728 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
729 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\>'
730 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
731 \ 'cnp', firstNC + 1000)
732 if kwline == 1 " lpat matched
733 let format = 'latex'
734 elseif kwline == 2 " cpat matched
735 let format = 'context'
736 endif " If neither matched, keep default set above.
737 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
738 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
739 " if cline > 0
740 " let format = 'context'
741 " endif
742 " if lline > 0 && (cline == 0 || cline > lline)
743 " let format = 'tex'
744 " endif
745 endif " firstNC
746 call setpos('.', save_cursor)
747 endif " firstline =~ '^%&\s*\a\+'
748
749 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
750 if format == 'plain'
751 setf plaintex
752 elseif format == 'context'
753 setf context
754 else " probably LaTeX
755 setf tex
756 endif
757 return
758endfunc
759
Bram Moolenaard09a2062017-11-11 15:37:45 +0100760func dist#ft#FTxml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100761 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100762 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100763 let line = getline(n)
764 " DocBook 4 or DocBook 5.
765 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
766 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
767 if is_docbook4 || is_docbook5
768 let b:docbk_type = "xml"
769 if is_docbook5
770 let b:docbk_ver = 5
771 else
772 let b:docbk_ver = 4
773 endif
774 setf docbk
775 return
776 endif
777 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
778 setf xbl
779 return
780 endif
781 let n += 1
782 endwhile
783 setf xml
784endfunc
785
Bram Moolenaard09a2062017-11-11 15:37:45 +0100786func dist#ft#FTy()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100787 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100788 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100789 let line = getline(n)
790 if line =~ '^\s*%'
791 setf yacc
792 return
793 endif
794 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
795 setf racc
796 return
797 endif
798 let n = n + 1
799 endwhile
800 setf yacc
801endfunc
802
Bram Moolenaard09a2062017-11-11 15:37:45 +0100803func dist#ft#Redif()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100804 let lnum = 1
805 while lnum <= 5 && lnum < line('$')
806 if getline(lnum) =~ "^\ctemplate-type:"
807 setf redif
808 return
809 endif
810 let lnum = lnum + 1
811 endwhile
812endfunc
813
James McCoy647ab4c2021-12-17 20:52:57 +0000814" This function is called for all files under */debian/patches/*, make sure not
815" to non-dep3patch files, such as README and other text files.
816func dist#ft#Dep3patch()
817 if expand('%:t') ==# 'series'
818 return
819 endif
820
821 for ln in getline(1, 100)
822 if ln =~# '^\%(Description\|Subject\|Origin\|Bug\|Forwarded\|Author\|From\|Reviewed-by\|Acked-by\|Last-Updated\|Applied-Upstream\):'
823 setf dep3patch
824 return
825 elseif ln =~# '^---'
826 " end of headers found. stop processing
827 return
828 endif
829 endfor
830endfunc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100831
Elwardi2284f6c2022-01-11 18:14:23 +0000832" This function checks the first 15 lines for appearance of 'FoamFile'
833" and then 'object' in a following line.
834" In that case, it's probably an OpenFOAM file
835func dist#ft#FTfoam()
836 let ffile = 0
837 let lnum = 1
838 while lnum <= 15
839 if getline(lnum) =~# '^FoamFile'
840 let ffile = 1
841 elseif ffile == 1 && getline(lnum) =~# '^\s*object'
842 setf foam
843 return
844 endif
845 let lnum = lnum + 1
846 endwhile
847endfunc
848
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100849" Restore 'cpoptions'
850let &cpo = s:cpo_save
851unlet s:cpo_save