blob: ff1dc037d2a19e030314ec595775759cf5d53f13 [file] [log] [blame]
Bram Moolenaara2baa732022-02-04 16:09:54 +00001vim9script
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01002
Bram Moolenaara2baa732022-02-04 16:09:54 +00003# Vim functions for file type detection
4#
Doug Kearns68a89472024-01-05 17:59:04 +01005# Maintainer: The Vim Project <https://github.com/vim/vim>
Wu Yongwei32a1b262025-07-08 21:42:37 +02006# Last Change: 2025 Jul 08
Christian Brabandte978b452023-08-13 10:33:05 +02007# Former Maintainer: Bram Moolenaar <Bram@vim.org>
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01008
Bram Moolenaara2baa732022-02-04 16:09:54 +00009# These functions are moved here from runtime/filetype.vim to make startup
10# faster.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010011
igna_martinoli37853b72024-07-18 21:34:36 +020012var prolog_pattern = '^\s*\(:-\|%\+\(\s\|$\)\|\/\*\)\|\.\s*$'
13
Bram Moolenaara2baa732022-02-04 16:09:54 +000014export def Check_inp()
Wu, Zhenyu61ee8332024-04-09 22:09:30 +020015 if getline(1) =~ '%%'
16 setf tex
17 elseif getline(1) =~ '^\*'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010018 setf abaqus
19 else
Bram Moolenaara2baa732022-02-04 16:09:54 +000020 var n = 1
21 var nmax = line("$") > 500 ? 500 : line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010022 while n <= nmax
23 if getline(n) =~? "^header surface data"
24 setf trasys
25 break
26 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +000027 n += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010028 endwhile
29 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +000030enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010031
Bram Moolenaara2baa732022-02-04 16:09:54 +000032# This function checks for the kind of assembly that is wanted by the user, or
Wu Yongwei32a1b262025-07-08 21:42:37 +020033# can be detected from the beginning of the file.
Bram Moolenaara2baa732022-02-04 16:09:54 +000034export def FTasm()
35 # make sure b:asmsyntax exists
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010036 if !exists("b:asmsyntax")
Bram Moolenaara2baa732022-02-04 16:09:54 +000037 b:asmsyntax = ""
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010038 endif
39
40 if b:asmsyntax == ""
Bram Moolenaara2baa732022-02-04 16:09:54 +000041 FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010042 endif
43
Bram Moolenaara2baa732022-02-04 16:09:54 +000044 # if b:asmsyntax still isn't set, default to asmsyntax or GNU
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010045 if b:asmsyntax == ""
46 if exists("g:asmsyntax")
Bram Moolenaara2baa732022-02-04 16:09:54 +000047 b:asmsyntax = g:asmsyntax
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010048 else
Bram Moolenaara2baa732022-02-04 16:09:54 +000049 b:asmsyntax = "asm"
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010050 endif
51 endif
52
Bram Moolenaara2baa732022-02-04 16:09:54 +000053 exe "setf " .. fnameescape(b:asmsyntax)
54enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010055
Bram Moolenaara2baa732022-02-04 16:09:54 +000056export def FTasmsyntax()
57 # see if the file contains any asmsyntax=foo overrides. If so, change
58 # b:asmsyntax appropriately
59 var head = " " .. getline(1) .. " " .. getline(2) .. " "
60 .. getline(3) .. " " .. getline(4) .. " " .. getline(5) .. " "
61 var match = matchstr(head, '\sasmsyntax=\zs[a-zA-Z0-9]\+\ze\s')
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010062 if match != ''
Bram Moolenaara2baa732022-02-04 16:09:54 +000063 b:asmsyntax = match
Wu Yongwei32a1b262025-07-08 21:42:37 +020064 else
65 # Use heuristics
66 var is_slash_star_encountered = false
67 var i = 1
68 const n = min([50, line("$")])
69 while i <= n
70 const line = getline(i)
71 if line =~ '\%(^\|\n\)/\*'
72 is_slash_star_encountered = true
73 endif
74 if line =~# '^; Listing generated by Microsoft' || line =~? '\%(^\|\n\)\%(\%(CONST\|_BSS\|_DATA\|_TEXT\)\s\+SEGMENT\>\)\|\s*\.[2-6]86P\?\>\|\s*\.XMM\>'
75 b:asmsyntax = "masm"
76 elseif line =~ 'Texas Instruments Incorporated' || (line =~ '\%(^\|\n\)\*' && !is_slash_star_encountered)
77 # tiasm uses `* commment`, but detection is unreliable if '/*' is seen
78 b:asmsyntax = "tiasm"
79 elseif ((line =~? '\.title\>\|\.ident\>\|\.macro\>\|\.subtitle\>\|\.library\>'))
80 b:asmsyntax = "vmasm"
81 endif
82 i += 1
83 endwhile
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010084 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +000085enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +010086
Doug Kearnsf97f6bb2023-08-27 18:44:09 +020087var ft_visual_basic_content = '\c^\s*\%(Attribute\s\+VB_Name\|Begin\s\+\%(VB\.\|{\%(\x\+-\)\+\x\+}\)\)'
Doug Kearnsc570e9c2022-01-31 17:09:14 +000088
Bram Moolenaara2baa732022-02-04 16:09:54 +000089# See FTfrm() for Visual Basic form file detection
90export def FTbas()
Bram Moolenaar6517f142022-01-21 14:55:13 +000091 if exists("g:filetype_bas")
Bram Moolenaara2baa732022-02-04 16:09:54 +000092 exe "setf " .. g:filetype_bas
Bram Moolenaar6517f142022-01-21 14:55:13 +000093 return
94 endif
95
Bram Moolenaara2baa732022-02-04 16:09:54 +000096 # most frequent FreeBASIC-specific keywords in distro files
97 var fb_keywords = '\c^\s*\%(extern\|var\|enum\|private\|scope\|union\|byref\|operator\|constructor\|delete\|namespace\|public\|property\|with\|destructor\|using\)\>\%(\s*[:=(]\)\@!'
Bram Moolenaar8b5901e2022-06-29 14:39:12 +010098 var fb_preproc = '\c^\s*\%(' ..
99 # preprocessor
100 '#\s*\a\+\|' ..
101 # compiler option
102 'option\s\+\%(byval\|dynamic\|escape\|\%(no\)\=gosub\|nokeyword\|private\|static\)\>\|' ..
103 # metacommand
104 '\%(''\|rem\)\s*\$lang\>\|' ..
105 # default datatype
106 'def\%(byte\|longint\|short\|ubyte\|uint\|ulongint\|ushort\)\>' ..
107 '\)'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000108 var fb_comment = "^\\s*/'"
Bram Moolenaar8b5901e2022-06-29 14:39:12 +0100109
Bram Moolenaara2baa732022-02-04 16:09:54 +0000110 # OPTION EXPLICIT, without the leading underscore, is common to many dialects
111 var qb64_preproc = '\c^\s*\%($\a\+\|option\s\+\%(_explicit\|_\=explicitarray\)\>\)'
Bram Moolenaar6517f142022-01-21 14:55:13 +0000112
Bram Moolenaar8b5901e2022-06-29 14:39:12 +0100113 for lnum in range(1, min([line("$"), 100]))
114 var line = getline(lnum)
115 if line =~ ft_visual_basic_content
116 setf vb
117 return
118 elseif line =~ fb_preproc || line =~ fb_comment || line =~ fb_keywords
119 setf freebasic
120 return
121 elseif line =~ qb64_preproc
122 setf qb64
123 return
124 endif
125 endfor
126 setf basic
Bram Moolenaara2baa732022-02-04 16:09:54 +0000127enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100128
Bram Moolenaara2baa732022-02-04 16:09:54 +0000129export def FTbtm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100130 if exists("g:dosbatch_syntax_for_btm") && g:dosbatch_syntax_for_btm
131 setf dosbatch
132 else
133 setf btm
134 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000135enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100136
Bram Moolenaara2baa732022-02-04 16:09:54 +0000137export def BindzoneCheck(default = '')
138 if getline(1) .. getline(2) .. getline(3) .. getline(4)
139 =~ '^; <<>> DiG [0-9.]\+.* <<>>\|$ORIGIN\|$TTL\|IN\s\+SOA'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100140 setf bindzone
Bram Moolenaara2baa732022-02-04 16:09:54 +0000141 elseif default != ''
142 exe 'setf ' .. default
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100143 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000144enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100145
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100146# Returns true if file content looks like RAPID
147def IsRapid(sChkExt: string = ""): bool
148 if sChkExt == "cfg"
149 return getline(1) =~? '\v^%(EIO|MMC|MOC|PROC|SIO|SYS):CFG'
150 endif
151 # called from FTmod, FTprg or FTsys
152 return getline(nextnonblank(1)) =~? '\v^\s*%(\%{3}|module\s+\k+\s*%(\(|$))'
153enddef
154
155export def FTcfg()
156 if exists("g:filetype_cfg")
157 exe "setf " .. g:filetype_cfg
158 elseif IsRapid("cfg")
159 setf rapid
160 else
161 setf cfg
162 endif
163enddef
164
Wu, Zhenyue2c27ca2024-11-19 20:55:25 +0100165export def FTcl()
166 if join(getline(1, 4), '') =~ '/\*'
167 setf opencl
168 else
169 setf lisp
170 endif
171enddef
172
Bram Moolenaar8b5901e2022-06-29 14:39:12 +0100173export def FTcls()
174 if exists("g:filetype_cls")
175 exe "setf " .. g:filetype_cls
176 return
177 endif
178
Doug Kearnsf97f6bb2023-08-27 18:44:09 +0200179 var line1 = getline(1)
Doug Kearnse06afb72023-08-29 22:21:35 +0200180 if line1 =~ '^#!.*\<\%(rexx\|regina\)\>'
Bram Moolenaar8b5901e2022-06-29 14:39:12 +0100181 setf rexx
Doug Kearnse06afb72023-08-29 22:21:35 +0200182 return
Doug Kearnsf97f6bb2023-08-27 18:44:09 +0200183 elseif line1 == 'VERSION 1.0 CLASS'
Bram Moolenaar8b5901e2022-06-29 14:39:12 +0100184 setf vb
Doug Kearnse06afb72023-08-29 22:21:35 +0200185 return
186 endif
187
188 var nonblank1 = getline(nextnonblank(1))
189 if nonblank1 =~ '^\v%(\%|\\)'
190 setf tex
191 elseif nonblank1 =~ '^\s*\%(/\*\|::\w\)'
192 setf rexx
Bram Moolenaar8b5901e2022-06-29 14:39:12 +0100193 else
194 setf st
195 endif
196enddef
197
Wu, Zhenyubc32bbd2024-11-14 22:55:36 +0100198export def FTll()
199 if getline(1) =~ ';\|\<source_filename\>\|\<target\>'
200 setf llvm
Eisuke Kawashima48295112025-06-17 20:30:52 +0200201 return
Wu, Zhenyubc32bbd2024-11-14 22:55:36 +0100202 endif
Eisuke Kawashima48295112025-06-17 20:30:52 +0200203 var n = 1
204 while n < 100 && n <= line("$")
205 var line = getline(n)
206 if line =~ '^\s*%'
207 setf lex
208 return
209 endif
210 n += 1
211 endwhile
212 setf lifelines
Wu, Zhenyubc32bbd2024-11-14 22:55:36 +0100213enddef
214
Bram Moolenaara2baa732022-02-04 16:09:54 +0000215export def FTlpc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100216 if exists("g:lpc_syntax_for_c")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000217 var lnum = 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100218 while lnum <= 12
219 if getline(lnum) =~# '^\(//\|inherit\|private\|protected\|nosave\|string\|object\|mapping\|mixed\)'
220 setf lpc
221 return
222 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000223 lnum += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100224 endwhile
225 endif
226 setf c
Bram Moolenaara2baa732022-02-04 16:09:54 +0000227enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100228
Amelia Clarke3041cf62025-04-19 11:48:10 +0200229# Searches within the first `maxlines` lines of the file for distinctive
230# Objective-C or C++ syntax and returns the appropriate filetype. Returns a
231# null_string if the search was inconclusive.
232def CheckObjCOrCpp(maxlines = 100): string
233 var n = 1
234 while n < maxlines && n <= line('$')
235 const line = getline(n)
236 if line =~ '\v^\s*\@%(class|interface|end)>'
237 return 'objcpp'
238 elseif line =~ '\v^\s*%(class|namespace|template|using)>'
239 return 'cpp'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100240 endif
Amelia Clarke3041cf62025-04-19 11:48:10 +0200241 ++n
242 endwhile
243 return null_string
244enddef
245
246# Determines whether a *.h file is C, C++, Ch, or Objective-C/Objective-C++.
247export def FTheader()
248 if exists('g:filetype_h')
249 execute $'setf {g:filetype_h}'
250 elseif exists('g:c_syntax_for_h')
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100251 setf c
Amelia Clarke3041cf62025-04-19 11:48:10 +0200252 elseif exists('g:ch_syntax_for_h')
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100253 setf ch
254 else
Amelia Clarke3041cf62025-04-19 11:48:10 +0200255 # Search the first 100 lines of the file for distinctive Objective-C or C++
256 # syntax and set the filetype accordingly. Otherwise, use C as the default
257 # filetype.
258 execute $'setf {CheckObjCOrCpp() ?? 'c'}'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100259 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000260enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100261
Bram Moolenaara2baa732022-02-04 16:09:54 +0000262# This function checks if one of the first ten lines start with a '@'. In
263# that case it is probably a change file.
264# If the first line starts with # or ! it's probably a ch file.
265# If a line has "main", "include", "//" or "/*" it's probably ch.
266# Otherwise CHILL is assumed.
267export def FTchange()
268 var lnum = 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100269 while lnum <= 10
270 if getline(lnum)[0] == '@'
271 setf change
272 return
273 endif
274 if lnum == 1 && (getline(1)[0] == '#' || getline(1)[0] == '!')
275 setf ch
276 return
277 endif
278 if getline(lnum) =~ "MODULE"
279 setf chill
280 return
281 endif
282 if getline(lnum) =~ 'main\s*(\|#\s*include\|//'
283 setf ch
284 return
285 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000286 lnum += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100287 endwhile
288 setf chill
Bram Moolenaara2baa732022-02-04 16:09:54 +0000289enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100290
Bram Moolenaara2baa732022-02-04 16:09:54 +0000291export def FTent()
292 # This function checks for valid cl syntax in the first five lines.
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100293 # Look for either an opening comment, '#', or a block start, '{'.
Bram Moolenaara2baa732022-02-04 16:09:54 +0000294 # If not found, assume SGML.
295 var lnum = 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100296 while lnum < 6
Bram Moolenaara2baa732022-02-04 16:09:54 +0000297 var line = getline(lnum)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100298 if line =~ '^\s*[#{]'
299 setf cl
300 return
301 elseif line !~ '^\s*$'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000302 # Not a blank line, not a comment, and not a block start,
303 # so doesn't look like valid cl code.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100304 break
305 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000306 lnum += 1
Bram Moolenaarc12dc472022-03-05 13:45:56 +0000307 endwhile
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100308 setf dtd
Bram Moolenaara2baa732022-02-04 16:09:54 +0000309enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100310
Bram Moolenaara2baa732022-02-04 16:09:54 +0000311export def ExCheck()
312 var lines = getline(1, min([line("$"), 100]))
Austin Gatlinf3caeb62021-06-26 12:02:55 +0200313 if exists('g:filetype_euphoria')
Bram Moolenaara2baa732022-02-04 16:09:54 +0000314 exe 'setf ' .. g:filetype_euphoria
Austin Gatlinf3caeb62021-06-26 12:02:55 +0200315 elseif match(lines, '^--\|^ifdef\>\|^include\>') > -1
316 setf euphoria3
317 else
318 setf elixir
319 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000320enddef
Austin Gatlinf3caeb62021-06-26 12:02:55 +0200321
Bram Moolenaara2baa732022-02-04 16:09:54 +0000322export def EuphoriaCheck()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100323 if exists('g:filetype_euphoria')
Bram Moolenaara2baa732022-02-04 16:09:54 +0000324 exe 'setf ' .. g:filetype_euphoria
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100325 else
326 setf euphoria3
327 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000328enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100329
Bram Moolenaara2baa732022-02-04 16:09:54 +0000330export def DtraceCheck()
=?UTF-8?q?Teubel=20Gy=C3=B6rgy?=4d56b972022-02-24 17:59:09 +0000331 if did_filetype()
332 # Filetype was already detected
333 return
334 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000335 var lines = getline(1, min([line("$"), 100]))
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100336 if match(lines, '^module\>\|^import\>') > -1
Bram Moolenaara2baa732022-02-04 16:09:54 +0000337 # D files often start with a module and/or import statement.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100338 setf d
339 elseif match(lines, '^#!\S\+dtrace\|#pragma\s\+D\s\+option\|:\S\{-}:\S\{-}:') > -1
340 setf dtrace
341 else
342 setf d
343 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000344enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100345
Doug Kearns68a89472024-01-05 17:59:04 +0100346export def FTdef()
Wu, Zhenyu61ee8332024-04-09 22:09:30 +0200347 # LaTeX def files are usually generated by docstrip, which will output '%%' in first line
348 if getline(1) =~ '%%'
349 setf tex
350 endif
Doug Kearns68a89472024-01-05 17:59:04 +0100351 if get(g:, "filetype_def", "") == "modula2" || IsModula2()
352 SetFiletypeModula2()
353 return
354 endif
355
356 if exists("g:filetype_def")
357 exe "setf " .. g:filetype_def
358 else
359 setf def
360 endif
361enddef
362
Bram Moolenaara2baa732022-02-04 16:09:54 +0000363export def FTe()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100364 if exists('g:filetype_euphoria')
Bram Moolenaara2baa732022-02-04 16:09:54 +0000365 exe 'setf ' .. g:filetype_euphoria
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100366 else
Bram Moolenaara2baa732022-02-04 16:09:54 +0000367 var n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100368 while n < 100 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100369 if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$"
370 setf specman
371 return
372 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000373 n += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100374 endwhile
375 setf eiffel
376 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000377enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100378
Doug Kearns19a3bc32023-08-20 20:51:12 +0200379def IsForth(): bool
380 var first_line = nextnonblank(1)
381
382 # SwiftForth block comment (line is usually filled with '-' or '=') or
383 # OPTIONAL (sometimes precedes the header comment)
384 if getline(first_line) =~? '^\%({\%(\s\|$\)\|OPTIONAL\s\)'
385 return true
386 endif
387
388 var n = first_line
389 while n < 100 && n <= line("$")
390 # Forth comments and colon definitions
391 if getline(n) =~ '^[:(\\] '
392 return true
393 endif
394 n += 1
395 endwhile
396 return false
397enddef
398
399# Distinguish between Forth and Fortran
400export def FTf()
401 if exists("g:filetype_f")
402 exe "setf " .. g:filetype_f
403 elseif IsForth()
404 setf forth
405 else
406 setf fortran
407 endif
408enddef
409
Bram Moolenaara2baa732022-02-04 16:09:54 +0000410export def FTfrm()
Doug Kearnsc570e9c2022-01-31 17:09:14 +0000411 if exists("g:filetype_frm")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000412 exe "setf " .. g:filetype_frm
Doug Kearnsc570e9c2022-01-31 17:09:14 +0000413 return
414 endif
415
Doug Kearnsf97f6bb2023-08-27 18:44:09 +0200416 if getline(1) == "VERSION 5.00"
417 setf vb
418 return
419 endif
420
Bram Moolenaara2baa732022-02-04 16:09:54 +0000421 var lines = getline(1, min([line("$"), 5]))
Doug Kearnsc570e9c2022-01-31 17:09:14 +0000422
Bram Moolenaarb2c72352022-02-22 21:17:40 +0000423 if match(lines, ft_visual_basic_content) > -1
Doug Kearnsc570e9c2022-01-31 17:09:14 +0000424 setf vb
425 else
426 setf form
427 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000428enddef
Doug Kearnsc570e9c2022-01-31 17:09:14 +0000429
Doug Kearns19a3bc32023-08-20 20:51:12 +0200430# Distinguish between Forth and F#
Bram Moolenaara2baa732022-02-04 16:09:54 +0000431export def FTfs()
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000432 if exists("g:filetype_fs")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000433 exe "setf " .. g:filetype_fs
Doug Kearns19a3bc32023-08-20 20:51:12 +0200434 elseif IsForth()
435 setf forth
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000436 else
Johan Kotlinski065088d2023-04-02 20:29:38 +0100437 setf fsharp
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000438 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000439enddef
Bram Moolenaar3d14c0f2021-11-27 17:22:07 +0000440
Amelia Clarke35dfe582024-05-24 08:05:00 +0200441# Recursively search for Hare source files in a directory and any
442# subdirectories, up to a given depth.
443def IsHareModule(dir: string, depth: number): bool
444 if depth <= 0
445 return !empty(glob(dir .. '/*.ha'))
446 endif
447
448 return reduce(sort(glob(dir .. '/*', true, true),
449 (a, b) => isdirectory(a) - isdirectory(b)),
450 (acc, n) => acc
451 || n =~ '\.ha$'
452 || isdirectory(n)
453 && IsHareModule(n, depth - 1),
454 false)
455enddef
456
457# Determine if a README file exists within a Hare module and should be given the
458# Haredoc filetype.
459export def FTharedoc()
460 if exists('g:filetype_haredoc')
461 if IsHareModule('<afile>:h', get(g:, 'haredoc_search_depth', 1))
462 setf haredoc
463 endif
464 endif
465enddef
466
Dennis van den Berg1ad194c2024-07-09 19:25:33 +0200467# Distinguish between HTML, XHTML, Django and Angular
Bram Moolenaara2baa732022-02-04 16:09:54 +0000468export def FThtml()
469 var n = 1
Dennis van den Berg1ad194c2024-07-09 19:25:33 +0200470
471 # Test if the filename follows the Angular component template convention
Christian Brabandtc03f6312024-07-10 19:23:39 +0200472 # Disabled for the reasons mentioned here: #13594
473 # if expand('%:t') =~ '^.*\.component\.html$'
474 # setf htmlangular
475 # return
476 # endif
Dennis van den Berg1ad194c2024-07-09 19:25:33 +0200477
Afiq Nazrie9718ed72024-06-18 19:59:53 +0200478 while n < 40 && n <= line("$")
Dennis van den Berg1ad194c2024-07-09 19:25:33 +0200479 # Check for Angular
Christian Brabandt668e9f22025-01-11 09:19:12 +0100480 if getline(n) =~ '@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content'
Dennis van den Berg1ad194c2024-07-09 19:25:33 +0200481 setf htmlangular
482 return
483 endif
Dennis van den Berg1ad194c2024-07-09 19:25:33 +0200484 # Check for XHTML
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100485 if getline(n) =~ '\<DTD\s\+XHTML\s'
486 setf xhtml
487 return
488 endif
Dennis van den Berg1ad194c2024-07-09 19:25:33 +0200489 # Check for Django
Afiq Nazrie9718ed72024-06-18 19:59:53 +0200490 if getline(n) =~ '{%\s*\(autoescape\|block\|comment\|csrf_token\|cycle\|debug\|extends\|filter\|firstof\|for\|if\|ifchanged\|include\|load\|lorem\|now\|query_string\|regroup\|resetcycle\|spaceless\|templatetag\|url\|verbatim\|widthratio\|with\)\>\|{#\s\+'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100491 setf htmldjango
492 return
493 endif
EliSaudere57c9a12024-07-28 21:28:11 +0200494 # Check for SuperHTML
495 if getline(n) =~ '<extend\|<super>'
496 setf superhtml
497 return
498 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000499 n += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100500 endwhile
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100501 setf FALLBACK html
Bram Moolenaara2baa732022-02-04 16:09:54 +0000502enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100503
Bram Moolenaara2baa732022-02-04 16:09:54 +0000504# Distinguish between standard IDL and MS-IDL
505export def FTidl()
506 var n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +0100507 while n < 50 && n <= line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100508 if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"'
509 setf msidl
510 return
511 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000512 n += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100513 endwhile
514 setf idl
Bram Moolenaara2baa732022-02-04 16:09:54 +0000515enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100516
Wu, Zhenyu887a38c2024-05-09 20:35:13 +0200517# Distinguish between "default", Prolog, zsh module's C and Cproto prototype file.
Bram Moolenaara2baa732022-02-04 16:09:54 +0000518export def ProtoCheck(default: string)
Wu, Zhenyu887a38c2024-05-09 20:35:13 +0200519 # zsh modules use '#include "*.pro"'
520 # https://github.com/zsh-users/zsh/blob/63f086d167960a27ecdbcb762179e2c2bf8a29f5/Src/Modules/example.c#L31
521 if getline(1) =~ '/* Generated automatically */'
522 setf c
Bram Moolenaara2baa732022-02-04 16:09:54 +0000523 # Cproto files have a comment in the first line and a function prototype in
524 # the second line, it always ends in ";". Indent files may also have
525 # comments, thus we can't match comments to see the difference.
526 # IDL files can have a single ';' in the second line, require at least one
527 # chacter before the ';'.
Wu, Zhenyu887a38c2024-05-09 20:35:13 +0200528 elseif getline(2) =~ '.;$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100529 setf cpp
530 else
Bram Moolenaar48c3f4e2022-08-08 15:42:38 +0100531 # recognize Prolog by specific text in the first non-empty line
532 # require a blank after the '%' because Perl uses "%list" and "%translate"
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100533 var lnum = getline(nextnonblank(1))
igna_martinoli37853b72024-07-18 21:34:36 +0200534 if lnum =~ '\<prolog\>' || lnum =~ prolog_pattern
Bram Moolenaar48c3f4e2022-08-08 15:42:38 +0100535 setf prolog
536 else
537 exe 'setf ' .. default
538 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100539 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000540enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100541
Bram Moolenaara2baa732022-02-04 16:09:54 +0000542export def FTm()
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200543 if exists("g:filetype_m")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000544 exe "setf " .. g:filetype_m
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200545 return
546 endif
547
Bram Moolenaara2baa732022-02-04 16:09:54 +0000548 # excluding end(for|function|if|switch|while) common to Murphi
549 var octave_block_terminators = '\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>'
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200550
Bram Moolenaara2baa732022-02-04 16:09:54 +0000551 var objc_preprocessor = '^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>'
Doug Kearns7329cfa2021-11-26 13:01:41 +0000552
Bram Moolenaara2baa732022-02-04 16:09:54 +0000553 var n = 1
554 var saw_comment = 0 # Whether we've seen a multiline comment leader.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100555 while n < 100
Bram Moolenaara2baa732022-02-04 16:09:54 +0000556 var line = getline(n)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100557 if line =~ '^\s*/\*'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000558 # /* ... */ is a comment in Objective C and Murphi, so we can't conclude
559 # it's either of them yet, but track this as a hint in case we don't see
560 # anything more definitive.
561 saw_comment = 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100562 endif
Doug Kearns7329cfa2021-11-26 13:01:41 +0000563 if line =~ '^\s*//' || line =~ '^\s*@import\>' || line =~ objc_preprocessor
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100564 setf objc
565 return
566 endif
Bram Moolenaarca0627d2021-09-12 17:03:08 +0200567 if line =~ '^\s*\%(#\|%!\)' || line =~ '^\s*unwind_protect\>' ||
Bram Moolenaardeba5eb2021-09-03 19:21:36 +0200568 \ line =~ '\%(^\|;\)\s*' .. octave_block_terminators
569 setf octave
570 return
571 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000572 # TODO: could be Matlab or Octave
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100573 if line =~ '^\s*%'
574 setf matlab
575 return
576 endif
577 if line =~ '^\s*(\*'
578 setf mma
579 return
580 endif
581 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)'
582 setf murphi
583 return
584 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000585 n += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100586 endwhile
587
588 if saw_comment
Bram Moolenaara2baa732022-02-04 16:09:54 +0000589 # We didn't see anything definitive, but this looks like either Objective C
590 # or Murphi based on the comment leader. Assume the former as it is more
591 # common.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100592 setf objc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100593 else
Bram Moolenaara2baa732022-02-04 16:09:54 +0000594 # Default is Matlab
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100595 setf matlab
596 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000597enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100598
Ken Takataeb4b9032024-07-25 21:07:13 +0200599export def FTmake()
Eisuke Kawashimaf35bd762025-04-15 19:20:06 +0200600 # Check if it is a BSD, GNU, or Microsoft Makefile
601 unlet! b:make_flavor
602
603 # 1. filename
604 if expand('%:t') == 'BSDmakefile'
605 b:make_flavor = 'bsd'
606 setf make
607 return
608 elseif expand('%:t') == 'GNUmakefile'
609 b:make_flavor = 'gnu'
610 setf make
611 return
612 endif
613
614 # 2. user's setting
615 if exists('g:make_flavor')
616 b:make_flavor = g:make_flavor
617 setf make
618 return
619 elseif get(g:, 'make_microsoft')
620 echom "make_microsoft is deprecated; try g:make_flavor = 'microsoft' instead"
621 b:make_flavor = 'microsoft'
622 setf make
623 return
624 endif
625
626 # 3. try to detect a flavor from file content
Ken Takataeb4b9032024-07-25 21:07:13 +0200627 var n = 1
628 while n < 1000 && n <= line('$')
629 var line = getline(n)
630 if line =~? '^\s*!\s*\(ifn\=\(def\)\=\|include\|message\|error\)\>'
Eisuke Kawashimaf35bd762025-04-15 19:20:06 +0200631 b:make_flavor = 'microsoft'
Ken Takataeb4b9032024-07-25 21:07:13 +0200632 break
Eisuke Kawashimaf35bd762025-04-15 19:20:06 +0200633 elseif line =~ '^\.\%(export\|error\|for\|if\%(n\=\%(def\|make\)\)\=\|info\|warning\)\>'
634 b:make_flavor = 'bsd'
Ken Takataeb4b9032024-07-25 21:07:13 +0200635 break
Eisuke Kawashimaf35bd762025-04-15 19:20:06 +0200636 elseif line =~ '^ *\%(ifn\=\%(eq\|def\)\|define\|override\)\>'
637 b:make_flavor = 'gnu'
638 break
639 elseif line =~ '\$[({][a-z-]\+\s\+\S\+' # a function call, e.g. $(shell pwd)
640 b:make_flavor = 'gnu'
Ken Takataeb4b9032024-07-25 21:07:13 +0200641 break
642 endif
643 n += 1
644 endwhile
645 setf make
646enddef
647
Bram Moolenaara2baa732022-02-04 16:09:54 +0000648export def FTmms()
649 var n = 1
Bram Moolenaard7df2792020-01-02 21:34:42 +0100650 while n < 20
Bram Moolenaara2baa732022-02-04 16:09:54 +0000651 var line = getline(n)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100652 if line =~ '^\s*\(%\|//\)' || line =~ '^\*'
653 setf mmix
654 return
655 endif
656 if line =~ '^\s*#'
657 setf make
658 return
659 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000660 n += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100661 endwhile
662 setf mmix
Bram Moolenaara2baa732022-02-04 16:09:54 +0000663enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100664
Eisuke Kawashima2cb42ef2025-04-21 10:51:05 +0200665# This function checks if one of the first five lines start with a typical
666# nroff pattern in man files. In that case it is probably an nroff file:
667# 'filetype' is set and 1 is returned.
Bram Moolenaara2baa732022-02-04 16:09:54 +0000668export def FTnroff(): number
Eisuke Kawashimababdb052025-04-15 18:30:05 +0200669 var n = 1
Eisuke Kawashima2cb42ef2025-04-21 10:51:05 +0200670 while n <= 5
Eisuke Kawashimababdb052025-04-15 18:30:05 +0200671 var line = getline(n)
Eisuke Kawashima2cb42ef2025-04-21 10:51:05 +0200672 if line =~ '^\%([.'']\s*\%(TH\|D[dt]\|S[Hh]\|d[es]1\?\|so\)\s\+\S\|[.'']\s*ig\>\|\%([.'']\s*\)\?\\"\)'
Eisuke Kawashimababdb052025-04-15 18:30:05 +0200673 setf nroff
674 return 1
675 endif
676 n += 1
677 endwhile
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100678 return 0
Bram Moolenaara2baa732022-02-04 16:09:54 +0000679enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100680
Bram Moolenaara2baa732022-02-04 16:09:54 +0000681export def FTmm()
682 var n = 1
Bram Moolenaard1caa942020-04-10 22:10:56 +0200683 while n < 20
Bram Moolenaara2baa732022-02-04 16:09:54 +0000684 if getline(n) =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100685 setf objcpp
686 return
687 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000688 n += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100689 endwhile
690 setf nroff
Bram Moolenaara2baa732022-02-04 16:09:54 +0000691enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100692
Bram Moolenaarcdbfc6d2022-06-30 16:25:21 +0100693# Returns true if file content looks like LambdaProlog module
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100694def IsLProlog(): bool
Gaetan Lepage4ce1bda2023-05-10 22:01:55 +0100695 # skip apparent comments and blank lines, what looks like
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100696 # LambdaProlog comment may be RAPID header
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100697 var lnum: number = nextnonblank(1)
698 while lnum > 0 && lnum < line('$') && getline(lnum) =~ '^\s*%' # LambdaProlog comment
699 lnum = nextnonblank(lnum + 1)
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100700 endwhile
701 # this pattern must not catch a go.mod file
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100702 return getline(lnum) =~ '\<module\s\+\w\+\s*\.\s*\(%\|$\)'
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100703enddef
704
Doug Kearns68a89472024-01-05 17:59:04 +0100705def IsModula2(): bool
dkearnsef387c02024-02-20 06:58:30 +1100706 return getline(nextnonblank(1)) =~ '\<MODULE\s\+\w\+\s*\%(\[.*]\s*\)\=;\|^\s*(\*'
Doug Kearns68a89472024-01-05 17:59:04 +0100707enddef
708
709def SetFiletypeModula2()
710 const KNOWN_DIALECTS = ["iso", "pim", "r10"]
711 const KNOWN_EXTENSIONS = ["gm2"]
712 const LINE_COUNT = 200
713 const TAG = '(\*!m2\(\w\+\)\%(+\(\w\+\)\)\=\*)'
714
715 var dialect = get(g:, "modula2_default_dialect", "pim")
716 var extension = get(g:, "modula2_default_extension", "")
717
718 var matches = []
719
720 # ignore unknown dialects or badly formatted tags
721 for lnum in range(1, min([line("$"), LINE_COUNT]))
722 matches = matchlist(getline(lnum), TAG)
723 if !empty(matches)
724 if index(KNOWN_DIALECTS, matches[1]) >= 0
725 dialect = matches[1]
726 endif
727 if index(KNOWN_EXTENSIONS, matches[2]) >= 0
728 extension = matches[2]
729 endif
730 break
731 endif
732 endfor
733
734 modula2#SetDialect(dialect, extension)
735
736 setf modula2
737enddef
738
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100739# Determine if *.mod is ABB RAPID, LambdaProlog, Modula-2, Modsim III or go.mod
740export def FTmod()
Doug Kearns68a89472024-01-05 17:59:04 +0100741 if get(g:, "filetype_mod", "") == "modula2" || IsModula2()
742 SetFiletypeModula2()
743 return
744 endif
745
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100746 if exists("g:filetype_mod")
747 exe "setf " .. g:filetype_mod
Omar El Halabic9fbd252023-05-29 19:59:45 +0100748 elseif expand("<afile>") =~ '\<go.mod$'
749 setf gomod
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100750 elseif IsLProlog()
751 setf lprolog
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100752 elseif IsRapid()
753 setf rapid
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100754 else
755 # Nothing recognized, assume modsim3
756 setf modsim3
757 endif
758enddef
759
Bram Moolenaara2baa732022-02-04 16:09:54 +0000760export def FTpl()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100761 if exists("g:filetype_pl")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000762 exe "setf " .. g:filetype_pl
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100763 else
Bram Moolenaara2baa732022-02-04 16:09:54 +0000764 # recognize Prolog by specific text in the first non-empty line
765 # require a blank after the '%' because Perl uses "%list" and "%translate"
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100766 var line = getline(nextnonblank(1))
igna_martinoli37853b72024-07-18 21:34:36 +0200767 if line =~ '\<prolog\>' || line =~ prolog_pattern
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100768 setf prolog
769 else
770 setf perl
771 endif
772 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000773enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100774
Bram Moolenaara2baa732022-02-04 16:09:54 +0000775export def FTinc()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100776 if exists("g:filetype_inc")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000777 exe "setf " .. g:filetype_inc
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100778 else
Bram Moolenaara2baa732022-02-04 16:09:54 +0000779 var lines = getline(1) .. getline(2) .. getline(3)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100780 if lines =~? "perlscript"
781 setf aspperl
782 elseif lines =~ "<%"
783 setf aspvbs
784 elseif lines =~ "<?"
785 setf php
Bram Moolenaara2baa732022-02-04 16:09:54 +0000786 # Pascal supports // comments but they're vary rarely used for file
787 # headers so assume POV-Ray
Bram Moolenaarb2c72352022-02-22 21:17:40 +0000788 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? ft_pascal_keywords
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100789 setf pascal
Gregory Anders30e212d2022-07-26 21:42:03 +0100790 elseif lines =~# '\<\%(require\|inherit\)\>' || lines =~# '[A-Z][A-Za-z0-9_:${}]*\s\+\%(??\|[?:+]\)\?= '
Gregory Andersfa49eb42022-07-16 17:46:47 +0100791 setf bitbake
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100792 else
Bram Moolenaara2baa732022-02-04 16:09:54 +0000793 FTasmsyntax()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100794 if exists("b:asmsyntax")
Gregory Andersfa49eb42022-07-16 17:46:47 +0100795 exe "setf " .. fnameescape(b:asmsyntax)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100796 else
Gregory Andersfa49eb42022-07-16 17:46:47 +0100797 setf pov
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100798 endif
799 endif
800 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000801enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100802
Bram Moolenaara2baa732022-02-04 16:09:54 +0000803export def FTprogress_cweb()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100804 if exists("g:filetype_w")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000805 exe "setf " .. g:filetype_w
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100806 return
807 endif
808 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE'
809 setf progress
810 else
811 setf cweb
812 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000813enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100814
Julien Marrec2e310652023-11-25 15:30:46 +0100815# These include the leading '%' sign
816var ft_swig_keywords = '^\s*%\%(addmethods\|apply\|beginfile\|clear\|constant\|define\|echo\|enddef\|endoffile\|extend\|feature\|fragment\|ignore\|import\|importfile\|include\|includefile\|inline\|insert\|keyword\|module\|name\|namewarn\|native\|newobject\|parms\|pragma\|rename\|template\|typedef\|typemap\|types\|varargs\|warn\)'
817# This is the start/end of a block that is copied literally to the processor file (C/C++)
818var ft_swig_verbatim_block_start = '^\s*%{'
819
820export def FTi()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100821 if exists("g:filetype_i")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000822 exe "setf " .. g:filetype_i
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100823 return
824 endif
Julien Marrec2e310652023-11-25 15:30:46 +0100825 # This function checks for an assembly comment or a SWIG keyword or verbatim block in the first 50 lines.
Bram Moolenaara2baa732022-02-04 16:09:54 +0000826 # If not found, assume Progress.
827 var lnum = 1
Julien Marrec2e310652023-11-25 15:30:46 +0100828 while lnum <= 50 && lnum < line('$')
Bram Moolenaara2baa732022-02-04 16:09:54 +0000829 var line = getline(lnum)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100830 if line =~ '^\s*;' || line =~ '^\*'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000831 FTasm()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100832 return
Julien Marrec2e310652023-11-25 15:30:46 +0100833 elseif line =~ ft_swig_keywords || line =~ ft_swig_verbatim_block_start
834 setf swig
835 return
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100836 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000837 lnum += 1
Bram Moolenaarc12dc472022-03-05 13:45:56 +0000838 endwhile
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100839 setf progress
Bram Moolenaara2baa732022-02-04 16:09:54 +0000840enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100841
Bram Moolenaara2baa732022-02-04 16:09:54 +0000842var ft_pascal_comments = '^\s*\%({\|(\*\|//\)'
843var ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>'
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100844
Bram Moolenaara2baa732022-02-04 16:09:54 +0000845export def FTprogress_pascal()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100846 if exists("g:filetype_p")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000847 exe "setf " .. g:filetype_p
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100848 return
849 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000850 # This function checks for valid Pascal syntax in the first ten lines.
851 # Look for either an opening comment or a program start.
852 # If not found, assume Progress.
853 var lnum = 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100854 while lnum <= 10 && lnum < line('$')
Bram Moolenaara2baa732022-02-04 16:09:54 +0000855 var line = getline(lnum)
Bram Moolenaarb2c72352022-02-22 21:17:40 +0000856 if line =~ ft_pascal_comments || line =~? ft_pascal_keywords
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100857 setf pascal
858 return
859 elseif line !~ '^\s*$' || line =~ '^/\*'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000860 # Not an empty line: Doesn't look like valid Pascal code.
861 # Or it looks like a Progress /* comment
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100862 break
863 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000864 lnum += 1
Bram Moolenaarc12dc472022-03-05 13:45:56 +0000865 endwhile
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100866 setf progress
Bram Moolenaara2baa732022-02-04 16:09:54 +0000867enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100868
Bram Moolenaara2baa732022-02-04 16:09:54 +0000869export def FTpp()
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100870 if exists("g:filetype_pp")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000871 exe "setf " .. g:filetype_pp
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100872 else
Bram Moolenaara2baa732022-02-04 16:09:54 +0000873 var line = getline(nextnonblank(1))
Bram Moolenaarb2c72352022-02-22 21:17:40 +0000874 if line =~ ft_pascal_comments || line =~? ft_pascal_keywords
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100875 setf pascal
876 else
877 setf puppet
878 endif
879 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000880enddef
Bram Moolenaara0122dc2021-01-12 17:42:24 +0100881
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100882# Determine if *.prg is ABB RAPID. Can also be Clipper, FoxPro or eviews
883export def FTprg()
884 if exists("g:filetype_prg")
885 exe "setf " .. g:filetype_prg
886 elseif IsRapid()
887 setf rapid
888 else
889 # Nothing recognized, assume Clipper
890 setf clipper
891 endif
892enddef
893
Bram Moolenaara2baa732022-02-04 16:09:54 +0000894export def FTr()
895 var max = line("$") > 50 ? 50 : line("$")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100896
897 for n in range(1, max)
Bram Moolenaara2baa732022-02-04 16:09:54 +0000898 # Rebol is easy to recognize, check for that first
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100899 if getline(n) =~? '\<REBOL\>'
900 setf rebol
901 return
902 endif
903 endfor
904
905 for n in range(1, max)
Bram Moolenaara2baa732022-02-04 16:09:54 +0000906 # R has # comments
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100907 if getline(n) =~ '^\s*#'
908 setf r
909 return
910 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000911 # Rexx has /* comments */
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100912 if getline(n) =~ '^\s*/\*'
913 setf rexx
914 return
915 endif
916 endfor
917
Bram Moolenaara2baa732022-02-04 16:09:54 +0000918 # Nothing recognized, use user default or assume Rexx
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100919 if exists("g:filetype_r")
Bram Moolenaara2baa732022-02-04 16:09:54 +0000920 exe "setf " .. g:filetype_r
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100921 else
Bram Moolenaara2baa732022-02-04 16:09:54 +0000922 # Rexx used to be the default, but R appears to be much more popular.
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100923 setf r
924 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +0000925enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100926
Bram Moolenaara2baa732022-02-04 16:09:54 +0000927export def McSetf()
928 # Rely on the file to start with a comment.
929 # MS message text files use ';', Sendmail files use '#' or 'dnl'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100930 for lnum in range(1, min([line("$"), 20]))
Bram Moolenaara2baa732022-02-04 16:09:54 +0000931 var line = getline(lnum)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100932 if line =~ '^\s*\(#\|dnl\)'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000933 setf m4 # Sendmail .mc file
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100934 return
935 elseif line =~ '^\s*;'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000936 setf msmessages # MS Message text file
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100937 return
938 endif
939 endfor
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +0100940 setf m4 # Default: Sendmail .mc file
Bram Moolenaara2baa732022-02-04 16:09:54 +0000941enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100942
Bram Moolenaara2baa732022-02-04 16:09:54 +0000943# Called from filetype.vim and scripts.vim.
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100944# When "setft" is passed and false then the 'filetype' option is not set.
945export def SetFileTypeSH(name: string, setft = true): string
946 if setft && did_filetype()
Bram Moolenaara2baa732022-02-04 16:09:54 +0000947 # Filetype was already detected
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100948 return ''
Bram Moolenaar147e7d02019-01-18 21:46:47 +0100949 endif
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100950 if setft && expand("<amatch>") =~ g:ft_ignore_pat
951 return ''
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100952 endif
Aliaksei Budavei5c84d122025-04-10 21:52:14 +0200953 if name =~ '^csh$' || name =~ '^#!.\{-2,}\<csh\>'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000954 # Some .sh scripts contain #!/bin/csh.
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100955 return SetFileTypeShell("csh", setft)
Aliaksei Budavei5c84d122025-04-10 21:52:14 +0200956 elseif name =~ '^tcsh$' || name =~ '^#!.\{-2,}\<tcsh\>'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000957 # Some .sh scripts contain #!/bin/tcsh.
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100958 return SetFileTypeShell("tcsh", setft)
Aliaksei Budavei5c84d122025-04-10 21:52:14 +0200959 elseif name =~ '^zsh$' || name =~ '^#!.\{-2,}\<zsh\>'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000960 # Some .sh scripts contain #!/bin/zsh.
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100961 return SetFileTypeShell("zsh", setft)
Aliaksei Budavei5c84d122025-04-10 21:52:14 +0200962 elseif name =~ '^ksh$' || name =~ '^#!.\{-2,}\<ksh\>'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000963 b:is_kornshell = 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100964 if exists("b:is_bash")
965 unlet b:is_bash
966 endif
967 if exists("b:is_sh")
968 unlet b:is_sh
969 endif
Aliaksei Budavei5c84d122025-04-10 21:52:14 +0200970 elseif exists("g:bash_is_sh") || name =~ '^bash2\=$' ||
971 \ name =~ '^#!.\{-2,}\<bash2\=\>'
Bram Moolenaara2baa732022-02-04 16:09:54 +0000972 b:is_bash = 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100973 if exists("b:is_kornshell")
974 unlet b:is_kornshell
975 endif
976 if exists("b:is_sh")
977 unlet b:is_sh
978 endif
Aliaksei Budavei5c84d122025-04-10 21:52:14 +0200979 elseif name =~ '^\%(da\)\=sh$' || name =~ '^#!.\{-2,}\<\%(da\)\=sh\>'
Eisuke Kawashima24482fb2022-11-24 10:58:10 +0000980 # Ubuntu links "sh" to "dash", thus it is expected to work the same way
Bram Moolenaara2baa732022-02-04 16:09:54 +0000981 b:is_sh = 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100982 if exists("b:is_kornshell")
983 unlet b:is_kornshell
984 endif
985 if exists("b:is_bash")
986 unlet b:is_bash
987 endif
988 endif
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100989
990 return SetFileTypeShell("sh", setft)
Bram Moolenaara2baa732022-02-04 16:09:54 +0000991enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +0100992
Bram Moolenaara2baa732022-02-04 16:09:54 +0000993# For shell-like file types, check for an "exec" command hidden in a comment,
994# as used for Tcl.
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100995# When "setft" is passed and false then the 'filetype' option is not set.
Bram Moolenaara2baa732022-02-04 16:09:54 +0000996# Also called from scripts.vim, thus can't be local to this script.
Bram Moolenaarf07d1a72023-06-09 21:01:47 +0100997export def SetFileTypeShell(name: string, setft = true): string
998 if setft && did_filetype()
Bram Moolenaara2baa732022-02-04 16:09:54 +0000999 # Filetype was already detected
Bram Moolenaarf07d1a72023-06-09 21:01:47 +01001000 return ''
Bram Moolenaar147e7d02019-01-18 21:46:47 +01001001 endif
Bram Moolenaarf07d1a72023-06-09 21:01:47 +01001002 if setft && expand("<amatch>") =~ g:ft_ignore_pat
1003 return ''
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001004 endif
Bram Moolenaarf07d1a72023-06-09 21:01:47 +01001005
1006 var lnum = 2
1007 while lnum < 20 && lnum < line("$") && getline(lnum) =~ '^\s*\(#\|$\)'
Bram Moolenaara2baa732022-02-04 16:09:54 +00001008 # Skip empty and comment lines.
Bram Moolenaarf07d1a72023-06-09 21:01:47 +01001009 lnum += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001010 endwhile
Bram Moolenaarf07d1a72023-06-09 21:01:47 +01001011 if lnum < line("$") && getline(lnum) =~ '\s*exec\s' && getline(lnum - 1) =~ '^\s*#.*\\$'
Bram Moolenaara2baa732022-02-04 16:09:54 +00001012 # Found an "exec" line after a comment with continuation
Bram Moolenaarf07d1a72023-06-09 21:01:47 +01001013 var n = substitute(getline(lnum), '\s*exec\s\+\([^ ]*/\)\=', '', '')
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001014 if n =~ '\<tclsh\|\<wish'
Bram Moolenaarf07d1a72023-06-09 21:01:47 +01001015 if setft
1016 setf tcl
1017 endif
1018 return 'tcl'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001019 endif
1020 endif
Bram Moolenaarf07d1a72023-06-09 21:01:47 +01001021
1022 if setft
1023 exe "setf " .. name
1024 endif
1025 return name
Bram Moolenaara2baa732022-02-04 16:09:54 +00001026enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001027
Bram Moolenaara2baa732022-02-04 16:09:54 +00001028export def CSH()
Bram Moolenaar147e7d02019-01-18 21:46:47 +01001029 if did_filetype()
Bram Moolenaara2baa732022-02-04 16:09:54 +00001030 # Filetype was already detected
Bram Moolenaar147e7d02019-01-18 21:46:47 +01001031 return
1032 endif
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001033 if exists("g:filetype_csh")
Bram Moolenaara2baa732022-02-04 16:09:54 +00001034 SetFileTypeShell(g:filetype_csh)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001035 elseif &shell =~ "tcsh"
Bram Moolenaara2baa732022-02-04 16:09:54 +00001036 SetFileTypeShell("tcsh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001037 else
Bram Moolenaara2baa732022-02-04 16:09:54 +00001038 SetFileTypeShell("csh")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001039 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +00001040enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001041
Bram Moolenaara2baa732022-02-04 16:09:54 +00001042var ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*'
1043export def FTRules()
1044 var path = expand('<amatch>:p')
Bram Moolenaaraa9675a2020-08-17 21:57:09 +02001045 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001046 setf udevrules
1047 return
1048 endif
1049 if path =~ '^/etc/ufw/'
Bram Moolenaara2baa732022-02-04 16:09:54 +00001050 setf conf # Better than hog
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001051 return
1052 endif
1053 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d'
1054 setf javascript
1055 return
1056 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +00001057 var config_lines: list<string>
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001058 try
Bram Moolenaara2baa732022-02-04 16:09:54 +00001059 config_lines = readfile('/etc/udev/udev.conf')
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001060 catch /^Vim\%((\a\+)\)\=:E484/
1061 setf hog
1062 return
1063 endtry
Bram Moolenaara2baa732022-02-04 16:09:54 +00001064 var dir = expand('<amatch>:p:h')
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001065 for line in config_lines
Bram Moolenaarb2c72352022-02-22 21:17:40 +00001066 if line =~ ft_rules_udev_rules_pattern
1067 var udev_rules = substitute(line, ft_rules_udev_rules_pattern, '\1', "")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001068 if dir == udev_rules
1069 setf udevrules
1070 endif
1071 break
1072 endif
1073 endfor
1074 setf hog
Bram Moolenaara2baa732022-02-04 16:09:54 +00001075enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001076
Bram Moolenaara2baa732022-02-04 16:09:54 +00001077export def SQL()
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001078 if exists("g:filetype_sql")
Bram Moolenaara2baa732022-02-04 16:09:54 +00001079 exe "setf " .. g:filetype_sql
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001080 else
1081 setf sql
1082 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +00001083enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001084
Wu, Zhenyu4f73c072025-01-08 20:20:06 +01001085export def FTsa()
1086 if join(getline(1, 4), "\n") =~# '\%(^\|\n\);'
1087 setf tiasm
1088 return
1089 endif
1090 setf sather
1091enddef
1092
ranjithshegde8cac20e2022-04-13 15:29:21 +01001093# This function checks the first 25 lines of file extension "sc" to resolve
Chris Kipp70ef3f52022-12-14 16:42:15 +00001094# detection between scala and SuperCollider.
1095# NOTE: We don't check for 'Class : Method', as this can easily be confused
1096# with valid Scala like `val x : Int = 3`. So we instead only rely on
1097# checks that can't be confused.
ranjithshegde8cac20e2022-04-13 15:29:21 +01001098export def FTsc()
1099 for lnum in range(1, min([line("$"), 25]))
Chris Kipp70ef3f52022-12-14 16:42:15 +00001100 if getline(lnum) =~# 'var\s<\|classvar\s<\|\^this.*\||\w\+|\|+\s\w*\s{\|\*ar\s'
ranjithshegde8cac20e2022-04-13 15:29:21 +01001101 setf supercollider
1102 return
1103 endif
1104 endfor
1105 setf scala
1106enddef
1107
1108# This function checks the first line of file extension "scd" to resolve
1109# detection between scdoc and SuperCollider
1110export def FTscd()
1111 if getline(1) =~# '\%^\S\+(\d[0-9A-Za-z]*)\%(\s\+\"[^"]*\"\%(\s\+\"[^"]*\"\)\=\)\=$'
1112 setf scdoc
1113 else
1114 setf supercollider
1115 endif
1116enddef
1117
Bram Moolenaara2baa732022-02-04 16:09:54 +00001118# If the file has an extension of 't' and is in a directory 't' or 'xt' then
1119# it is almost certainly a Perl test file.
1120# If the first line starts with '#' and contains 'perl' it's probably a Perl
1121# file.
1122# (Slow test) If a file contains a 'use' statement then it is almost certainly
1123# a Perl file.
1124export def FTperl(): number
1125 var dirname = expand("%:p:h:t")
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001126 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt')
1127 setf perl
1128 return 1
1129 endif
1130 if getline(1)[0] == '#' && getline(1) =~ 'perl'
1131 setf perl
1132 return 1
1133 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +00001134 var save_cursor = getpos('.')
1135 call cursor(1, 1)
Bram Moolenaare5b78972022-02-05 19:50:34 +00001136 var has_use = search('^use\s\s*\k', 'c', 30) > 0
Bram Moolenaarf0b03c42017-12-17 17:17:07 +01001137 call setpos('.', save_cursor)
1138 if has_use
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001139 setf perl
1140 return 1
1141 endif
1142 return 0
Bram Moolenaara2baa732022-02-04 16:09:54 +00001143enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001144
Bram Moolenaarcdbfc6d2022-06-30 16:25:21 +01001145# LambdaProlog and Standard ML signature files
1146export def FTsig()
1147 if exists("g:filetype_sig")
1148 exe "setf " .. g:filetype_sig
1149 return
1150 endif
1151
1152 var lprolog_comment = '^\s*\%(/\*\|%\)'
1153 var lprolog_keyword = '^\s*sig\s\+\a'
1154 var sml_comment = '^\s*(\*'
1155 var sml_keyword = '^\s*\%(signature\|structure\)\s\+\a'
1156
1157 var line = getline(nextnonblank(1))
1158
1159 if line =~ lprolog_comment || line =~# lprolog_keyword
1160 setf lprolog
1161 elseif line =~ sml_comment || line =~# sml_keyword
1162 setf sml
1163 endif
1164enddef
1165
Bram Moolenaarbe807d52022-09-01 15:01:25 +01001166# This function checks the first 100 lines of files matching "*.sil" to
1167# resolve detection between Swift Intermediate Language and SILE.
1168export def FTsil()
1169 for lnum in range(1, [line('$'), 100]->min())
1170 var line: string = getline(lnum)
1171 if line =~ '^\s*[\\%]'
1172 setf sile
1173 return
1174 elseif line =~ '^\s*\S'
1175 setf sil
1176 return
1177 endif
1178 endfor
1179 # no clue, default to "sil"
1180 setf sil
1181enddef
1182
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +01001183export def FTsys()
KnoP-01f420ff22022-04-13 20:46:21 +01001184 if exists("g:filetype_sys")
1185 exe "setf " .. g:filetype_sys
1186 elseif IsRapid()
Bram Moolenaar0bbf09c2022-04-09 15:16:53 +01001187 setf rapid
1188 else
1189 setf bat
1190 endif
1191enddef
1192
Bram Moolenaara2baa732022-02-04 16:09:54 +00001193# Choose context, plaintex, or tex (LaTeX) based on these rules:
1194# 1. Check the first line of the file for "%&<format>".
1195# 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
1196# 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
1197export def FTtex()
1198 var firstline = getline(1)
1199 var format: string
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001200 if firstline =~ '^%&\s*\a\+'
Bram Moolenaara2baa732022-02-04 16:09:54 +00001201 format = tolower(matchstr(firstline, '\a\+'))
1202 format = substitute(format, 'pdf', '', '')
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001203 if format == 'tex'
Bram Moolenaara2baa732022-02-04 16:09:54 +00001204 format = 'latex'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001205 elseif format == 'plaintex'
Bram Moolenaara2baa732022-02-04 16:09:54 +00001206 format = 'plain'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001207 endif
1208 elseif expand('%') =~ 'tex/context/.*/.*.tex'
Bram Moolenaara2baa732022-02-04 16:09:54 +00001209 format = 'context'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001210 else
Bram Moolenaara2baa732022-02-04 16:09:54 +00001211 # Default value, may be changed later:
1212 format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
1213 # Save position, go to the top of the file, find first non-comment line.
1214 var save_cursor = getpos('.')
1215 call cursor(1, 1)
1216 var firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
Bram Moolenaare5b78972022-02-05 19:50:34 +00001217 if firstNC > 0
1218 # Check the next thousand lines for a LaTeX or ConTeXt keyword.
Bram Moolenaara2baa732022-02-04 16:09:54 +00001219 var lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
1220 var 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\>'
1221 var kwline = search('^\s*\\\%(' .. lpat .. '\)\|^\s*\\\(' .. cpat .. '\)',
1222 'cnp', firstNC + 1000)
1223 if kwline == 1 # lpat matched
1224 format = 'latex'
1225 elseif kwline == 2 # cpat matched
1226 format = 'context'
1227 endif # If neither matched, keep default set above.
1228 # let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
1229 # let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
1230 # if cline > 0
1231 # let format = 'context'
1232 # endif
1233 # if lline > 0 && (cline == 0 || cline > lline)
1234 # let format = 'tex'
1235 # endif
1236 endif # firstNC
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001237 call setpos('.', save_cursor)
Bram Moolenaara2baa732022-02-04 16:09:54 +00001238 endif # firstline =~ '^%&\s*\a\+'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001239
Bram Moolenaara2baa732022-02-04 16:09:54 +00001240 # Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001241 if format == 'plain'
1242 setf plaintex
1243 elseif format == 'context'
1244 setf context
Bram Moolenaara2baa732022-02-04 16:09:54 +00001245 else # probably LaTeX
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001246 setf tex
1247 endif
1248 return
Bram Moolenaara2baa732022-02-04 16:09:54 +00001249enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001250
Bram Moolenaara2baa732022-02-04 16:09:54 +00001251export def FTxml()
1252 var n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +01001253 while n < 100 && n <= line("$")
Bram Moolenaara2baa732022-02-04 16:09:54 +00001254 var line = getline(n)
1255 # DocBook 4 or DocBook 5.
1256 var is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
1257 var is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"'
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001258 if is_docbook4 || is_docbook5
Bram Moolenaara2baa732022-02-04 16:09:54 +00001259 b:docbk_type = "xml"
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001260 if is_docbook5
Bram Moolenaara2baa732022-02-04 16:09:54 +00001261 b:docbk_ver = 5
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001262 else
Bram Moolenaara2baa732022-02-04 16:09:54 +00001263 b:docbk_ver = 4
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001264 endif
1265 setf docbk
1266 return
1267 endif
1268 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"'
1269 setf xbl
1270 return
1271 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +00001272 n += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001273 endwhile
1274 setf xml
Bram Moolenaara2baa732022-02-04 16:09:54 +00001275enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001276
Bram Moolenaara2baa732022-02-04 16:09:54 +00001277export def FTy()
1278 var n = 1
Bram Moolenaar493fbe42019-03-17 17:16:12 +01001279 while n < 100 && n <= line("$")
Bram Moolenaara2baa732022-02-04 16:09:54 +00001280 var line = getline(n)
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001281 if line =~ '^\s*%'
1282 setf yacc
1283 return
1284 endif
1285 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include'
1286 setf racc
1287 return
1288 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +00001289 n += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001290 endwhile
1291 setf yacc
Bram Moolenaara2baa732022-02-04 16:09:54 +00001292enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001293
Bram Moolenaara2baa732022-02-04 16:09:54 +00001294export def Redif()
1295 var lnum = 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001296 while lnum <= 5 && lnum < line('$')
1297 if getline(lnum) =~ "^\ctemplate-type:"
1298 setf redif
1299 return
1300 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +00001301 lnum += 1
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001302 endwhile
Bram Moolenaara2baa732022-02-04 16:09:54 +00001303enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001304
Bram Moolenaara2baa732022-02-04 16:09:54 +00001305# This function is called for all files under */debian/patches/*, make sure not
1306# to non-dep3patch files, such as README and other text files.
1307export def Dep3patch()
James McCoy647ab4c2021-12-17 20:52:57 +00001308 if expand('%:t') ==# 'series'
1309 return
1310 endif
1311
1312 for ln in getline(1, 100)
1313 if ln =~# '^\%(Description\|Subject\|Origin\|Bug\|Forwarded\|Author\|From\|Reviewed-by\|Acked-by\|Last-Updated\|Applied-Upstream\):'
1314 setf dep3patch
1315 return
1316 elseif ln =~# '^---'
Bram Moolenaara2baa732022-02-04 16:09:54 +00001317 # end of headers found. stop processing
James McCoy647ab4c2021-12-17 20:52:57 +00001318 return
1319 endif
1320 endfor
Bram Moolenaara2baa732022-02-04 16:09:54 +00001321enddef
Bram Moolenaar851ee6c2017-11-09 20:46:17 +01001322
Bram Moolenaara2baa732022-02-04 16:09:54 +00001323# This function checks the first 15 lines for appearance of 'FoamFile'
1324# and then 'object' in a following line.
1325# In that case, it's probably an OpenFOAM file
1326export def FTfoam()
1327 var ffile = 0
1328 var lnum = 1
Elwardi2284f6c2022-01-11 18:14:23 +00001329 while lnum <= 15
1330 if getline(lnum) =~# '^FoamFile'
Bram Moolenaara2baa732022-02-04 16:09:54 +00001331 ffile = 1
Elwardi2284f6c2022-01-11 18:14:23 +00001332 elseif ffile == 1 && getline(lnum) =~# '^\s*object'
1333 setf foam
1334 return
1335 endif
Bram Moolenaara2baa732022-02-04 16:09:54 +00001336 lnum += 1
Elwardi2284f6c2022-01-11 18:14:23 +00001337 endwhile
Bram Moolenaara2baa732022-02-04 16:09:54 +00001338enddef
Elwardi2284f6c2022-01-11 18:14:23 +00001339
Bram Moolenaara2baa732022-02-04 16:09:54 +00001340# Determine if a *.tf file is TF mud client or terraform
1341export def FTtf()
1342 var numberOfLines = line('$')
=?UTF-8?q?Dundar=20G=C3=B6c?=bd8168c2022-01-28 14:15:09 +00001343 for i in range(1, numberOfLines)
Bram Moolenaara2baa732022-02-04 16:09:54 +00001344 var currentLine = trim(getline(i))
1345 var firstCharacter = currentLine[0]
=?UTF-8?q?Dundar=20G=C3=B6c?=bd8168c2022-01-28 14:15:09 +00001346 if firstCharacter !=? ";" && firstCharacter !=? "/" && firstCharacter !=? ""
1347 setf terraform
1348 return
1349 endif
1350 endfor
1351 setf tf
Bram Moolenaara2baa732022-02-04 16:09:54 +00001352enddef
=?UTF-8?q?Dundar=20G=C3=B6c?=bd8168c2022-01-28 14:15:09 +00001353
KnoP-0193c7a452022-04-16 21:14:04 +01001354var ft_krl_header = '\&\w+'
Bram Moolenaar3ad20902022-04-06 18:57:39 +01001355# Determine if a *.src file is Kuka Robot Language
1356export def FTsrc()
KnoP-0193c7a452022-04-16 21:14:04 +01001357 var ft_krl_def_or_deffct = '%(global\s+)?def%(fct)?>'
Bram Moolenaar3ad20902022-04-06 18:57:39 +01001358 if exists("g:filetype_src")
1359 exe "setf " .. g:filetype_src
KnoP-0193c7a452022-04-16 21:14:04 +01001360 elseif getline(nextnonblank(1)) =~? '\v^\s*%(' .. ft_krl_header .. '|' .. ft_krl_def_or_deffct .. ')'
Bram Moolenaar3ad20902022-04-06 18:57:39 +01001361 setf krl
1362 endif
1363enddef
1364
1365# Determine if a *.dat file is Kuka Robot Language
1366export def FTdat()
KnoP-0193c7a452022-04-16 21:14:04 +01001367 var ft_krl_defdat = 'defdat>'
Bram Moolenaar3ad20902022-04-06 18:57:39 +01001368 if exists("g:filetype_dat")
1369 exe "setf " .. g:filetype_dat
KnoP-0193c7a452022-04-16 21:14:04 +01001370 elseif getline(nextnonblank(1)) =~? '\v^\s*%(' .. ft_krl_header .. '|' .. ft_krl_defdat .. ')'
Bram Moolenaar3ad20902022-04-06 18:57:39 +01001371 setf krl
1372 endif
1373enddef
=?UTF-8?q?Dundar=20G=C3=B6c?=bd8168c2022-01-28 14:15:09 +00001374
Doug Kearns4ac8e792022-10-17 13:32:17 +01001375export def FTlsl()
1376 if exists("g:filetype_lsl")
1377 exe "setf " .. g:filetype_lsl
1378 endif
1379
1380 var line = getline(nextnonblank(1))
1381 if line =~ '^\s*%' || line =~# ':\s*trait\s*$'
1382 setf larch
1383 else
1384 setf lsl
1385 endif
1386enddef
1387
Gaetan Lepage4ce1bda2023-05-10 22:01:55 +01001388export def FTtyp()
1389 if exists("g:filetype_typ")
1390 exe "setf " .. g:filetype_typ
1391 return
1392 endif
1393
1394 # Look for SQL type definition syntax
1395 for line in getline(1, 200)
1396 # SQL type files may define the casing
1397 if line =~ '^CASE\s\==\s\=\(SAME\|LOWER\|UPPER\|OPPOSITE\)$'
1398 setf sql
1399 return
1400 endif
1401
1402 # SQL type files may define some types as follows
1403 if line =~ '^TYPE\s.*$'
1404 setf sql
1405 return
1406 endif
1407 endfor
1408
1409 # Otherwise, affect the typst filetype
1410 setf typst
1411enddef
1412
PowerUser64aa61b8a2024-06-19 20:32:11 +02001413# Detect Microsoft Developer Studio Project files (Makefile) or Faust DSP
1414# files.
1415export def FTdsp()
1416 if exists("g:filetype_dsp")
1417 exe "setf " .. g:filetype_dsp
1418 return
1419 endif
1420
1421 # Test the filename
1422 if expand('%:t') =~ '^[mM]akefile.*$'
1423 setf make
1424 return
1425 endif
1426
1427 # Test the file contents
1428 for line in getline(1, 200)
1429 # Chech for comment style
1430 if line =~ '^#.*'
1431 setf make
1432 return
1433 endif
1434
1435 # Check for common lines
1436 if line =~ '^.*Microsoft Developer Studio Project File.*$'
1437 setf make
1438 return
1439 endif
1440
1441 if line =~ '^!MESSAGE This is not a valid makefile\..+$'
1442 setf make
1443 return
1444 endif
1445
1446 # Check for keywords
1447 if line =~ '^!(IF,ELSEIF,ENDIF).*$'
1448 setf make
1449 return
1450 endif
1451
1452 # Check for common assignments
1453 if line =~ '^SOURCE=.*$'
1454 setf make
1455 return
1456 endif
1457 endfor
1458
1459 # Otherwise, assume we have a Faust file
1460 setf faust
1461enddef
1462
Christian Brabandt99181202025-01-25 14:54:28 +01001463# Set the filetype of a *.v file to Verilog, V or Cog based on the first 500
Turiiya80406c22023-04-22 21:38:47 +01001464# lines.
1465export def FTv()
1466 if did_filetype()
1467 # ":setf" will do nothing, bail out early
1468 return
1469 endif
Christian Brabandt10b4f752024-01-01 19:19:20 +01001470 if exists("g:filetype_v")
1471 exe "setf " .. g:filetype_v
1472 return
1473 endif
Turiiya80406c22023-04-22 21:38:47 +01001474
Christian Brabandt10b4f752024-01-01 19:19:20 +01001475 var in_comment = 0
Christian Brabandt99181202025-01-25 14:54:28 +01001476 for lnum in range(1, min([line("$"), 500]))
Christian Brabandt10b4f752024-01-01 19:19:20 +01001477 var line = getline(lnum)
1478 # Skip Verilog and V comments (lines and blocks).
1479 if line =~ '^\s*/\*'
1480 # start comment block
1481 in_comment = 1
1482 endif
1483 if in_comment == 1
1484 if line =~ '\*/'
1485 # end comment block
1486 in_comment = 0
1487 endif
1488 # skip comment-block line
1489 continue
1490 endif
1491 if line =~ '^\s*//'
Turiiya80406c22023-04-22 21:38:47 +01001492 # skip comment line
1493 continue
1494 endif
1495
Christian Brabandt10b4f752024-01-01 19:19:20 +01001496 # Coq: line ends with a '.' followed by an optional variable number of
1497 # spaces or contains the start of a comment, but not inside a Verilog or V
1498 # comment.
1499 # Example: "Definition x := 10. (*".
1500 if (line =~ '\.\s*$' && line !~ '/[/*]') || (line =~ '(\*' && line !~ '/[/*].*(\*')
1501 setf coq
1502 return
1503 endif
1504
Turiiya80406c22023-04-22 21:38:47 +01001505 # Verilog: line ends with ';' followed by an optional variable number of
1506 # spaces and an optional start of a comment.
1507 # Example: " b <= a + 1; // Add 1".
Christian Brabandtfb49e3c2025-01-25 16:18:51 +01001508 # Alternatively: a module is defined: " module MyModule ( input )"
1509 if line =~ ';\s*\(/[/*].*\)\?$' || line =~ '\C^\s*module\s\+\w\+\s*('
Turiiya80406c22023-04-22 21:38:47 +01001510 setf verilog
1511 return
1512 endif
Turiiya80406c22023-04-22 21:38:47 +01001513 endfor
1514
1515 # No line matched, fall back to "v".
1516 setf v
1517enddef
1518
Doug Kearnsf97f6bb2023-08-27 18:44:09 +02001519export def FTvba()
1520 if getline(1) =~ '^["#] Vimball Archiver'
1521 setf vim
1522 else
1523 setf vb
1524 endif
1525enddef
1526
Colin Caine4b3fab12024-04-18 23:53:02 +02001527export def Detect_UCI_statements(): bool
1528 # Match a config or package statement at the start of the line.
1529 const config_or_package_statement = '^\s*\(\(c\|config\)\|\(p\|package\)\)\s\+\S'
1530 # Match a line that is either all blank or blank followed by a comment
1531 const comment_or_blank = '^\s*\(#.*\)\?$'
1532
1533 # Return true iff the file has a config or package statement near the
1534 # top of the file and all preceding lines were comments or blank.
1535 return getline(1) =~# config_or_package_statement
1536 \ || getline(1) =~# comment_or_blank
1537 \ && ( getline(2) =~# config_or_package_statement
1538 \ || getline(2) =~# comment_or_blank
1539 \ && getline(3) =~# config_or_package_statement
1540 \ )
1541enddef
1542
Bram Moolenaara2baa732022-02-04 16:09:54 +00001543# Uncomment this line to check for compilation errors early
dkearnsef387c02024-02-20 06:58:30 +11001544# defcompile