blob: 7484149a26d418d66af469ae3fd4e4f98c0a554b [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
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100275 let n = 1
276 let saw_comment = 0 " Whether we've seen a multiline comment leader.
277 while n < 100
278 let line = getline(n)
279 if line =~ '^\s*/\*'
280 " /* ... */ is a comment in Objective C and Murphi, so we can't conclude
281 " it's either of them yet, but track this as a hint in case we don't see
282 " anything more definitive.
283 let saw_comment = 1
284 endif
285 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|//\)'
286 setf objc
287 return
288 endif
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200289 if line =~ '^\s*\%(#\|%!\)' || line =~ '^\s*unwind_protect\>' ||
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200290 \ line =~ '\%(^\|;\)\s*' .. octave_block_terminators
291 setf octave
292 return
293 endif
294 " TODO: could be Matlab or Octave
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100295 if line =~ '^\s*%'
296 setf matlab
297 return
298 endif
299 if line =~ '^\s*(\*'
300 setf mma
301 return
302 endif
303 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
304 setf murphi
305 return
306 endif
307 let n = n + 1
308 endwhile
309
310 if saw_comment
311 " We didn't see anything definitive, but this looks like either Objective C
312 " or Murphi based on the comment leader. Assume the former as it is more
313 " common.
314 setf objc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100315 else
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200316 " Default is Matlab
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100317 setf matlab
318 endif
319endfunc
320
Bram Moolenaard09a2062017-11-11 15:37:45 +0100321func dist#ft#FTmms()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100322 let n = 1
Bram Moolenaard7df2792020-01-02 21:34:42 +0100323 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100324 let line = getline(n)
325 if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
326 setf mmix
327 return
328 endif
329 if line =~ '^\s*#'
330 setf make
331 return
332 endif
333 let n = n + 1
334 endwhile
335 setf mmix
336endfunc
337
338" This function checks if one of the first five lines start with a dot. In
339" that case it is probably an nroff file: 'filetype' is set and 1 is returned.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100340func dist#ft#FTnroff()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100341 if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.'
342 setf nroff
343 return 1
344 endif
345 return 0
346endfunc
347
Bram Moolenaard09a2062017-11-11 15:37:45 +0100348func dist#ft#FTmm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100349 let n = 1
Bram Moolenaard1caa942020-04-10 22:10:56 +0200350 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100351 let line = getline(n)
352 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
353 setf objcpp
354 return
355 endif
356 let n = n + 1
357 endwhile
358 setf nroff
359endfunc
360
Bram Moolenaard09a2062017-11-11 15:37:45 +0100361func dist#ft#FTpl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100362 if exists("g:filetype_pl")
363 exe "setf " . g:filetype_pl
364 else
365 " recognize Prolog by specific text in the first non-empty line
366 " require a blank after the '%' because Perl uses "%list" and "%translate"
367 let l = getline(nextnonblank(1))
368 if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
369 setf prolog
370 else
371 setf perl
372 endif
373 endif
374endfunc
375
Bram Moolenaard09a2062017-11-11 15:37:45 +0100376func dist#ft#FTinc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100377 if exists("g:filetype_inc")
378 exe "setf " . g:filetype_inc
379 else
380 let lines = getline(1).getline(2).getline(3)
381 if lines =~? "perlscript"
382 setf aspperl
383 elseif lines =~ "<%"
384 setf aspvbs
385 elseif lines =~ "<?"
386 setf php
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100387 " Pascal supports // comments but they're vary rarely used for file
388 " headers so assume POV-Ray
389 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
390 setf pascal
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100391 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100392 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100393 if exists("b:asmsyntax")
394 exe "setf " . fnameescape(b:asmsyntax)
395 else
396 setf pov
397 endif
398 endif
399 endif
400endfunc
401
Bram Moolenaard09a2062017-11-11 15:37:45 +0100402func dist#ft#FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100403 if exists("g:filetype_w")
404 exe "setf " . g:filetype_w
405 return
406 endif
407 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
408 setf progress
409 else
410 setf cweb
411 endif
412endfunc
413
Bram Moolenaard09a2062017-11-11 15:37:45 +0100414func dist#ft#FTprogress_asm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100415 if exists("g:filetype_i")
416 exe "setf " . g:filetype_i
417 return
418 endif
419 " This function checks for an assembly comment the first ten lines.
420 " If not found, assume Progress.
421 let lnum = 1
422 while lnum <= 10 && lnum < line('$')
423 let line = getline(lnum)
424 if line =~ '^\s*;' || line =~ '^\*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100425 call dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100426 return
427 elseif line !~ '^\s*$' || line =~ '^/\*'
428 " Not an empty line: Doesn't look like valid assembly code.
429 " Or it looks like a Progress /* comment
430 break
431 endif
432 let lnum = lnum + 1
433 endw
434 setf progress
435endfunc
436
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100437let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
438let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
439
Bram Moolenaard09a2062017-11-11 15:37:45 +0100440func dist#ft#FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100441 if exists("g:filetype_p")
442 exe "setf " . g:filetype_p
443 return
444 endif
445 " This function checks for valid Pascal syntax in the first ten lines.
446 " Look for either an opening comment or a program start.
447 " If not found, assume Progress.
448 let lnum = 1
449 while lnum <= 10 && lnum < line('$')
450 let line = getline(lnum)
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100451 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100452 setf pascal
453 return
454 elseif line !~ '^\s*$' || line =~ '^/\*'
455 " Not an empty line: Doesn't look like valid Pascal code.
456 " Or it looks like a Progress /* comment
457 break
458 endif
459 let lnum = lnum + 1
460 endw
461 setf progress
462endfunc
463
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100464func dist#ft#FTpp()
465 if exists("g:filetype_pp")
466 exe "setf " . g:filetype_pp
467 else
468 let line = getline(nextnonblank(1))
469 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
470 setf pascal
471 else
472 setf puppet
473 endif
474 endif
475endfunc
476
Bram Moolenaard09a2062017-11-11 15:37:45 +0100477func dist#ft#FTr()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100478 let max = line("$") > 50 ? 50 : line("$")
479
480 for n in range(1, max)
481 " Rebol is easy to recognize, check for that first
482 if getline(n) =~? '\<REBOL\>'
483 setf rebol
484 return
485 endif
486 endfor
487
488 for n in range(1, max)
489 " R has # comments
490 if getline(n) =~ '^\s*#'
491 setf r
492 return
493 endif
494 " Rexx has /* comments */
495 if getline(n) =~ '^\s*/\*'
496 setf rexx
497 return
498 endif
499 endfor
500
501 " Nothing recognized, use user default or assume Rexx
502 if exists("g:filetype_r")
503 exe "setf " . g:filetype_r
504 else
505 " Rexx used to be the default, but R appears to be much more popular.
506 setf r
507 endif
508endfunc
509
Bram Moolenaard09a2062017-11-11 15:37:45 +0100510func dist#ft#McSetf()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100511 " Rely on the file to start with a comment.
512 " MS message text files use ';', Sendmail files use '#' or 'dnl'
513 for lnum in range(1, min([line("$"), 20]))
514 let line = getline(lnum)
515 if line =~ '^\s*\(#\|dnl\)'
516 setf m4 " Sendmail .mc file
517 return
518 elseif line =~ '^\s*;'
519 setf msmessages " MS Message text file
520 return
521 endif
522 endfor
523 setf m4 " Default: Sendmail .mc file
524endfunc
525
526" Called from filetype.vim and scripts.vim.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100527func dist#ft#SetFileTypeSH(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100528 if did_filetype()
529 " Filetype was already detected
530 return
531 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100532 if expand("<amatch>") =~ g:ft_ignore_pat
533 return
534 endif
535 if a:name =~ '\<csh\>'
536 " Some .sh scripts contain #!/bin/csh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100537 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100538 return
539 elseif a:name =~ '\<tcsh\>'
540 " Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100541 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100542 return
543 elseif a:name =~ '\<zsh\>'
544 " Some .sh scripts contain #!/bin/zsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100545 call dist#ft#SetFileTypeShell("zsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100546 return
547 elseif a:name =~ '\<ksh\>'
548 let b:is_kornshell = 1
549 if exists("b:is_bash")
550 unlet b:is_bash
551 endif
552 if exists("b:is_sh")
553 unlet b:is_sh
554 endif
555 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
556 let b:is_bash = 1
557 if exists("b:is_kornshell")
558 unlet b:is_kornshell
559 endif
560 if exists("b:is_sh")
561 unlet b:is_sh
562 endif
563 elseif a:name =~ '\<sh\>'
564 let b:is_sh = 1
565 if exists("b:is_kornshell")
566 unlet b:is_kornshell
567 endif
568 if exists("b:is_bash")
569 unlet b:is_bash
570 endif
571 endif
Bram Moolenaard09a2062017-11-11 15:37:45 +0100572 call dist#ft#SetFileTypeShell("sh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100573endfunc
574
575" For shell-like file types, check for an "exec" command hidden in a comment,
576" as used for Tcl.
577" Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100578func dist#ft#SetFileTypeShell(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100579 if did_filetype()
580 " Filetype was already detected
581 return
582 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100583 if expand("<amatch>") =~ g:ft_ignore_pat
584 return
585 endif
586 let l = 2
587 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
588 " Skip empty and comment lines.
589 let l = l + 1
590 endwhile
591 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
592 " Found an "exec" line after a comment with continuation
593 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
594 if n =~ '\<tclsh\|\<wish'
595 setf tcl
596 return
597 endif
598 endif
599 exe "setf " . a:name
600endfunc
601
Bram Moolenaard09a2062017-11-11 15:37:45 +0100602func dist#ft#CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100603 if did_filetype()
604 " Filetype was already detected
605 return
606 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100607 if exists("g:filetype_csh")
Bram Moolenaard09a2062017-11-11 15:37:45 +0100608 call dist#ft#SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100609 elseif &shell =~ "tcsh"
Bram Moolenaard09a2062017-11-11 15:37:45 +0100610 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100611 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100612 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100613 endif
614endfunc
615
Bram Moolenaarcef73222017-11-09 21:05:31 +0100616let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100617func dist#ft#FTRules()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100618 let path = expand('<amatch>:p')
Bram Moolenaaraa9675a2020-08-17 21:57:09 +0200619 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100620 setf udevrules
621 return
622 endif
623 if path =~ '^/etc/ufw/'
624 setf conf " Better than hog
625 return
626 endif
627 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
628 setf javascript
629 return
630 endif
631 try
632 let config_lines = readfile('/etc/udev/udev.conf')
633 catch /^Vim\%((\a\+)\)\=:E484/
634 setf hog
635 return
636 endtry
637 let dir = expand('<amatch>:p:h')
638 for line in config_lines
639 if line =~ s:ft_rules_udev_rules_pattern
640 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
641 if dir == udev_rules
642 setf udevrules
643 endif
644 break
645 endif
646 endfor
647 setf hog
648endfunc
649
Bram Moolenaard09a2062017-11-11 15:37:45 +0100650func dist#ft#SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100651 if exists("g:filetype_sql")
652 exe "setf " . g:filetype_sql
653 else
654 setf sql
655 endif
656endfunc
657
658" If the file has an extension of 't' and is in a directory 't' or 'xt' then
659" it is almost certainly a Perl test file.
660" If the first line starts with '#' and contains 'perl' it's probably a Perl
661" file.
662" (Slow test) If a file contains a 'use' statement then it is almost certainly
663" a Perl file.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100664func dist#ft#FTperl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100665 let dirname = expand("%:p:h:t")
666 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
667 setf perl
668 return 1
669 endif
670 if getline(1)[0] == '#' && getline(1) =~ 'perl'
671 setf perl
672 return 1
673 endif
Bram Moolenaarf0b03c42017-12-17 17:17:07 +0100674 let save_cursor = getpos('.')
675 call cursor(1,1)
676 let has_use = search('^use\s\s*\k', 'c', 30)
677 call setpos('.', save_cursor)
678 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100679 setf perl
680 return 1
681 endif
682 return 0
683endfunc
684
685" Choose context, plaintex, or tex (LaTeX) based on these rules:
686" 1. Check the first line of the file for "%&<format>".
687" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200688" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100689func dist#ft#FTtex()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100690 let firstline = getline(1)
691 if firstline =~ '^%&\s*\a\+'
692 let format = tolower(matchstr(firstline, '\a\+'))
693 let format = substitute(format, 'pdf', '', '')
694 if format == 'tex'
695 let format = 'latex'
696 elseif format == 'plaintex'
697 let format = 'plain'
698 endif
699 elseif expand('%') =~ 'tex/context/.*/.*.tex'
700 let format = 'context'
701 else
702 " Default value, may be changed later:
703 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
704 " Save position, go to the top of the file, find first non-comment line.
705 let save_cursor = getpos('.')
706 call cursor(1,1)
707 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
708 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
709 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
710 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\>'
711 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
712 \ 'cnp', firstNC + 1000)
713 if kwline == 1 " lpat matched
714 let format = 'latex'
715 elseif kwline == 2 " cpat matched
716 let format = 'context'
717 endif " If neither matched, keep default set above.
718 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
719 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
720 " if cline > 0
721 " let format = 'context'
722 " endif
723 " if lline > 0 && (cline == 0 || cline > lline)
724 " let format = 'tex'
725 " endif
726 endif " firstNC
727 call setpos('.', save_cursor)
728 endif " firstline =~ '^%&\s*\a\+'
729
730 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
731 if format == 'plain'
732 setf plaintex
733 elseif format == 'context'
734 setf context
735 else " probably LaTeX
736 setf tex
737 endif
738 return
739endfunc
740
Bram Moolenaard09a2062017-11-11 15:37:45 +0100741func dist#ft#FTxml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100742 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100743 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100744 let line = getline(n)
745 " DocBook 4 or DocBook 5.
746 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
747 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
748 if is_docbook4 || is_docbook5
749 let b:docbk_type = "xml"
750 if is_docbook5
751 let b:docbk_ver = 5
752 else
753 let b:docbk_ver = 4
754 endif
755 setf docbk
756 return
757 endif
758 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
759 setf xbl
760 return
761 endif
762 let n += 1
763 endwhile
764 setf xml
765endfunc
766
Bram Moolenaard09a2062017-11-11 15:37:45 +0100767func dist#ft#FTy()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100768 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100769 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100770 let line = getline(n)
771 if line =~ '^\s*%'
772 setf yacc
773 return
774 endif
775 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
776 setf racc
777 return
778 endif
779 let n = n + 1
780 endwhile
781 setf yacc
782endfunc
783
Bram Moolenaard09a2062017-11-11 15:37:45 +0100784func dist#ft#Redif()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100785 let lnum = 1
786 while lnum <= 5 && lnum < line('$')
787 if getline(lnum) =~ "^\ctemplate-type:"
788 setf redif
789 return
790 endif
791 let lnum = lnum + 1
792 endwhile
793endfunc
794
795
796" Restore 'cpoptions'
797let &cpo = s:cpo_save
798unlet s:cpo_save