blob: 86c71fa52dba901bbe0b8e453b00e14be796bd89 [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 Moolenaarfd31be22022-01-16 14:46:06 +00004" Last Change: 2022 Jan 11
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
Bram Moolenaar6517f142022-01-21 14:55:13 +000070func dist#ft#FTbas()
71 if exists("g:filetype_bas")
72 exe "setf " . g:filetype_bas
73 return
74 endif
75
76 " most frequent FreeBASIC-specific keywords in distro files
77 let fb_keywords = '\c^\s*\%(extern\|var\|enum\|private\|scope\|union\|byref\|operator\|constructor\|delete\|namespace\|public\|property\|with\|destructor\|using\)\>\%(\s*[:=(]\)\@!'
78 let fb_preproc = '\c^\s*\%(#\a\+\|option\s\+\%(byval\|dynamic\|escape\|\%(no\)\=gosub\|nokeyword\|private\|static\)\>\)'
79 let fb_comment = "^\\s*/'"
80 " OPTION EXPLICIT, without the leading underscore, is common to many dialects
81 let qb64_preproc = '\c^\s*\%($\a\+\|option\s\+\%(_explicit\|_\=explicitarray\)\>\)'
82
83 let lines = getline(1, min([line("$"), 100]))
84
85 if match(lines, fb_preproc) > -1 || match(lines, fb_comment) > -1 || match(lines, fb_keywords) > -1
86 setf freebasic
87 elseif match(lines, qb64_preproc) > -1
88 setf qb64
89 elseif match(lines, '\cVB_Name\|Begin VB\.\(Form\|MDIForm\|UserControl\)') > -1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010090 setf vb
91 else
Bram Moolenaar6517f142022-01-21 14:55:13 +000092 setf basic
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010093 endif
94endfunc
95
Bram Moolenaard09a2062017-11-11 15:37:45 +010096func dist#ft#FTbtm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010097 if exists("g:dosbatch_syntax_for_btm") && g:dosbatch_syntax_for_btm
98 setf dosbatch
99 else
100 setf btm
101 endif
102endfunc
103
Bram Moolenaard09a2062017-11-11 15:37:45 +0100104func dist#ft#BindzoneCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100105 if getline(1).getline(2).getline(3).getline(4) =~ '^; <<>> DiG [0-9.]\+.* <<>>\|$ORIGIN\|$TTL\|IN\s\+SOA'
106 setf bindzone
107 elseif a:default != ''
108 exe 'setf ' . a:default
109 endif
110endfunc
111
Bram Moolenaard09a2062017-11-11 15:37:45 +0100112func dist#ft#FTlpc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100113 if exists("g:lpc_syntax_for_c")
114 let lnum = 1
115 while lnum <= 12
116 if getline(lnum) =~# '^\(//\|inherit\|private\|protected\|nosave\|string\|object\|mapping\|mixed\)'
117 setf lpc
118 return
119 endif
120 let lnum = lnum + 1
121 endwhile
122 endif
123 setf c
124endfunc
125
Bram Moolenaard09a2062017-11-11 15:37:45 +0100126func dist#ft#FTheader()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100127 if match(getline(1, min([line("$"), 200])), '^@\(interface\|end\|class\)') > -1
128 if exists("g:c_syntax_for_h")
129 setf objc
130 else
131 setf objcpp
132 endif
133 elseif exists("g:c_syntax_for_h")
134 setf c
135 elseif exists("g:ch_syntax_for_h")
136 setf ch
137 else
138 setf cpp
139 endif
140endfunc
141
142" This function checks if one of the first ten lines start with a '@'. In
143" that case it is probably a change file.
144" If the first line starts with # or ! it's probably a ch file.
Bram Moolenaarba3ff532018-11-04 14:45:49 +0100145" If a line has "main", "include", "//" or "/*" it's probably ch.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100146" Otherwise CHILL is assumed.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100147func dist#ft#FTchange()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100148 let lnum = 1
149 while lnum <= 10
150 if getline(lnum)[0] == '@'
151 setf change
152 return
153 endif
154 if lnum == 1 && (getline(1)[0] == '#' || getline(1)[0] == '!')
155 setf ch
156 return
157 endif
158 if getline(lnum) =~ "MODULE"
159 setf chill
160 return
161 endif
162 if getline(lnum) =~ 'main\s*(\|#\s*include\|//'
163 setf ch
164 return
165 endif
166 let lnum = lnum + 1
167 endwhile
168 setf chill
169endfunc
170
Bram Moolenaard09a2062017-11-11 15:37:45 +0100171func dist#ft#FTent()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100172 " This function checks for valid cl syntax in the first five lines.
173 " Look for either an opening comment, '#', or a block start, '{".
174 " If not found, assume SGML.
175 let lnum = 1
176 while lnum < 6
177 let line = getline(lnum)
178 if line =~ '^\s*[#{]'
179 setf cl
180 return
181 elseif line !~ '^\s*$'
182 " Not a blank line, not a comment, and not a block start,
183 " so doesn't look like valid cl code.
184 break
185 endif
186 let lnum = lnum + 1
187 endw
188 setf dtd
189endfunc
190
Austin Gatlinf3caeb62021-06-26 12:02:55 +0200191func dist#ft#ExCheck()
192 let lines = getline(1, min([line("$"), 100]))
193 if exists('g:filetype_euphoria')
194 exe 'setf ' . g:filetype_euphoria
195 elseif match(lines, '^--\|^ifdef\>\|^include\>') > -1
196 setf euphoria3
197 else
198 setf elixir
199 endif
200endfunc
201
Bram Moolenaard09a2062017-11-11 15:37:45 +0100202func dist#ft#EuphoriaCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100203 if exists('g:filetype_euphoria')
204 exe 'setf ' . g:filetype_euphoria
205 else
206 setf euphoria3
207 endif
208endfunc
209
Bram Moolenaard09a2062017-11-11 15:37:45 +0100210func dist#ft#DtraceCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100211 let lines = getline(1, min([line("$"), 100]))
212 if match(lines, '^module\>\|^import\>') > -1
213 " D files often start with a module and/or import statement.
214 setf d
215 elseif match(lines, '^#!\S\+dtrace\|#pragma\s\+D\s\+option\|:\S\{-}:\S\{-}:') > -1
216 setf dtrace
217 else
218 setf d
219 endif
220endfunc
221
Bram Moolenaard09a2062017-11-11 15:37:45 +0100222func dist#ft#FTe()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100223 if exists('g:filetype_euphoria')
224 exe 'setf ' . g:filetype_euphoria
225 else
226 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100227 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100228 if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$"
229 setf specman
230 return
231 endif
232 let n = n + 1
233 endwhile
234 setf eiffel
235 endif
236endfunc
237
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000238" Distinguish between Forth and F#.
239" Provided by Doug Kearns.
240func dist#ft#FTfs()
241 if exists("g:filetype_fs")
242 exe "setf " . g:filetype_fs
243 else
244 let line = getline(nextnonblank(1))
245 " comments and colon definitions
246 if line =~ '^\s*\.\=( ' || line =~ '^\s*\\G\= ' || line =~ '^\\$'
247 \ || line =~ '^\s*: \S'
248 setf forth
249 else
Bram Moolenaar53ba95e2021-11-30 13:02:58 +0000250 setf fsharp
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000251 endif
252 endif
253endfunc
254
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100255" Distinguish between HTML, XHTML and Django
Bram Moolenaard09a2062017-11-11 15:37:45 +0100256func dist#ft#FThtml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100257 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100258 while n < 10 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100259 if getline(n) =~ '\<DTD\s\+XHTML\s'
260 setf xhtml
261 return
262 endif
263 if getline(n) =~ '{%\s*\(extends\|block\|load\)\>\|{#\s\+'
264 setf htmldjango
265 return
266 endif
267 let n = n + 1
268 endwhile
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100269 setf FALLBACK html
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100270endfunc
271
272" Distinguish between standard IDL and MS-IDL
Bram Moolenaard09a2062017-11-11 15:37:45 +0100273func dist#ft#FTidl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100274 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100275 while n < 50 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100276 if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"'
277 setf msidl
278 return
279 endif
280 let n = n + 1
281 endwhile
282 setf idl
283endfunc
284
285" Distinguish between "default" and Cproto prototype file. */
Bram Moolenaard09a2062017-11-11 15:37:45 +0100286func dist#ft#ProtoCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100287 " Cproto files have a comment in the first line and a function prototype in
288 " the second line, it always ends in ";". Indent files may also have
289 " comments, thus we can't match comments to see the difference.
290 " IDL files can have a single ';' in the second line, require at least one
291 " chacter before the ';'.
292 if getline(2) =~ '.;$'
293 setf cpp
294 else
295 exe 'setf ' . a:default
296 endif
297endfunc
298
Bram Moolenaard09a2062017-11-11 15:37:45 +0100299func dist#ft#FTm()
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200300 if exists("g:filetype_m")
301 exe "setf " . g:filetype_m
302 return
303 endif
304
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200305 " excluding end(for|function|if|switch|while) common to Murphi
306 let octave_block_terminators = '\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>'
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200307
Doug Kearns7329cfa2021-11-26 13:01:41 +0000308 let objc_preprocessor = '^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>'
309
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100310 let n = 1
311 let saw_comment = 0 " Whether we've seen a multiline comment leader.
312 while n < 100
313 let line = getline(n)
314 if line =~ '^\s*/\*'
315 " /* ... */ is a comment in Objective C and Murphi, so we can't conclude
316 " it's either of them yet, but track this as a hint in case we don't see
317 " anything more definitive.
318 let saw_comment = 1
319 endif
Doug Kearns7329cfa2021-11-26 13:01:41 +0000320 if line =~ '^\s*//' || line =~ '^\s*@import\>' || line =~ objc_preprocessor
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100321 setf objc
322 return
323 endif
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200324 if line =~ '^\s*\%(#\|%!\)' || line =~ '^\s*unwind_protect\>' ||
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200325 \ line =~ '\%(^\|;\)\s*' .. octave_block_terminators
326 setf octave
327 return
328 endif
329 " TODO: could be Matlab or Octave
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100330 if line =~ '^\s*%'
331 setf matlab
332 return
333 endif
334 if line =~ '^\s*(\*'
335 setf mma
336 return
337 endif
338 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
339 setf murphi
340 return
341 endif
342 let n = n + 1
343 endwhile
344
345 if saw_comment
346 " We didn't see anything definitive, but this looks like either Objective C
347 " or Murphi based on the comment leader. Assume the former as it is more
348 " common.
349 setf objc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100350 else
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200351 " Default is Matlab
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100352 setf matlab
353 endif
354endfunc
355
Bram Moolenaard09a2062017-11-11 15:37:45 +0100356func dist#ft#FTmms()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100357 let n = 1
Bram Moolenaard7df2792020-01-02 21:34:42 +0100358 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100359 let line = getline(n)
360 if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
361 setf mmix
362 return
363 endif
364 if line =~ '^\s*#'
365 setf make
366 return
367 endif
368 let n = n + 1
369 endwhile
370 setf mmix
371endfunc
372
373" This function checks if one of the first five lines start with a dot. In
374" that case it is probably an nroff file: 'filetype' is set and 1 is returned.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100375func dist#ft#FTnroff()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100376 if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.'
377 setf nroff
378 return 1
379 endif
380 return 0
381endfunc
382
Bram Moolenaard09a2062017-11-11 15:37:45 +0100383func dist#ft#FTmm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100384 let n = 1
Bram Moolenaard1caa942020-04-10 22:10:56 +0200385 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100386 let line = getline(n)
387 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
388 setf objcpp
389 return
390 endif
391 let n = n + 1
392 endwhile
393 setf nroff
394endfunc
395
Bram Moolenaard09a2062017-11-11 15:37:45 +0100396func dist#ft#FTpl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100397 if exists("g:filetype_pl")
398 exe "setf " . g:filetype_pl
399 else
400 " recognize Prolog by specific text in the first non-empty line
401 " require a blank after the '%' because Perl uses "%list" and "%translate"
402 let l = getline(nextnonblank(1))
403 if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
404 setf prolog
405 else
406 setf perl
407 endif
408 endif
409endfunc
410
Bram Moolenaard09a2062017-11-11 15:37:45 +0100411func dist#ft#FTinc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100412 if exists("g:filetype_inc")
413 exe "setf " . g:filetype_inc
414 else
415 let lines = getline(1).getline(2).getline(3)
416 if lines =~? "perlscript"
417 setf aspperl
418 elseif lines =~ "<%"
419 setf aspvbs
420 elseif lines =~ "<?"
421 setf php
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100422 " Pascal supports // comments but they're vary rarely used for file
423 " headers so assume POV-Ray
424 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
425 setf pascal
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100426 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100427 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100428 if exists("b:asmsyntax")
429 exe "setf " . fnameescape(b:asmsyntax)
430 else
431 setf pov
432 endif
433 endif
434 endif
435endfunc
436
Bram Moolenaard09a2062017-11-11 15:37:45 +0100437func dist#ft#FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100438 if exists("g:filetype_w")
439 exe "setf " . g:filetype_w
440 return
441 endif
442 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
443 setf progress
444 else
445 setf cweb
446 endif
447endfunc
448
Bram Moolenaard09a2062017-11-11 15:37:45 +0100449func dist#ft#FTprogress_asm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100450 if exists("g:filetype_i")
451 exe "setf " . g:filetype_i
452 return
453 endif
454 " This function checks for an assembly comment the first ten lines.
455 " If not found, assume Progress.
456 let lnum = 1
457 while lnum <= 10 && lnum < line('$')
458 let line = getline(lnum)
459 if line =~ '^\s*;' || line =~ '^\*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100460 call dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100461 return
462 elseif line !~ '^\s*$' || line =~ '^/\*'
463 " Not an empty line: Doesn't look like valid assembly code.
464 " Or it looks like a Progress /* comment
465 break
466 endif
467 let lnum = lnum + 1
468 endw
469 setf progress
470endfunc
471
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100472let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
473let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
474
Bram Moolenaard09a2062017-11-11 15:37:45 +0100475func dist#ft#FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100476 if exists("g:filetype_p")
477 exe "setf " . g:filetype_p
478 return
479 endif
480 " This function checks for valid Pascal syntax in the first ten lines.
481 " Look for either an opening comment or a program start.
482 " If not found, assume Progress.
483 let lnum = 1
484 while lnum <= 10 && lnum < line('$')
485 let line = getline(lnum)
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100486 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100487 setf pascal
488 return
489 elseif line !~ '^\s*$' || line =~ '^/\*'
490 " Not an empty line: Doesn't look like valid Pascal code.
491 " Or it looks like a Progress /* comment
492 break
493 endif
494 let lnum = lnum + 1
495 endw
496 setf progress
497endfunc
498
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100499func dist#ft#FTpp()
500 if exists("g:filetype_pp")
501 exe "setf " . g:filetype_pp
502 else
503 let line = getline(nextnonblank(1))
504 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
505 setf pascal
506 else
507 setf puppet
508 endif
509 endif
510endfunc
511
Bram Moolenaard09a2062017-11-11 15:37:45 +0100512func dist#ft#FTr()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100513 let max = line("$") > 50 ? 50 : line("$")
514
515 for n in range(1, max)
516 " Rebol is easy to recognize, check for that first
517 if getline(n) =~? '\<REBOL\>'
518 setf rebol
519 return
520 endif
521 endfor
522
523 for n in range(1, max)
524 " R has # comments
525 if getline(n) =~ '^\s*#'
526 setf r
527 return
528 endif
529 " Rexx has /* comments */
530 if getline(n) =~ '^\s*/\*'
531 setf rexx
532 return
533 endif
534 endfor
535
536 " Nothing recognized, use user default or assume Rexx
537 if exists("g:filetype_r")
538 exe "setf " . g:filetype_r
539 else
540 " Rexx used to be the default, but R appears to be much more popular.
541 setf r
542 endif
543endfunc
544
Bram Moolenaard09a2062017-11-11 15:37:45 +0100545func dist#ft#McSetf()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100546 " Rely on the file to start with a comment.
547 " MS message text files use ';', Sendmail files use '#' or 'dnl'
548 for lnum in range(1, min([line("$"), 20]))
549 let line = getline(lnum)
550 if line =~ '^\s*\(#\|dnl\)'
551 setf m4 " Sendmail .mc file
552 return
553 elseif line =~ '^\s*;'
554 setf msmessages " MS Message text file
555 return
556 endif
557 endfor
558 setf m4 " Default: Sendmail .mc file
559endfunc
560
561" Called from filetype.vim and scripts.vim.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100562func dist#ft#SetFileTypeSH(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100563 if did_filetype()
564 " Filetype was already detected
565 return
566 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100567 if expand("<amatch>") =~ g:ft_ignore_pat
568 return
569 endif
570 if a:name =~ '\<csh\>'
571 " Some .sh scripts contain #!/bin/csh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100572 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100573 return
574 elseif a:name =~ '\<tcsh\>'
575 " Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100576 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100577 return
578 elseif a:name =~ '\<zsh\>'
579 " Some .sh scripts contain #!/bin/zsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100580 call dist#ft#SetFileTypeShell("zsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100581 return
582 elseif a:name =~ '\<ksh\>'
583 let b:is_kornshell = 1
584 if exists("b:is_bash")
585 unlet b:is_bash
586 endif
587 if exists("b:is_sh")
588 unlet b:is_sh
589 endif
590 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
591 let b:is_bash = 1
592 if exists("b:is_kornshell")
593 unlet b:is_kornshell
594 endif
595 if exists("b:is_sh")
596 unlet b:is_sh
597 endif
598 elseif a:name =~ '\<sh\>'
599 let b:is_sh = 1
600 if exists("b:is_kornshell")
601 unlet b:is_kornshell
602 endif
603 if exists("b:is_bash")
604 unlet b:is_bash
605 endif
606 endif
Bram Moolenaard09a2062017-11-11 15:37:45 +0100607 call dist#ft#SetFileTypeShell("sh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100608endfunc
609
610" For shell-like file types, check for an "exec" command hidden in a comment,
611" as used for Tcl.
612" Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100613func dist#ft#SetFileTypeShell(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100614 if did_filetype()
615 " Filetype was already detected
616 return
617 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100618 if expand("<amatch>") =~ g:ft_ignore_pat
619 return
620 endif
621 let l = 2
622 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
623 " Skip empty and comment lines.
624 let l = l + 1
625 endwhile
626 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
627 " Found an "exec" line after a comment with continuation
628 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
629 if n =~ '\<tclsh\|\<wish'
630 setf tcl
631 return
632 endif
633 endif
634 exe "setf " . a:name
635endfunc
636
Bram Moolenaard09a2062017-11-11 15:37:45 +0100637func dist#ft#CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100638 if did_filetype()
639 " Filetype was already detected
640 return
641 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100642 if exists("g:filetype_csh")
Bram Moolenaard09a2062017-11-11 15:37:45 +0100643 call dist#ft#SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100644 elseif &shell =~ "tcsh"
Bram Moolenaard09a2062017-11-11 15:37:45 +0100645 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100646 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100647 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100648 endif
649endfunc
650
Bram Moolenaarcef73222017-11-09 21:05:31 +0100651let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100652func dist#ft#FTRules()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100653 let path = expand('<amatch>:p')
Bram Moolenaaraa9675a2020-08-17 21:57:09 +0200654 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100655 setf udevrules
656 return
657 endif
658 if path =~ '^/etc/ufw/'
659 setf conf " Better than hog
660 return
661 endif
662 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
663 setf javascript
664 return
665 endif
666 try
667 let config_lines = readfile('/etc/udev/udev.conf')
668 catch /^Vim\%((\a\+)\)\=:E484/
669 setf hog
670 return
671 endtry
672 let dir = expand('<amatch>:p:h')
673 for line in config_lines
674 if line =~ s:ft_rules_udev_rules_pattern
675 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
676 if dir == udev_rules
677 setf udevrules
678 endif
679 break
680 endif
681 endfor
682 setf hog
683endfunc
684
Bram Moolenaard09a2062017-11-11 15:37:45 +0100685func dist#ft#SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100686 if exists("g:filetype_sql")
687 exe "setf " . g:filetype_sql
688 else
689 setf sql
690 endif
691endfunc
692
693" If the file has an extension of 't' and is in a directory 't' or 'xt' then
694" it is almost certainly a Perl test file.
695" If the first line starts with '#' and contains 'perl' it's probably a Perl
696" file.
697" (Slow test) If a file contains a 'use' statement then it is almost certainly
698" a Perl file.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100699func dist#ft#FTperl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100700 let dirname = expand("%:p:h:t")
701 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
702 setf perl
703 return 1
704 endif
705 if getline(1)[0] == '#' && getline(1) =~ 'perl'
706 setf perl
707 return 1
708 endif
Bram Moolenaarf0b03c42017-12-17 17:17:07 +0100709 let save_cursor = getpos('.')
710 call cursor(1,1)
711 let has_use = search('^use\s\s*\k', 'c', 30)
712 call setpos('.', save_cursor)
713 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100714 setf perl
715 return 1
716 endif
717 return 0
718endfunc
719
720" Choose context, plaintex, or tex (LaTeX) based on these rules:
721" 1. Check the first line of the file for "%&<format>".
722" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200723" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100724func dist#ft#FTtex()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100725 let firstline = getline(1)
726 if firstline =~ '^%&\s*\a\+'
727 let format = tolower(matchstr(firstline, '\a\+'))
728 let format = substitute(format, 'pdf', '', '')
729 if format == 'tex'
730 let format = 'latex'
731 elseif format == 'plaintex'
732 let format = 'plain'
733 endif
734 elseif expand('%') =~ 'tex/context/.*/.*.tex'
735 let format = 'context'
736 else
737 " Default value, may be changed later:
738 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
739 " Save position, go to the top of the file, find first non-comment line.
740 let save_cursor = getpos('.')
741 call cursor(1,1)
742 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
743 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
744 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
745 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\>'
746 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
747 \ 'cnp', firstNC + 1000)
748 if kwline == 1 " lpat matched
749 let format = 'latex'
750 elseif kwline == 2 " cpat matched
751 let format = 'context'
752 endif " If neither matched, keep default set above.
753 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
754 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
755 " if cline > 0
756 " let format = 'context'
757 " endif
758 " if lline > 0 && (cline == 0 || cline > lline)
759 " let format = 'tex'
760 " endif
761 endif " firstNC
762 call setpos('.', save_cursor)
763 endif " firstline =~ '^%&\s*\a\+'
764
765 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
766 if format == 'plain'
767 setf plaintex
768 elseif format == 'context'
769 setf context
770 else " probably LaTeX
771 setf tex
772 endif
773 return
774endfunc
775
Bram Moolenaard09a2062017-11-11 15:37:45 +0100776func dist#ft#FTxml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100777 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100778 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100779 let line = getline(n)
780 " DocBook 4 or DocBook 5.
781 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
782 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
783 if is_docbook4 || is_docbook5
784 let b:docbk_type = "xml"
785 if is_docbook5
786 let b:docbk_ver = 5
787 else
788 let b:docbk_ver = 4
789 endif
790 setf docbk
791 return
792 endif
793 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
794 setf xbl
795 return
796 endif
797 let n += 1
798 endwhile
799 setf xml
800endfunc
801
Bram Moolenaard09a2062017-11-11 15:37:45 +0100802func dist#ft#FTy()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100803 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100804 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100805 let line = getline(n)
806 if line =~ '^\s*%'
807 setf yacc
808 return
809 endif
810 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
811 setf racc
812 return
813 endif
814 let n = n + 1
815 endwhile
816 setf yacc
817endfunc
818
Bram Moolenaard09a2062017-11-11 15:37:45 +0100819func dist#ft#Redif()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100820 let lnum = 1
821 while lnum <= 5 && lnum < line('$')
822 if getline(lnum) =~ "^\ctemplate-type:"
823 setf redif
824 return
825 endif
826 let lnum = lnum + 1
827 endwhile
828endfunc
829
James McCoy647ab4c2021-12-17 20:52:57 +0000830" This function is called for all files under */debian/patches/*, make sure not
831" to non-dep3patch files, such as README and other text files.
832func dist#ft#Dep3patch()
833 if expand('%:t') ==# 'series'
834 return
835 endif
836
837 for ln in getline(1, 100)
838 if ln =~# '^\%(Description\|Subject\|Origin\|Bug\|Forwarded\|Author\|From\|Reviewed-by\|Acked-by\|Last-Updated\|Applied-Upstream\):'
839 setf dep3patch
840 return
841 elseif ln =~# '^---'
842 " end of headers found. stop processing
843 return
844 endif
845 endfor
846endfunc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100847
Elwardi2284f6c2022-01-11 18:14:23 +0000848" This function checks the first 15 lines for appearance of 'FoamFile'
849" and then 'object' in a following line.
850" In that case, it's probably an OpenFOAM file
851func dist#ft#FTfoam()
852 let ffile = 0
853 let lnum = 1
854 while lnum <= 15
855 if getline(lnum) =~# '^FoamFile'
856 let ffile = 1
857 elseif ffile == 1 && getline(lnum) =~# '^\s*object'
858 setf foam
859 return
860 endif
861 let lnum = lnum + 1
862 endwhile
863endfunc
864
=?UTF-8?q?Dundar=20G=C3=B6c?=bd8168c2022-01-28 14:15:09 +0000865" Determine if a *.tf file is TF mud client or terraform
866func dist#ft#FTtf()
867 let numberOfLines = line('$')
868 for i in range(1, numberOfLines)
869 let currentLine = trim(getline(i))
870 let firstCharacter = currentLine[0]
871 if firstCharacter !=? ";" && firstCharacter !=? "/" && firstCharacter !=? ""
872 setf terraform
873 return
874 endif
875 endfor
876 setf tf
877endfunc
878
879
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100880" Restore 'cpoptions'
881let &cpo = s:cpo_save
882unlet s:cpo_save