blob: f513a2ac8fb85a0d4e9082c853339b571b66d424 [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 Moolenaarc4573eb2022-01-31 15:40:56 +00004" Last Change: 2022 Jan 31
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 Moolenaarc4573eb2022-01-31 15:40:56 +000070func dist#ft#FTbas(alt = '')
Bram Moolenaar6517f142022-01-21 14:55:13 +000071 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
Bram Moolenaarc4573eb2022-01-31 15:40:56 +000091 elseif a:alt != ''
92 exe 'setf ' .. a:alt
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010093 else
Bram Moolenaar6517f142022-01-21 14:55:13 +000094 setf basic
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010095 endif
96endfunc
97
Bram Moolenaard09a2062017-11-11 15:37:45 +010098func dist#ft#FTbtm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010099 if exists("g:dosbatch_syntax_for_btm") && g:dosbatch_syntax_for_btm
100 setf dosbatch
101 else
102 setf btm
103 endif
104endfunc
105
Bram Moolenaard09a2062017-11-11 15:37:45 +0100106func dist#ft#BindzoneCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100107 if getline(1).getline(2).getline(3).getline(4) =~ '^; <<>> DiG [0-9.]\+.* <<>>\|$ORIGIN\|$TTL\|IN\s\+SOA'
108 setf bindzone
109 elseif a:default != ''
110 exe 'setf ' . a:default
111 endif
112endfunc
113
Bram Moolenaard09a2062017-11-11 15:37:45 +0100114func dist#ft#FTlpc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100115 if exists("g:lpc_syntax_for_c")
116 let lnum = 1
117 while lnum <= 12
118 if getline(lnum) =~# '^\(//\|inherit\|private\|protected\|nosave\|string\|object\|mapping\|mixed\)'
119 setf lpc
120 return
121 endif
122 let lnum = lnum + 1
123 endwhile
124 endif
125 setf c
126endfunc
127
Bram Moolenaard09a2062017-11-11 15:37:45 +0100128func dist#ft#FTheader()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100129 if match(getline(1, min([line("$"), 200])), '^@\(interface\|end\|class\)') > -1
130 if exists("g:c_syntax_for_h")
131 setf objc
132 else
133 setf objcpp
134 endif
135 elseif exists("g:c_syntax_for_h")
136 setf c
137 elseif exists("g:ch_syntax_for_h")
138 setf ch
139 else
140 setf cpp
141 endif
142endfunc
143
144" This function checks if one of the first ten lines start with a '@'. In
145" that case it is probably a change file.
146" If the first line starts with # or ! it's probably a ch file.
Bram Moolenaarba3ff532018-11-04 14:45:49 +0100147" If a line has "main", "include", "//" or "/*" it's probably ch.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100148" Otherwise CHILL is assumed.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100149func dist#ft#FTchange()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100150 let lnum = 1
151 while lnum <= 10
152 if getline(lnum)[0] == '@'
153 setf change
154 return
155 endif
156 if lnum == 1 && (getline(1)[0] == '#' || getline(1)[0] == '!')
157 setf ch
158 return
159 endif
160 if getline(lnum) =~ "MODULE"
161 setf chill
162 return
163 endif
164 if getline(lnum) =~ 'main\s*(\|#\s*include\|//'
165 setf ch
166 return
167 endif
168 let lnum = lnum + 1
169 endwhile
170 setf chill
171endfunc
172
Bram Moolenaard09a2062017-11-11 15:37:45 +0100173func dist#ft#FTent()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100174 " This function checks for valid cl syntax in the first five lines.
175 " Look for either an opening comment, '#', or a block start, '{".
176 " If not found, assume SGML.
177 let lnum = 1
178 while lnum < 6
179 let line = getline(lnum)
180 if line =~ '^\s*[#{]'
181 setf cl
182 return
183 elseif line !~ '^\s*$'
184 " Not a blank line, not a comment, and not a block start,
185 " so doesn't look like valid cl code.
186 break
187 endif
188 let lnum = lnum + 1
189 endw
190 setf dtd
191endfunc
192
Austin Gatlinf3caeb62021-06-26 12:02:55 +0200193func dist#ft#ExCheck()
194 let lines = getline(1, min([line("$"), 100]))
195 if exists('g:filetype_euphoria')
196 exe 'setf ' . g:filetype_euphoria
197 elseif match(lines, '^--\|^ifdef\>\|^include\>') > -1
198 setf euphoria3
199 else
200 setf elixir
201 endif
202endfunc
203
Bram Moolenaard09a2062017-11-11 15:37:45 +0100204func dist#ft#EuphoriaCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100205 if exists('g:filetype_euphoria')
206 exe 'setf ' . g:filetype_euphoria
207 else
208 setf euphoria3
209 endif
210endfunc
211
Bram Moolenaard09a2062017-11-11 15:37:45 +0100212func dist#ft#DtraceCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100213 let lines = getline(1, min([line("$"), 100]))
214 if match(lines, '^module\>\|^import\>') > -1
215 " D files often start with a module and/or import statement.
216 setf d
217 elseif match(lines, '^#!\S\+dtrace\|#pragma\s\+D\s\+option\|:\S\{-}:\S\{-}:') > -1
218 setf dtrace
219 else
220 setf d
221 endif
222endfunc
223
Bram Moolenaard09a2062017-11-11 15:37:45 +0100224func dist#ft#FTe()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100225 if exists('g:filetype_euphoria')
226 exe 'setf ' . g:filetype_euphoria
227 else
228 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100229 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100230 if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$"
231 setf specman
232 return
233 endif
234 let n = n + 1
235 endwhile
236 setf eiffel
237 endif
238endfunc
239
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000240" Distinguish between Forth and F#.
241" Provided by Doug Kearns.
242func dist#ft#FTfs()
243 if exists("g:filetype_fs")
244 exe "setf " . g:filetype_fs
245 else
246 let line = getline(nextnonblank(1))
247 " comments and colon definitions
248 if line =~ '^\s*\.\=( ' || line =~ '^\s*\\G\= ' || line =~ '^\\$'
249 \ || line =~ '^\s*: \S'
250 setf forth
251 else
Bram Moolenaar53ba95e2021-11-30 13:02:58 +0000252 setf fsharp
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000253 endif
254 endif
255endfunc
256
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100257" Distinguish between HTML, XHTML and Django
Bram Moolenaard09a2062017-11-11 15:37:45 +0100258func dist#ft#FThtml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100259 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100260 while n < 10 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100261 if getline(n) =~ '\<DTD\s\+XHTML\s'
262 setf xhtml
263 return
264 endif
265 if getline(n) =~ '{%\s*\(extends\|block\|load\)\>\|{#\s\+'
266 setf htmldjango
267 return
268 endif
269 let n = n + 1
270 endwhile
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100271 setf FALLBACK html
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100272endfunc
273
274" Distinguish between standard IDL and MS-IDL
Bram Moolenaard09a2062017-11-11 15:37:45 +0100275func dist#ft#FTidl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100276 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100277 while n < 50 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100278 if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"'
279 setf msidl
280 return
281 endif
282 let n = n + 1
283 endwhile
284 setf idl
285endfunc
286
287" Distinguish between "default" and Cproto prototype file. */
Bram Moolenaard09a2062017-11-11 15:37:45 +0100288func dist#ft#ProtoCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100289 " Cproto files have a comment in the first line and a function prototype in
290 " the second line, it always ends in ";". Indent files may also have
291 " comments, thus we can't match comments to see the difference.
292 " IDL files can have a single ';' in the second line, require at least one
293 " chacter before the ';'.
294 if getline(2) =~ '.;$'
295 setf cpp
296 else
297 exe 'setf ' . a:default
298 endif
299endfunc
300
Bram Moolenaard09a2062017-11-11 15:37:45 +0100301func dist#ft#FTm()
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200302 if exists("g:filetype_m")
303 exe "setf " . g:filetype_m
304 return
305 endif
306
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200307 " excluding end(for|function|if|switch|while) common to Murphi
308 let octave_block_terminators = '\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>'
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200309
Doug Kearns7329cfa2021-11-26 13:01:41 +0000310 let objc_preprocessor = '^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>'
311
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100312 let n = 1
313 let saw_comment = 0 " Whether we've seen a multiline comment leader.
314 while n < 100
315 let line = getline(n)
316 if line =~ '^\s*/\*'
317 " /* ... */ is a comment in Objective C and Murphi, so we can't conclude
318 " it's either of them yet, but track this as a hint in case we don't see
319 " anything more definitive.
320 let saw_comment = 1
321 endif
Doug Kearns7329cfa2021-11-26 13:01:41 +0000322 if line =~ '^\s*//' || line =~ '^\s*@import\>' || line =~ objc_preprocessor
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100323 setf objc
324 return
325 endif
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200326 if line =~ '^\s*\%(#\|%!\)' || line =~ '^\s*unwind_protect\>' ||
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200327 \ line =~ '\%(^\|;\)\s*' .. octave_block_terminators
328 setf octave
329 return
330 endif
331 " TODO: could be Matlab or Octave
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100332 if line =~ '^\s*%'
333 setf matlab
334 return
335 endif
336 if line =~ '^\s*(\*'
337 setf mma
338 return
339 endif
340 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
341 setf murphi
342 return
343 endif
344 let n = n + 1
345 endwhile
346
347 if saw_comment
348 " We didn't see anything definitive, but this looks like either Objective C
349 " or Murphi based on the comment leader. Assume the former as it is more
350 " common.
351 setf objc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100352 else
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200353 " Default is Matlab
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100354 setf matlab
355 endif
356endfunc
357
Bram Moolenaard09a2062017-11-11 15:37:45 +0100358func dist#ft#FTmms()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100359 let n = 1
Bram Moolenaard7df2792020-01-02 21:34:42 +0100360 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100361 let line = getline(n)
362 if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
363 setf mmix
364 return
365 endif
366 if line =~ '^\s*#'
367 setf make
368 return
369 endif
370 let n = n + 1
371 endwhile
372 setf mmix
373endfunc
374
375" This function checks if one of the first five lines start with a dot. In
376" that case it is probably an nroff file: 'filetype' is set and 1 is returned.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100377func dist#ft#FTnroff()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100378 if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.'
379 setf nroff
380 return 1
381 endif
382 return 0
383endfunc
384
Bram Moolenaard09a2062017-11-11 15:37:45 +0100385func dist#ft#FTmm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100386 let n = 1
Bram Moolenaard1caa942020-04-10 22:10:56 +0200387 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100388 let line = getline(n)
389 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
390 setf objcpp
391 return
392 endif
393 let n = n + 1
394 endwhile
395 setf nroff
396endfunc
397
Bram Moolenaard09a2062017-11-11 15:37:45 +0100398func dist#ft#FTpl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100399 if exists("g:filetype_pl")
400 exe "setf " . g:filetype_pl
401 else
402 " recognize Prolog by specific text in the first non-empty line
403 " require a blank after the '%' because Perl uses "%list" and "%translate"
404 let l = getline(nextnonblank(1))
405 if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
406 setf prolog
407 else
408 setf perl
409 endif
410 endif
411endfunc
412
Bram Moolenaard09a2062017-11-11 15:37:45 +0100413func dist#ft#FTinc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100414 if exists("g:filetype_inc")
415 exe "setf " . g:filetype_inc
416 else
417 let lines = getline(1).getline(2).getline(3)
418 if lines =~? "perlscript"
419 setf aspperl
420 elseif lines =~ "<%"
421 setf aspvbs
422 elseif lines =~ "<?"
423 setf php
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100424 " Pascal supports // comments but they're vary rarely used for file
425 " headers so assume POV-Ray
426 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
427 setf pascal
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100428 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100429 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100430 if exists("b:asmsyntax")
431 exe "setf " . fnameescape(b:asmsyntax)
432 else
433 setf pov
434 endif
435 endif
436 endif
437endfunc
438
Bram Moolenaard09a2062017-11-11 15:37:45 +0100439func dist#ft#FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100440 if exists("g:filetype_w")
441 exe "setf " . g:filetype_w
442 return
443 endif
444 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
445 setf progress
446 else
447 setf cweb
448 endif
449endfunc
450
Bram Moolenaard09a2062017-11-11 15:37:45 +0100451func dist#ft#FTprogress_asm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100452 if exists("g:filetype_i")
453 exe "setf " . g:filetype_i
454 return
455 endif
456 " This function checks for an assembly comment the first ten lines.
457 " If not found, assume Progress.
458 let lnum = 1
459 while lnum <= 10 && lnum < line('$')
460 let line = getline(lnum)
461 if line =~ '^\s*;' || line =~ '^\*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100462 call dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100463 return
464 elseif line !~ '^\s*$' || line =~ '^/\*'
465 " Not an empty line: Doesn't look like valid assembly code.
466 " Or it looks like a Progress /* comment
467 break
468 endif
469 let lnum = lnum + 1
470 endw
471 setf progress
472endfunc
473
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100474let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
475let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
476
Bram Moolenaard09a2062017-11-11 15:37:45 +0100477func dist#ft#FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100478 if exists("g:filetype_p")
479 exe "setf " . g:filetype_p
480 return
481 endif
482 " This function checks for valid Pascal syntax in the first ten lines.
483 " Look for either an opening comment or a program start.
484 " If not found, assume Progress.
485 let lnum = 1
486 while lnum <= 10 && lnum < line('$')
487 let line = getline(lnum)
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100488 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100489 setf pascal
490 return
491 elseif line !~ '^\s*$' || line =~ '^/\*'
492 " Not an empty line: Doesn't look like valid Pascal code.
493 " Or it looks like a Progress /* comment
494 break
495 endif
496 let lnum = lnum + 1
497 endw
498 setf progress
499endfunc
500
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100501func dist#ft#FTpp()
502 if exists("g:filetype_pp")
503 exe "setf " . g:filetype_pp
504 else
505 let line = getline(nextnonblank(1))
506 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
507 setf pascal
508 else
509 setf puppet
510 endif
511 endif
512endfunc
513
Bram Moolenaard09a2062017-11-11 15:37:45 +0100514func dist#ft#FTr()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100515 let max = line("$") > 50 ? 50 : line("$")
516
517 for n in range(1, max)
518 " Rebol is easy to recognize, check for that first
519 if getline(n) =~? '\<REBOL\>'
520 setf rebol
521 return
522 endif
523 endfor
524
525 for n in range(1, max)
526 " R has # comments
527 if getline(n) =~ '^\s*#'
528 setf r
529 return
530 endif
531 " Rexx has /* comments */
532 if getline(n) =~ '^\s*/\*'
533 setf rexx
534 return
535 endif
536 endfor
537
538 " Nothing recognized, use user default or assume Rexx
539 if exists("g:filetype_r")
540 exe "setf " . g:filetype_r
541 else
542 " Rexx used to be the default, but R appears to be much more popular.
543 setf r
544 endif
545endfunc
546
Bram Moolenaard09a2062017-11-11 15:37:45 +0100547func dist#ft#McSetf()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100548 " Rely on the file to start with a comment.
549 " MS message text files use ';', Sendmail files use '#' or 'dnl'
550 for lnum in range(1, min([line("$"), 20]))
551 let line = getline(lnum)
552 if line =~ '^\s*\(#\|dnl\)'
553 setf m4 " Sendmail .mc file
554 return
555 elseif line =~ '^\s*;'
556 setf msmessages " MS Message text file
557 return
558 endif
559 endfor
560 setf m4 " Default: Sendmail .mc file
561endfunc
562
563" Called from filetype.vim and scripts.vim.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100564func dist#ft#SetFileTypeSH(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100565 if did_filetype()
566 " Filetype was already detected
567 return
568 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100569 if expand("<amatch>") =~ g:ft_ignore_pat
570 return
571 endif
572 if a:name =~ '\<csh\>'
573 " Some .sh scripts contain #!/bin/csh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100574 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100575 return
576 elseif a:name =~ '\<tcsh\>'
577 " Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100578 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100579 return
580 elseif a:name =~ '\<zsh\>'
581 " Some .sh scripts contain #!/bin/zsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100582 call dist#ft#SetFileTypeShell("zsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100583 return
584 elseif a:name =~ '\<ksh\>'
585 let b:is_kornshell = 1
586 if exists("b:is_bash")
587 unlet b:is_bash
588 endif
589 if exists("b:is_sh")
590 unlet b:is_sh
591 endif
592 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
593 let b:is_bash = 1
594 if exists("b:is_kornshell")
595 unlet b:is_kornshell
596 endif
597 if exists("b:is_sh")
598 unlet b:is_sh
599 endif
600 elseif a:name =~ '\<sh\>'
601 let b:is_sh = 1
602 if exists("b:is_kornshell")
603 unlet b:is_kornshell
604 endif
605 if exists("b:is_bash")
606 unlet b:is_bash
607 endif
608 endif
Bram Moolenaard09a2062017-11-11 15:37:45 +0100609 call dist#ft#SetFileTypeShell("sh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100610endfunc
611
612" For shell-like file types, check for an "exec" command hidden in a comment,
613" as used for Tcl.
614" Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100615func dist#ft#SetFileTypeShell(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100616 if did_filetype()
617 " Filetype was already detected
618 return
619 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100620 if expand("<amatch>") =~ g:ft_ignore_pat
621 return
622 endif
623 let l = 2
624 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
625 " Skip empty and comment lines.
626 let l = l + 1
627 endwhile
628 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
629 " Found an "exec" line after a comment with continuation
630 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
631 if n =~ '\<tclsh\|\<wish'
632 setf tcl
633 return
634 endif
635 endif
636 exe "setf " . a:name
637endfunc
638
Bram Moolenaard09a2062017-11-11 15:37:45 +0100639func dist#ft#CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100640 if did_filetype()
641 " Filetype was already detected
642 return
643 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100644 if exists("g:filetype_csh")
Bram Moolenaard09a2062017-11-11 15:37:45 +0100645 call dist#ft#SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100646 elseif &shell =~ "tcsh"
Bram Moolenaard09a2062017-11-11 15:37:45 +0100647 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100648 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100649 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100650 endif
651endfunc
652
Bram Moolenaarcef73222017-11-09 21:05:31 +0100653let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100654func dist#ft#FTRules()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100655 let path = expand('<amatch>:p')
Bram Moolenaaraa9675a2020-08-17 21:57:09 +0200656 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100657 setf udevrules
658 return
659 endif
660 if path =~ '^/etc/ufw/'
661 setf conf " Better than hog
662 return
663 endif
664 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
665 setf javascript
666 return
667 endif
668 try
669 let config_lines = readfile('/etc/udev/udev.conf')
670 catch /^Vim\%((\a\+)\)\=:E484/
671 setf hog
672 return
673 endtry
674 let dir = expand('<amatch>:p:h')
675 for line in config_lines
676 if line =~ s:ft_rules_udev_rules_pattern
677 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
678 if dir == udev_rules
679 setf udevrules
680 endif
681 break
682 endif
683 endfor
684 setf hog
685endfunc
686
Bram Moolenaard09a2062017-11-11 15:37:45 +0100687func dist#ft#SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100688 if exists("g:filetype_sql")
689 exe "setf " . g:filetype_sql
690 else
691 setf sql
692 endif
693endfunc
694
695" If the file has an extension of 't' and is in a directory 't' or 'xt' then
696" it is almost certainly a Perl test file.
697" If the first line starts with '#' and contains 'perl' it's probably a Perl
698" file.
699" (Slow test) If a file contains a 'use' statement then it is almost certainly
700" a Perl file.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100701func dist#ft#FTperl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100702 let dirname = expand("%:p:h:t")
703 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
704 setf perl
705 return 1
706 endif
707 if getline(1)[0] == '#' && getline(1) =~ 'perl'
708 setf perl
709 return 1
710 endif
Bram Moolenaarf0b03c42017-12-17 17:17:07 +0100711 let save_cursor = getpos('.')
712 call cursor(1,1)
713 let has_use = search('^use\s\s*\k', 'c', 30)
714 call setpos('.', save_cursor)
715 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100716 setf perl
717 return 1
718 endif
719 return 0
720endfunc
721
722" Choose context, plaintex, or tex (LaTeX) based on these rules:
723" 1. Check the first line of the file for "%&<format>".
724" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200725" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100726func dist#ft#FTtex()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100727 let firstline = getline(1)
728 if firstline =~ '^%&\s*\a\+'
729 let format = tolower(matchstr(firstline, '\a\+'))
730 let format = substitute(format, 'pdf', '', '')
731 if format == 'tex'
732 let format = 'latex'
733 elseif format == 'plaintex'
734 let format = 'plain'
735 endif
736 elseif expand('%') =~ 'tex/context/.*/.*.tex'
737 let format = 'context'
738 else
739 " Default value, may be changed later:
740 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
741 " Save position, go to the top of the file, find first non-comment line.
742 let save_cursor = getpos('.')
743 call cursor(1,1)
744 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
745 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
746 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
747 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\>'
748 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
749 \ 'cnp', firstNC + 1000)
750 if kwline == 1 " lpat matched
751 let format = 'latex'
752 elseif kwline == 2 " cpat matched
753 let format = 'context'
754 endif " If neither matched, keep default set above.
755 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
756 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
757 " if cline > 0
758 " let format = 'context'
759 " endif
760 " if lline > 0 && (cline == 0 || cline > lline)
761 " let format = 'tex'
762 " endif
763 endif " firstNC
764 call setpos('.', save_cursor)
765 endif " firstline =~ '^%&\s*\a\+'
766
767 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
768 if format == 'plain'
769 setf plaintex
770 elseif format == 'context'
771 setf context
772 else " probably LaTeX
773 setf tex
774 endif
775 return
776endfunc
777
Bram Moolenaard09a2062017-11-11 15:37:45 +0100778func dist#ft#FTxml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100779 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100780 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100781 let line = getline(n)
782 " DocBook 4 or DocBook 5.
783 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
784 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
785 if is_docbook4 || is_docbook5
786 let b:docbk_type = "xml"
787 if is_docbook5
788 let b:docbk_ver = 5
789 else
790 let b:docbk_ver = 4
791 endif
792 setf docbk
793 return
794 endif
795 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
796 setf xbl
797 return
798 endif
799 let n += 1
800 endwhile
801 setf xml
802endfunc
803
Bram Moolenaard09a2062017-11-11 15:37:45 +0100804func dist#ft#FTy()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100805 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100806 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100807 let line = getline(n)
808 if line =~ '^\s*%'
809 setf yacc
810 return
811 endif
812 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
813 setf racc
814 return
815 endif
816 let n = n + 1
817 endwhile
818 setf yacc
819endfunc
820
Bram Moolenaard09a2062017-11-11 15:37:45 +0100821func dist#ft#Redif()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100822 let lnum = 1
823 while lnum <= 5 && lnum < line('$')
824 if getline(lnum) =~ "^\ctemplate-type:"
825 setf redif
826 return
827 endif
828 let lnum = lnum + 1
829 endwhile
830endfunc
831
James McCoy647ab4c2021-12-17 20:52:57 +0000832" This function is called for all files under */debian/patches/*, make sure not
833" to non-dep3patch files, such as README and other text files.
834func dist#ft#Dep3patch()
835 if expand('%:t') ==# 'series'
836 return
837 endif
838
839 for ln in getline(1, 100)
840 if ln =~# '^\%(Description\|Subject\|Origin\|Bug\|Forwarded\|Author\|From\|Reviewed-by\|Acked-by\|Last-Updated\|Applied-Upstream\):'
841 setf dep3patch
842 return
843 elseif ln =~# '^---'
844 " end of headers found. stop processing
845 return
846 endif
847 endfor
848endfunc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100849
Elwardi2284f6c2022-01-11 18:14:23 +0000850" This function checks the first 15 lines for appearance of 'FoamFile'
851" and then 'object' in a following line.
852" In that case, it's probably an OpenFOAM file
853func dist#ft#FTfoam()
854 let ffile = 0
855 let lnum = 1
856 while lnum <= 15
857 if getline(lnum) =~# '^FoamFile'
858 let ffile = 1
859 elseif ffile == 1 && getline(lnum) =~# '^\s*object'
860 setf foam
861 return
862 endif
863 let lnum = lnum + 1
864 endwhile
865endfunc
866
=?UTF-8?q?Dundar=20G=C3=B6c?=bd8168c2022-01-28 14:15:09 +0000867" Determine if a *.tf file is TF mud client or terraform
868func dist#ft#FTtf()
869 let numberOfLines = line('$')
870 for i in range(1, numberOfLines)
871 let currentLine = trim(getline(i))
872 let firstCharacter = currentLine[0]
873 if firstCharacter !=? ";" && firstCharacter !=? "/" && firstCharacter !=? ""
874 setf terraform
875 return
876 endif
877 endfor
878 setf tf
879endfunc
880
881
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100882" Restore 'cpoptions'
883let &cpo = s:cpo_save
884unlet s:cpo_save