blob: 5d8734a625eb9c1a9f01143a2323c010e14c25d7 [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
Doug Kearnsc570e9c2022-01-31 17:09:14 +000070let s:ft_visual_basic_content = '\cVB_Name\|Begin VB\.\(Form\|MDIForm\|UserControl\)'
71
72" See FTfrm() for Visual Basic form file detection
73func dist#ft#FTbas()
Bram Moolenaar6517f142022-01-21 14:55:13 +000074 if exists("g:filetype_bas")
75 exe "setf " . g:filetype_bas
76 return
77 endif
78
79 " most frequent FreeBASIC-specific keywords in distro files
80 let fb_keywords = '\c^\s*\%(extern\|var\|enum\|private\|scope\|union\|byref\|operator\|constructor\|delete\|namespace\|public\|property\|with\|destructor\|using\)\>\%(\s*[:=(]\)\@!'
81 let fb_preproc = '\c^\s*\%(#\a\+\|option\s\+\%(byval\|dynamic\|escape\|\%(no\)\=gosub\|nokeyword\|private\|static\)\>\)'
82 let fb_comment = "^\\s*/'"
83 " OPTION EXPLICIT, without the leading underscore, is common to many dialects
84 let qb64_preproc = '\c^\s*\%($\a\+\|option\s\+\%(_explicit\|_\=explicitarray\)\>\)'
85
86 let lines = getline(1, min([line("$"), 100]))
87
88 if match(lines, fb_preproc) > -1 || match(lines, fb_comment) > -1 || match(lines, fb_keywords) > -1
89 setf freebasic
90 elseif match(lines, qb64_preproc) > -1
91 setf qb64
Doug Kearnsc570e9c2022-01-31 17:09:14 +000092 elseif match(lines, s:ft_visual_basic_content) > -1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010093 setf vb
94 else
Bram Moolenaar6517f142022-01-21 14:55:13 +000095 setf basic
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010096 endif
97endfunc
98
Bram Moolenaard09a2062017-11-11 15:37:45 +010099func dist#ft#FTbtm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100100 if exists("g:dosbatch_syntax_for_btm") && g:dosbatch_syntax_for_btm
101 setf dosbatch
102 else
103 setf btm
104 endif
105endfunc
106
Bram Moolenaard09a2062017-11-11 15:37:45 +0100107func dist#ft#BindzoneCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100108 if getline(1).getline(2).getline(3).getline(4) =~ '^; <<>> DiG [0-9.]\+.* <<>>\|$ORIGIN\|$TTL\|IN\s\+SOA'
109 setf bindzone
110 elseif a:default != ''
111 exe 'setf ' . a:default
112 endif
113endfunc
114
Bram Moolenaard09a2062017-11-11 15:37:45 +0100115func dist#ft#FTlpc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100116 if exists("g:lpc_syntax_for_c")
117 let lnum = 1
118 while lnum <= 12
119 if getline(lnum) =~# '^\(//\|inherit\|private\|protected\|nosave\|string\|object\|mapping\|mixed\)'
120 setf lpc
121 return
122 endif
123 let lnum = lnum + 1
124 endwhile
125 endif
126 setf c
127endfunc
128
Bram Moolenaard09a2062017-11-11 15:37:45 +0100129func dist#ft#FTheader()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100130 if match(getline(1, min([line("$"), 200])), '^@\(interface\|end\|class\)') > -1
131 if exists("g:c_syntax_for_h")
132 setf objc
133 else
134 setf objcpp
135 endif
136 elseif exists("g:c_syntax_for_h")
137 setf c
138 elseif exists("g:ch_syntax_for_h")
139 setf ch
140 else
141 setf cpp
142 endif
143endfunc
144
145" This function checks if one of the first ten lines start with a '@'. In
146" that case it is probably a change file.
147" If the first line starts with # or ! it's probably a ch file.
Bram Moolenaarba3ff532018-11-04 14:45:49 +0100148" If a line has "main", "include", "//" or "/*" it's probably ch.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100149" Otherwise CHILL is assumed.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100150func dist#ft#FTchange()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100151 let lnum = 1
152 while lnum <= 10
153 if getline(lnum)[0] == '@'
154 setf change
155 return
156 endif
157 if lnum == 1 && (getline(1)[0] == '#' || getline(1)[0] == '!')
158 setf ch
159 return
160 endif
161 if getline(lnum) =~ "MODULE"
162 setf chill
163 return
164 endif
165 if getline(lnum) =~ 'main\s*(\|#\s*include\|//'
166 setf ch
167 return
168 endif
169 let lnum = lnum + 1
170 endwhile
171 setf chill
172endfunc
173
Bram Moolenaard09a2062017-11-11 15:37:45 +0100174func dist#ft#FTent()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100175 " This function checks for valid cl syntax in the first five lines.
176 " Look for either an opening comment, '#', or a block start, '{".
177 " If not found, assume SGML.
178 let lnum = 1
179 while lnum < 6
180 let line = getline(lnum)
181 if line =~ '^\s*[#{]'
182 setf cl
183 return
184 elseif line !~ '^\s*$'
185 " Not a blank line, not a comment, and not a block start,
186 " so doesn't look like valid cl code.
187 break
188 endif
189 let lnum = lnum + 1
190 endw
191 setf dtd
192endfunc
193
Austin Gatlinf3caeb62021-06-26 12:02:55 +0200194func dist#ft#ExCheck()
195 let lines = getline(1, min([line("$"), 100]))
196 if exists('g:filetype_euphoria')
197 exe 'setf ' . g:filetype_euphoria
198 elseif match(lines, '^--\|^ifdef\>\|^include\>') > -1
199 setf euphoria3
200 else
201 setf elixir
202 endif
203endfunc
204
Bram Moolenaard09a2062017-11-11 15:37:45 +0100205func dist#ft#EuphoriaCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100206 if exists('g:filetype_euphoria')
207 exe 'setf ' . g:filetype_euphoria
208 else
209 setf euphoria3
210 endif
211endfunc
212
Bram Moolenaard09a2062017-11-11 15:37:45 +0100213func dist#ft#DtraceCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100214 let lines = getline(1, min([line("$"), 100]))
215 if match(lines, '^module\>\|^import\>') > -1
216 " D files often start with a module and/or import statement.
217 setf d
218 elseif match(lines, '^#!\S\+dtrace\|#pragma\s\+D\s\+option\|:\S\{-}:\S\{-}:') > -1
219 setf dtrace
220 else
221 setf d
222 endif
223endfunc
224
Bram Moolenaard09a2062017-11-11 15:37:45 +0100225func dist#ft#FTe()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100226 if exists('g:filetype_euphoria')
227 exe 'setf ' . g:filetype_euphoria
228 else
229 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100230 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100231 if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$"
232 setf specman
233 return
234 endif
235 let n = n + 1
236 endwhile
237 setf eiffel
238 endif
239endfunc
240
Doug Kearnsc570e9c2022-01-31 17:09:14 +0000241func dist#ft#FTfrm()
242 if exists("g:filetype_frm")
243 exe "setf " . g:filetype_frm
244 return
245 endif
246
247 let lines = getline(1, min([line("$"), 5]))
248
249 if match(lines, s:ft_visual_basic_content) > -1
250 setf vb
251 else
252 setf form
253 endif
254endfunc
255
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000256" Distinguish between Forth and F#.
257" Provided by Doug Kearns.
258func dist#ft#FTfs()
259 if exists("g:filetype_fs")
260 exe "setf " . g:filetype_fs
261 else
262 let line = getline(nextnonblank(1))
263 " comments and colon definitions
264 if line =~ '^\s*\.\=( ' || line =~ '^\s*\\G\= ' || line =~ '^\\$'
265 \ || line =~ '^\s*: \S'
266 setf forth
267 else
Bram Moolenaar53ba95e2021-11-30 13:02:58 +0000268 setf fsharp
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000269 endif
270 endif
271endfunc
272
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100273" Distinguish between HTML, XHTML and Django
Bram Moolenaard09a2062017-11-11 15:37:45 +0100274func dist#ft#FThtml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100275 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100276 while n < 10 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100277 if getline(n) =~ '\<DTD\s\+XHTML\s'
278 setf xhtml
279 return
280 endif
281 if getline(n) =~ '{%\s*\(extends\|block\|load\)\>\|{#\s\+'
282 setf htmldjango
283 return
284 endif
285 let n = n + 1
286 endwhile
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100287 setf FALLBACK html
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100288endfunc
289
290" Distinguish between standard IDL and MS-IDL
Bram Moolenaard09a2062017-11-11 15:37:45 +0100291func dist#ft#FTidl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100292 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100293 while n < 50 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100294 if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"'
295 setf msidl
296 return
297 endif
298 let n = n + 1
299 endwhile
300 setf idl
301endfunc
302
303" Distinguish between "default" and Cproto prototype file. */
Bram Moolenaard09a2062017-11-11 15:37:45 +0100304func dist#ft#ProtoCheck(default)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100305 " Cproto files have a comment in the first line and a function prototype in
306 " the second line, it always ends in ";". Indent files may also have
307 " comments, thus we can't match comments to see the difference.
308 " IDL files can have a single ';' in the second line, require at least one
309 " chacter before the ';'.
310 if getline(2) =~ '.;$'
311 setf cpp
312 else
313 exe 'setf ' . a:default
314 endif
315endfunc
316
Bram Moolenaard09a2062017-11-11 15:37:45 +0100317func dist#ft#FTm()
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200318 if exists("g:filetype_m")
319 exe "setf " . g:filetype_m
320 return
321 endif
322
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200323 " excluding end(for|function|if|switch|while) common to Murphi
324 let octave_block_terminators = '\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>'
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200325
Doug Kearns7329cfa2021-11-26 13:01:41 +0000326 let objc_preprocessor = '^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>'
327
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100328 let n = 1
329 let saw_comment = 0 " Whether we've seen a multiline comment leader.
330 while n < 100
331 let line = getline(n)
332 if line =~ '^\s*/\*'
333 " /* ... */ is a comment in Objective C and Murphi, so we can't conclude
334 " it's either of them yet, but track this as a hint in case we don't see
335 " anything more definitive.
336 let saw_comment = 1
337 endif
Doug Kearns7329cfa2021-11-26 13:01:41 +0000338 if line =~ '^\s*//' || line =~ '^\s*@import\>' || line =~ objc_preprocessor
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100339 setf objc
340 return
341 endif
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200342 if line =~ '^\s*\%(#\|%!\)' || line =~ '^\s*unwind_protect\>' ||
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200343 \ line =~ '\%(^\|;\)\s*' .. octave_block_terminators
344 setf octave
345 return
346 endif
347 " TODO: could be Matlab or Octave
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100348 if line =~ '^\s*%'
349 setf matlab
350 return
351 endif
352 if line =~ '^\s*(\*'
353 setf mma
354 return
355 endif
356 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
357 setf murphi
358 return
359 endif
360 let n = n + 1
361 endwhile
362
363 if saw_comment
364 " We didn't see anything definitive, but this looks like either Objective C
365 " or Murphi based on the comment leader. Assume the former as it is more
366 " common.
367 setf objc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100368 else
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200369 " Default is Matlab
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100370 setf matlab
371 endif
372endfunc
373
Bram Moolenaard09a2062017-11-11 15:37:45 +0100374func dist#ft#FTmms()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100375 let n = 1
Bram Moolenaard7df2792020-01-02 21:34:42 +0100376 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100377 let line = getline(n)
378 if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
379 setf mmix
380 return
381 endif
382 if line =~ '^\s*#'
383 setf make
384 return
385 endif
386 let n = n + 1
387 endwhile
388 setf mmix
389endfunc
390
391" This function checks if one of the first five lines start with a dot. In
392" that case it is probably an nroff file: 'filetype' is set and 1 is returned.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100393func dist#ft#FTnroff()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100394 if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.'
395 setf nroff
396 return 1
397 endif
398 return 0
399endfunc
400
Bram Moolenaard09a2062017-11-11 15:37:45 +0100401func dist#ft#FTmm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100402 let n = 1
Bram Moolenaard1caa942020-04-10 22:10:56 +0200403 while n < 20
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100404 let line = getline(n)
405 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
406 setf objcpp
407 return
408 endif
409 let n = n + 1
410 endwhile
411 setf nroff
412endfunc
413
Bram Moolenaard09a2062017-11-11 15:37:45 +0100414func dist#ft#FTpl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100415 if exists("g:filetype_pl")
416 exe "setf " . g:filetype_pl
417 else
418 " recognize Prolog by specific text in the first non-empty line
419 " require a blank after the '%' because Perl uses "%list" and "%translate"
420 let l = getline(nextnonblank(1))
421 if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-'
422 setf prolog
423 else
424 setf perl
425 endif
426 endif
427endfunc
428
Bram Moolenaard09a2062017-11-11 15:37:45 +0100429func dist#ft#FTinc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100430 if exists("g:filetype_inc")
431 exe "setf " . g:filetype_inc
432 else
433 let lines = getline(1).getline(2).getline(3)
434 if lines =~? "perlscript"
435 setf aspperl
436 elseif lines =~ "<%"
437 setf aspvbs
438 elseif lines =~ "<?"
439 setf php
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100440 " Pascal supports // comments but they're vary rarely used for file
441 " headers so assume POV-Ray
442 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
443 setf pascal
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100444 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100445 call dist#ft#FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100446 if exists("b:asmsyntax")
447 exe "setf " . fnameescape(b:asmsyntax)
448 else
449 setf pov
450 endif
451 endif
452 endif
453endfunc
454
Bram Moolenaard09a2062017-11-11 15:37:45 +0100455func dist#ft#FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100456 if exists("g:filetype_w")
457 exe "setf " . g:filetype_w
458 return
459 endif
460 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
461 setf progress
462 else
463 setf cweb
464 endif
465endfunc
466
Bram Moolenaard09a2062017-11-11 15:37:45 +0100467func dist#ft#FTprogress_asm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100468 if exists("g:filetype_i")
469 exe "setf " . g:filetype_i
470 return
471 endif
472 " This function checks for an assembly comment the first ten lines.
473 " If not found, assume Progress.
474 let lnum = 1
475 while lnum <= 10 && lnum < line('$')
476 let line = getline(lnum)
477 if line =~ '^\s*;' || line =~ '^\*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100478 call dist#ft#FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100479 return
480 elseif line !~ '^\s*$' || line =~ '^/\*'
481 " Not an empty line: Doesn't look like valid assembly code.
482 " Or it looks like a Progress /* comment
483 break
484 endif
485 let lnum = lnum + 1
486 endw
487 setf progress
488endfunc
489
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100490let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
491let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
492
Bram Moolenaard09a2062017-11-11 15:37:45 +0100493func dist#ft#FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100494 if exists("g:filetype_p")
495 exe "setf " . g:filetype_p
496 return
497 endif
498 " This function checks for valid Pascal syntax in the first ten lines.
499 " Look for either an opening comment or a program start.
500 " If not found, assume Progress.
501 let lnum = 1
502 while lnum <= 10 && lnum < line('$')
503 let line = getline(lnum)
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100504 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100505 setf pascal
506 return
507 elseif line !~ '^\s*$' || line =~ '^/\*'
508 " Not an empty line: Doesn't look like valid Pascal code.
509 " Or it looks like a Progress /* comment
510 break
511 endif
512 let lnum = lnum + 1
513 endw
514 setf progress
515endfunc
516
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100517func dist#ft#FTpp()
518 if exists("g:filetype_pp")
519 exe "setf " . g:filetype_pp
520 else
521 let line = getline(nextnonblank(1))
522 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords
523 setf pascal
524 else
525 setf puppet
526 endif
527 endif
528endfunc
529
Bram Moolenaard09a2062017-11-11 15:37:45 +0100530func dist#ft#FTr()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100531 let max = line("$") > 50 ? 50 : line("$")
532
533 for n in range(1, max)
534 " Rebol is easy to recognize, check for that first
535 if getline(n) =~? '\<REBOL\>'
536 setf rebol
537 return
538 endif
539 endfor
540
541 for n in range(1, max)
542 " R has # comments
543 if getline(n) =~ '^\s*#'
544 setf r
545 return
546 endif
547 " Rexx has /* comments */
548 if getline(n) =~ '^\s*/\*'
549 setf rexx
550 return
551 endif
552 endfor
553
554 " Nothing recognized, use user default or assume Rexx
555 if exists("g:filetype_r")
556 exe "setf " . g:filetype_r
557 else
558 " Rexx used to be the default, but R appears to be much more popular.
559 setf r
560 endif
561endfunc
562
Bram Moolenaard09a2062017-11-11 15:37:45 +0100563func dist#ft#McSetf()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100564 " Rely on the file to start with a comment.
565 " MS message text files use ';', Sendmail files use '#' or 'dnl'
566 for lnum in range(1, min([line("$"), 20]))
567 let line = getline(lnum)
568 if line =~ '^\s*\(#\|dnl\)'
569 setf m4 " Sendmail .mc file
570 return
571 elseif line =~ '^\s*;'
572 setf msmessages " MS Message text file
573 return
574 endif
575 endfor
576 setf m4 " Default: Sendmail .mc file
577endfunc
578
579" Called from filetype.vim and scripts.vim.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100580func dist#ft#SetFileTypeSH(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100581 if did_filetype()
582 " Filetype was already detected
583 return
584 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100585 if expand("<amatch>") =~ g:ft_ignore_pat
586 return
587 endif
588 if a:name =~ '\<csh\>'
589 " Some .sh scripts contain #!/bin/csh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100590 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100591 return
592 elseif a:name =~ '\<tcsh\>'
593 " Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100594 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100595 return
596 elseif a:name =~ '\<zsh\>'
597 " Some .sh scripts contain #!/bin/zsh.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100598 call dist#ft#SetFileTypeShell("zsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100599 return
600 elseif a:name =~ '\<ksh\>'
601 let b:is_kornshell = 1
602 if exists("b:is_bash")
603 unlet b:is_bash
604 endif
605 if exists("b:is_sh")
606 unlet b:is_sh
607 endif
608 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>'
609 let b:is_bash = 1
610 if exists("b:is_kornshell")
611 unlet b:is_kornshell
612 endif
613 if exists("b:is_sh")
614 unlet b:is_sh
615 endif
616 elseif a:name =~ '\<sh\>'
617 let b:is_sh = 1
618 if exists("b:is_kornshell")
619 unlet b:is_kornshell
620 endif
621 if exists("b:is_bash")
622 unlet b:is_bash
623 endif
624 endif
Bram Moolenaard09a2062017-11-11 15:37:45 +0100625 call dist#ft#SetFileTypeShell("sh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100626endfunc
627
628" For shell-like file types, check for an "exec" command hidden in a comment,
629" as used for Tcl.
630" Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100631func dist#ft#SetFileTypeShell(name)
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100632 if did_filetype()
633 " Filetype was already detected
634 return
635 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100636 if expand("<amatch>") =~ g:ft_ignore_pat
637 return
638 endif
639 let l = 2
640 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)'
641 " Skip empty and comment lines.
642 let l = l + 1
643 endwhile
644 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$'
645 " Found an "exec" line after a comment with continuation
646 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '')
647 if n =~ '\<tclsh\|\<wish'
648 setf tcl
649 return
650 endif
651 endif
652 exe "setf " . a:name
653endfunc
654
Bram Moolenaard09a2062017-11-11 15:37:45 +0100655func dist#ft#CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100656 if did_filetype()
657 " Filetype was already detected
658 return
659 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100660 if exists("g:filetype_csh")
Bram Moolenaard09a2062017-11-11 15:37:45 +0100661 call dist#ft#SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100662 elseif &shell =~ "tcsh"
Bram Moolenaard09a2062017-11-11 15:37:45 +0100663 call dist#ft#SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100664 else
Bram Moolenaard09a2062017-11-11 15:37:45 +0100665 call dist#ft#SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100666 endif
667endfunc
668
Bram Moolenaarcef73222017-11-09 21:05:31 +0100669let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
Bram Moolenaard09a2062017-11-11 15:37:45 +0100670func dist#ft#FTRules()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100671 let path = expand('<amatch>:p')
Bram Moolenaaraa9675a2020-08-17 21:57:09 +0200672 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100673 setf udevrules
674 return
675 endif
676 if path =~ '^/etc/ufw/'
677 setf conf " Better than hog
678 return
679 endif
680 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
681 setf javascript
682 return
683 endif
684 try
685 let config_lines = readfile('/etc/udev/udev.conf')
686 catch /^Vim\%((\a\+)\)\=:E484/
687 setf hog
688 return
689 endtry
690 let dir = expand('<amatch>:p:h')
691 for line in config_lines
692 if line =~ s:ft_rules_udev_rules_pattern
693 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "")
694 if dir == udev_rules
695 setf udevrules
696 endif
697 break
698 endif
699 endfor
700 setf hog
701endfunc
702
Bram Moolenaard09a2062017-11-11 15:37:45 +0100703func dist#ft#SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100704 if exists("g:filetype_sql")
705 exe "setf " . g:filetype_sql
706 else
707 setf sql
708 endif
709endfunc
710
711" If the file has an extension of 't' and is in a directory 't' or 'xt' then
712" it is almost certainly a Perl test file.
713" If the first line starts with '#' and contains 'perl' it's probably a Perl
714" file.
715" (Slow test) If a file contains a 'use' statement then it is almost certainly
716" a Perl file.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100717func dist#ft#FTperl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100718 let dirname = expand("%:p:h:t")
719 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
720 setf perl
721 return 1
722 endif
723 if getline(1)[0] == '#' && getline(1) =~ 'perl'
724 setf perl
725 return 1
726 endif
Bram Moolenaarf0b03c42017-12-17 17:17:07 +0100727 let save_cursor = getpos('.')
728 call cursor(1,1)
729 let has_use = search('^use\s\s*\k', 'c', 30)
730 call setpos('.', save_cursor)
731 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100732 setf perl
733 return 1
734 endif
735 return 0
736endfunc
737
738" Choose context, plaintex, or tex (LaTeX) based on these rules:
739" 1. Check the first line of the file for "%&<format>".
740" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
Bram Moolenaar20aac6c2018-09-02 21:07:30 +0200741" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
Bram Moolenaard09a2062017-11-11 15:37:45 +0100742func dist#ft#FTtex()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100743 let firstline = getline(1)
744 if firstline =~ '^%&\s*\a\+'
745 let format = tolower(matchstr(firstline, '\a\+'))
746 let format = substitute(format, 'pdf', '', '')
747 if format == 'tex'
748 let format = 'latex'
749 elseif format == 'plaintex'
750 let format = 'plain'
751 endif
752 elseif expand('%') =~ 'tex/context/.*/.*.tex'
753 let format = 'context'
754 else
755 " Default value, may be changed later:
756 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
757 " Save position, go to the top of the file, find first non-comment line.
758 let save_cursor = getpos('.')
759 call cursor(1,1)
760 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
761 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
762 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
763 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\>'
764 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
765 \ 'cnp', firstNC + 1000)
766 if kwline == 1 " lpat matched
767 let format = 'latex'
768 elseif kwline == 2 " cpat matched
769 let format = 'context'
770 endif " If neither matched, keep default set above.
771 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
772 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
773 " if cline > 0
774 " let format = 'context'
775 " endif
776 " if lline > 0 && (cline == 0 || cline > lline)
777 " let format = 'tex'
778 " endif
779 endif " firstNC
780 call setpos('.', save_cursor)
781 endif " firstline =~ '^%&\s*\a\+'
782
783 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
784 if format == 'plain'
785 setf plaintex
786 elseif format == 'context'
787 setf context
788 else " probably LaTeX
789 setf tex
790 endif
791 return
792endfunc
793
Bram Moolenaard09a2062017-11-11 15:37:45 +0100794func dist#ft#FTxml()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100795 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100796 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100797 let line = getline(n)
798 " DocBook 4 or DocBook 5.
799 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
800 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
801 if is_docbook4 || is_docbook5
802 let b:docbk_type = "xml"
803 if is_docbook5
804 let b:docbk_ver = 5
805 else
806 let b:docbk_ver = 4
807 endif
808 setf docbk
809 return
810 endif
811 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
812 setf xbl
813 return
814 endif
815 let n += 1
816 endwhile
817 setf xml
818endfunc
819
Bram Moolenaard09a2062017-11-11 15:37:45 +0100820func dist#ft#FTy()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100821 let n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100822 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100823 let line = getline(n)
824 if line =~ '^\s*%'
825 setf yacc
826 return
827 endif
828 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
829 setf racc
830 return
831 endif
832 let n = n + 1
833 endwhile
834 setf yacc
835endfunc
836
Bram Moolenaard09a2062017-11-11 15:37:45 +0100837func dist#ft#Redif()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100838 let lnum = 1
839 while lnum <= 5 && lnum < line('$')
840 if getline(lnum) =~ "^\ctemplate-type:"
841 setf redif
842 return
843 endif
844 let lnum = lnum + 1
845 endwhile
846endfunc
847
James McCoy647ab4c2021-12-17 20:52:57 +0000848" This function is called for all files under */debian/patches/*, make sure not
849" to non-dep3patch files, such as README and other text files.
850func dist#ft#Dep3patch()
851 if expand('%:t') ==# 'series'
852 return
853 endif
854
855 for ln in getline(1, 100)
856 if ln =~# '^\%(Description\|Subject\|Origin\|Bug\|Forwarded\|Author\|From\|Reviewed-by\|Acked-by\|Last-Updated\|Applied-Upstream\):'
857 setf dep3patch
858 return
859 elseif ln =~# '^---'
860 " end of headers found. stop processing
861 return
862 endif
863 endfor
864endfunc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100865
Elwardi2284f6c2022-01-11 18:14:23 +0000866" This function checks the first 15 lines for appearance of 'FoamFile'
867" and then 'object' in a following line.
868" In that case, it's probably an OpenFOAM file
869func dist#ft#FTfoam()
870 let ffile = 0
871 let lnum = 1
872 while lnum <= 15
873 if getline(lnum) =~# '^FoamFile'
874 let ffile = 1
875 elseif ffile == 1 && getline(lnum) =~# '^\s*object'
876 setf foam
877 return
878 endif
879 let lnum = lnum + 1
880 endwhile
881endfunc
882
=?UTF-8?q?Dundar=20G=C3=B6c?=bd8168c2022-01-28 14:15:09 +0000883" Determine if a *.tf file is TF mud client or terraform
884func dist#ft#FTtf()
885 let numberOfLines = line('$')
886 for i in range(1, numberOfLines)
887 let currentLine = trim(getline(i))
888 let firstCharacter = currentLine[0]
889 if firstCharacter !=? ";" && firstCharacter !=? "/" && firstCharacter !=? ""
890 setf terraform
891 return
892 endif
893 endfor
894 setf tf
895endfunc
896
897
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100898" Restore 'cpoptions'
899let &cpo = s:cpo_save
900unlet s:cpo_save